1 Généralité

publicité
Université de Provence
Licence Math-Info
Première Année
V. Phan Luong
Algorithmique et
Programmation en Python
Cours 5 : Notions d’Algorithme et de Complexité
1
Généralité
Un algorithme est une méthode effective pour résoudre un problème. Par les termes
“méthode effective” on s’entend que la méthode peut réellement résoudre le problème
en un nombre fini d’opérations dont l’exécution peut se faire en un temps fini. La
spécification d’un algorithme se faire d’abord par la spécification formelle du problème
qu’il résoudre, suivie d’une méthode qu’il adopte pour résoudre le problème.
Il faut bien analyser le problème pour le comprendre et déterminer les données essentielles
du problème et ce que l’on cherche comme solution. Les données essentielles du problème
constituent ce que l’on appelle l’entrée de l’algorithme. Ce que cherche le problème
constitue la sortie de l’algorithme.
Par exemple, le problème de calculer le pgcd de deux entiers naturels a pour l’entrée les
deux entiers et la sortie le plus grand commun diviseur de ces deux entiers.
La méthode de résolution est spécifiée en un nombre fini d’étapes dont chacune exécute
une opération bien connue et finie pour assurer que chaque étape termine et que l’algorithme termine en un temps fini. Une bonne connaissance du domaine d’application est
nécessaire pour pouvoir spécifier une méthode de résolution d’un problème à résoudre.
Un algorithme est efficace si sa méthode de résolution optimise l’espace de mémoire
et le temps de calcul pour résoudre le problème. L’efficacité est estimée par la notion de complexité. On a donc la complexité en espace de mémoire et la complexité
en temps d’exécution. Ces complexités sont estimées en fonction de la grandeur des
données d’entrée du problème. Dans l’ordre de plus efficace au moins efficace, on peut
citer les algorithmes dont l’estimation est en fonction logarithme, linéaire, quadratique,
exponentielle de la grandeur des données d’entrée.
Pour estimer de manière indépendante de tous ordinateurs, l’efficacité en temps d’exécution
est calculée en nombre d’opérations de base de l’agorithme. Par exemple, pour un
1
algorithme de calcul du pgcd, la complexité en temps peut être estimée en nombre
d’opérations de division.
Dans la suite on étudie certain nombre d’algorithmes concernant les listes.
2
Recherche dans une liste
2.1
Liste non triée
Problème de recherche d’un élément dans une liste.
Entrée : un élément x et une liste L.
Sortie : un index de la liste si x existe dans L, -1 sinon.
Méthode :
Informelle : Parcourir la liste en comparant chaque élément de L avec x. On s’arrête lors
de la première rencontre de x et retourne l’index de cette rencontre. Si la liste est toute
parcourue et on ne voit pas x, alors retourne -1.
Formelle :
Pour chaque case de la liste, commencant par la première case, faire
Comparer x avec l’élément de la case ;
Si x est identique à l’élément, alors retourner l’index courant et s’arrête.
Sinon, passer à la case suivante
Si on arrive à ce stade, alors retourner -1
Fonction Python :
def cherche(x, L) :
i, n = 0, len(L)
while i < n :
if x == L[i] : return i
i = i + 1
return -1
## appels de fonction
print cherche (5, [4,7,5,3,6,1])
2
print cherche (5, [4,7,4,3,6,1])
La complexité en nombre de comparaisons de données (l’opération x == L[i]) :
– Au pire, on doit comparer x avec tous les éléments de L. Donc, soit n le nombre
d’élémements de L, alors la complexité en nombre de comparaisons de données est n ;
elle est en fonction linéaire de la grandeur de la liste.
– Au meilleur, on voit x à la première case de la liste. La complexité est en une comparaison.
– La commplexité moyenne est n/2.
2.2
Liste triée
On peut toujours appliquer l’algorithme ci-dessus pour rechercher un élément dans une
liste triée. Cependant, pour cette configuration spéciale, on autre algorithme plus efficace
qui effectue la recherche de manière dichotomique.
Problème de recherche d’un élément dans une liste triée.
Entrée : un élément x et une liste triée L.
Sortie : un index de la liste si x existe dans L, -1 sinon.
Méthode :
Si la liste est vide, alors retourne -1.
Sinon,
Comparer x avec l’élément au milieu de la liste.
Si x est égal à cet élément, alors retourner l’index du milieu.
Sinon, si x est supérieur à cet élément, alors re-appliquer la recherche dans la partie
droite de la liste.
Sinon, re-appliquer la recherche dans la partie gauche de la liste.
3
Fonction Python :
# fonction iterative
def cherch_ord(x, L):
g, d = 0, len(L) - 1
while g <= d :
m = (d + g)/2
if x == L[m]:
return m
elif x > L[m]:
g = m +1
else :
d = m - 1
if g > d : return -1
#else: return -1
# fonction recursive
def cherch_ord_r(x, L, g, d):
if g <= d :
m = (d + g)/2
if x == L[m]:
return m
elif x > L[m]:
return cherch_ord_r(x, L, m+1, d)
else :
return cherch_ord_r(x, L, g, m-1)
if g > d : return -1
#else: return -1
# Tests
L = [4, 6, 7, 9, 12, 15, 35, 56]
4
x = input(’Entrer un nombre: ’)
print "Test de la fonction itérative: "
print cherch_ord(x, L)
print "Test de la fonction récursive: "
g, d = 0, len(L) - 1
print cherch_ord_r(x, L, g, d)
La complexité en nombre de comparaisons de données (l’opération x == L[i]) :
– Au meilleur, on voit x à la case au milieu de la liste. La complexité est en une comparaison.
– Au pire, on doit comparer x avec l’élément au milieu des parties divisées successivement
de L, jusqu’à ce que la dernière partie (de longueur 1). Pour atteindre cette longueur, le
nombre de divisions successives est log2 (n), car 2log2 (n) = n.
3
Tris dans une liste
3.1
Un tri simple
La méthode de tri (dans l’ordre croissant) la plus intuitive est de rechercher d’abord
l’élément minimal de la liste, le placer au début de la liste et recommencer le tri dans la
queue de la liste, ainsi de suite jusqu’à ce que la queue de la liste courant est vide.
Entrée : une liste L.
Sortie : la même liste mais triée dans l’ordre croissant.
Méthode : trier par permutation.
Parcourir L à partir du premier élément, pour chaque élément faire
Pour la partie de la liste commençant par l’élément courant,
chercher un élément minimal de cette partie ;
permuter l’élément minimal avec l’élément au début de la partie.
Retourner la liste.
5
Fonction Python :
# Chercher l’index du premier élément minimal de la partie
# de la liste commençant par index i
def index_min(L, i):
min = L[i]
m, n, k = i, i+1, len(L)
while n < k :
if min > L[n]:
min = L[n]
m = n
n = n+1
return m
# Le tri par permutation
def tri_permuta(L) :
i, n = 0, len(L)
while i < n-1 :
k = index_min(L, i)
tmp = L[i]
L[i] = L[k]
L[k] = tmp
i = i+1
# Tests
L = [6,12,7,5,9,4,8,3]
tri_permuta(L)
print L
Complexité en temps : La recherche d’un élément minimal d’une partie de la liste
coûte k-1 comparaisons, où k est le nombre d’élément de la partie.
6
Pour faire le trie, l’algorithme considére n-1 parties (sous-listes) de L de longueurs n-1,
n-2, ..., 1, respectivement.
Donc, le nombre total de comparaisons pour achever le tri est 1 + 2 + ... + n-2 + n-1
= n(n-1)/2. La complexité en temps de cet algorithme est en fonction quadratique de n
(la taille de la liste).
3.2
Le tri rapide
Un tri plus efficace est connu sous le nom le tri rapide (ou dichotomique). La méthode
de ce tri consiste en étapes suivantes :
1) Prendre un élément, appelon le pivot, de la liste courante et le placer à une position
dans la liste telle que tous les éléments à droite du pivot sont supérieurs au pivot et tous
les éléments à droite du pivot sont inférieurs ou égaux au pivot.
2) Re-appliquer les actions du point 1) pour chaque partie gauche et droite de la liste,
ainsi de suite jusqu’à ce que les parties se reduite en un seul élément.
Souvent le premier élément de la partie courante est choisi comme le pivot de la partie.
Fonction Python :
# choisir le premier element de l comme pivot et le placer au bon endroit
def placer(L, g, d) :
v, m, i = L[g], g, g+1
while i <= d :
if L[i] <= v :
m = m+1
tmp = L[m]
L[m] = L[i]
L[i] = tmp
i = i+1
tmp = L[g]
L[g] = L[m]
L[m] = tmp
return m
7
# Le tri rapide
def monqsort(L, i, j):
if i < j :
m = placer(L, i, j)
monqsort(L, i, m-1)
monqsort(L, m+1, j)
L = [3,5,8,3,7,4,5,9]
i, j = 0, len(L)-1
monqsort(L, i, j)
print L
Dans le pire des cas, la complexité du tri rapide en temps est une fonction quadratique
de la taille de la liste. C’est le cas de trier une liste déjà bien ordonnée. En moyenne, on
peut montrer que le nombre de comparaisons en nlog2 (n) où n est la taille de la liste.
8
Téléchargement