NOM, Prénom : Brillant Dany DS
Lycée Buffon 1 / 6
NOTE : 20/20 Youhou !!
DS d’informatique n°2 (Corrigé)
EXERCICE 1 : FONCTION MYSTERE
Soit le programme suivant :
def tri_mystere(L): # L une liste d’entiers et de flottants
n = len(L)
for i in range(1, n):
j = i
x = L[i]
while 0 < j and x < L[j-1]:
L[j] = L[j-1]
j = j-1
L[j] = x
Question 1 : (2 pt) Lors de l'appel tri_mystere(L), lorsque L est la liste [5, 2, 3, 1,
4], donner le contenu de la liste L à la fin de chaque itération de la boucle for.
A la fin de l’itération
Contenu de L
0 (avant les
itérations)
[5, 2, 3, 1, 4]
1
[2, 5, 3, 1, 4]
2
[2, 3, 5, 1, 4]
3
[1, 2, 3, 5, 4]
4
[1, 2, 3, 4, 5]
Question 2 : (1 pt) Quel type de tri est utilidans la fonction tri_mystere(L) ? Justifier
votre réponse.
C’est du tri par insertion, car à chaque itération de la boucle for, l’élément d’indice i , est inséré
au bon endroit dans la sous-liste L[0:i] .
Question 3 : (3 pt) Montrer, par l’invariant de boucle, que la fonction permet bien de trier la
liste.
Invariant de boucle : Inv(i) = « à la fin d’une l’itération de la boucle for, la sous-liste
L[0:i+1] est trié ».
Démo :
Initialisation : Avant de rentrer dans la boucle, la sous-liste L[0:1]= L[0] est
forcément triée car composée d’un seul élément.
Hérédité : supposons Inv(i) vrai. A l’itération suivante, la boucle while ne s’arrête que
si
DS 2 (1h15)
Lycée Buffon 2 / 6
! x >= L[j-1]: et x s’insère entre deux éléments inférieurs et
supérieurs, donc bien triée
! soit jatteint 0 et x est placé en tout début de liste car inférieur à tous
les éléments qui le précèdent.
Donc la sous-liste L[0:i+2]est triée, donc Inv(i+1) vrai.
Terminaison : Un boucle for s’arrête forcement, et la boucle while s’arrête également
car j diminue à chaque passage, et donc va forcément atteindre 0, dans le pire des
cas.
Bonne terminaison : Les itérations s’arrêtent pour i=n-1, donc Inv(n-1) vrai, soit la
sous-liste L[0:n] , c’est à dire la liste L est triée, CQFD.
Question 4 : (3 pt) Soit n, le nombre d’éléments dans la liste L. Donner, en le montrant, et
en précisant à quelle forme de L cela correspond, la complexité algorithmique de la fonction
tri_mystere(L) dans le meilleur et le pire des cas :
Meilleur des cas :
- L est déjà triée
- Complexi (à démontrer):
Prennons comme critère de calcul de la complexité, le nombre de comparaisons.
Chaque test de la boucle while est faux, donc il n’y a globalement qu’une boucle for, il y a
donc n-1 itérations, donc 2(n-1) comparaisons, la complexité algorithmique est donc en O(n).
Pire des cas :
- L est triée par ordre décroissant
- Complexi (à démontrer):
Chaque test de la boucle while est vrai, jusqu’à ce que j atteigne 0.
Pour i= 1 : 1 passage dans les instructions de la boucle while (2 comparaisons)
Pour i= 2 : 2 passages dans les instructions de la boucle while (4 comparaisons)
Pour i = n-1 : n-1 passage dans les instructions de la boucle while (2(n-1) comparaisons)
Donc le nombre de passage total, noté Nit , dans les instructions de la boucle while est de :
𝑁!" =1+2++𝑛1=𝑖
!!!
!!!
=(𝑛1)𝑛
2
Il y a donc (𝑛1)𝑛 comparaisons, donc une complexité algorithmique en O(n2).
Question 5 (question de cours) (3 pt): Pour les trois algorithmes de tri suivants, donner la
complexité temporelle pour les deux types de listes proposées.
DS 2 (1h15)
Lycée Buffon 3 / 6
Tri par Insertion
Tri Rapide
Tri fusion
Liste quasiment triée
O(n)
O(n2)
O(n.log(n))
Liste quelconque
O(n2).
O(n.log(n)) ou O(n2)
O(n.log(n))
EXERCICE 2 : OPTIMISATION DU TRI PAR INSERTION
On peut optimiser l’algorithme du tri par insertion, en recherchant par dichotomie la position
d’insertion.
Question 6 : (4 pt) Écrire une fonction ajouter(L,x) qui étant donnés une liste triée L et un
élément x, modifie L en insérant x dans la liste L à la bonne place (de sorte d’obtenir une liste
triée dans l’ordre croissant). On recherchera la position d’insertion par dichotomie.
Exemple : l’instruction ajouter([1,3,4],2) doit renvoyer la liste [1,2,3,4]
Remarque : on pourra utiliser la méthode L.insert(i,x) qui permet d’insérer l’élément x
dans la liste L dans la position i.
def ajouter(L,x):
a = 0
b = len(L)
if x>L[-1]:
L.append(x)
else:
while a<b:
m=(a+b)//2
if L[m]<=x:
a=m+1
else:
b=m
L.insert(a,x)
return L
Question 7 : (1 pt) Estimer, en justifiant brièvement, la complexité algorithmique de la fonction
ajouter() si on suppose que la fonction insert() a une complexité algorithmique en O(1).
C’est une algorithme de dichotomie, donc l’intervalle de recherche est divisé par 2 à chaque
itération, ce qui est typique d’une complexité algorithmique en O(log2(n)) ou O(log(n)).
A savoir redémontrer !
On donne la fonction Tri_Insertion(L) qui permet de trier une liste L par l’algorithme du
tri par insertion et qui utilise la fonction ajouter(L,x) définie précédemment.
def Tri_Insertion(L):
for i in range(1,len(L)):
SL = L[0:i] # sous-liste triee
x=L.pop(i)
ajouter(SL,x)
L[0:i] = SL
return L
DS 2 (1h15)
Lycée Buffon 4 / 6
Question 8 : (2 pt) Expliquer simplement pourquoi, si n est la longueur de la liste, alors la
complexité de cet algorithme est en O(n log(n)). On précise que :
" les méthodes "classiques" des listes comme pop, append... fonctionnent en temps
constant.
" on n'attend pas une démonstration mathématique rigoureuse, mais il faut tout de
même justifier votre réponse.
En déduire l'avantage et l'inconvénient de cette version du tri par insertion par rapport à
l’algorithme « classique » vu en cours.
La boucle for effectue n itérations.
A chacune d’elle, on appelle la fonction ajouter(L,x) sur une sous-liste de longueur i ; les autres
opérations s'effectuent en temps constant. Le coût du tour est donc en O(log(i)) (cf. question 6).
Le coût total de la fonction est donc en O(log(1) + log(2) + ... + log(n)) soit en O(n log(n)). Sans entrer
dans les détails mathématiques, cela peut se justifier simplement :
soit%par%une%comparaison%série/intégrale,%une%primitive%de%ln%étant%la%fonction%x%7>%xln(x)%7%x,%
soit%en%remarquant%que%log(1)%+%...%+%log(n)%=%log(n!),%si%l'on%sait%que%n!%se%comporte%comme%
nn (d'après la formule de Stirling par exemple).
Avantage : on évite le "pire cas" en O(n2) de l’algorithme classique (liste triée à l'envers).
Inconvénient : on évite le "meilleur cas" en O(n) de l'algorithme classique (liste triée).
Question 9 : (3 pt)Donner une version récursive, notée Tri_Insertion_REC(L) de la
fonction Tri_Insertion(L).
def Tri_Insertion_REC(L): # recursive
if len(L)==1:
return L
else:
x=L.pop()
Tri_Insertion_REC(L)
ajouter(L,x)
return L
DS 2 (1h15)
Lycée Buffon 5 / 6
EXERCICE 3 : TRI PAR SELECTION
L'algorithme du tri par sélection est le suivant :
" on recherche le plus petit élément de la liste à trier,
" on l'échange avec le premier élément,
" puis on répète le même procédé sur la "sous-liste" restante.
Question 10 : (3 pt)Ecrire une fonction Tri_Selection(L) qui utilise l’algorithme défini.
def tri_selection(L):
n = len(L)
# boucle sur l'elt a placer (sauf le dernier)
for i in range(n):
# recherche du minimum de la sous-liste
Lmin,jmin = L[i],i
for j in range(i+1,n):
if L[j] < Lmin:
Lmin,jmin = L[j],j
# echange
L[jmin],L[i] = L[i],L[jmin]
return L
Question 11 : (1,5pt) Estimer la complexité de cette fonction et comparer son efficacité par
rapport aux algorithmes de tri évoqués de la question 5.
Soit n la taille de la liste.
La fonction possède deux boucles imbriquées de longueur n, dont le contenu s'effectue en
temps constant. On a donc une complexité en O(n2).
Cet algorithme est moins efficace que les algorithmes évoqués dans l’exercice 2 ; au mieux, il est
aussi efficace. Si la liste est quasiment triée, même le tri par insertion est plus efficace !
On pourrait donc aussi l’appeler le tri_naze, le tri_mou, ou le tri_qu_est_pas_efficace.
1 / 6 100%
La catégorie de ce document est-elle correcte?
Merci pour votre participation!

Faire une suggestion

Avez-vous trouvé des erreurs dans linterface ou les textes ? Ou savez-vous comment améliorer linterface utilisateur de StudyLib ? Nhésitez pas à envoyer vos suggestions. Cest très important pour nous !