Fiche récapitulative générée pour impression / export PDF.

Licence 1 · Informatique L1 — Introduction à Python pour les mathématiques

Algorithmes simples

Algorithmes simples

### 1. Recherche dans une liste (recherche séquentielle)

Pour savoir si une valeur v se trouve dans une liste L, on parcourt la liste élément par élément :

> def recherche(L, v):
> for x in L:
> if x == v:
> return True
> return False

Cette méthode, appelée recherche séquentielle, a un coût proportionnel à la longueur de la liste dans le pire cas (la valeur n'est pas présente, ou se trouve en dernière position) : on dit qu'elle est de complexité linéaire, O(n)O(n)n=len(L)n=\text{len}(L).

Variante : trouver l'indice d'une valeur.

> def indice(L, v):
> for i in range(len(L)):
> if L[i] == v:
> return i
> return -1

On renvoie souvent 1-1 par convention pour signaler que la valeur n'a pas été trouvée.

### 2. Tri par sélection

Le tri par sélection construit la liste triée en répétant le principe suivant : trouver le plus petit élément restant et le placer en tête.

> def tri_selection(L):
> n = len(L)
> for i in range(n):
> indice_min = i
> for j in range(i+1, n):
> if L[j] < L[indice_min]:
> indice_min = j
> L[i], L[indice_min] = L[indice_min], L[i]
> return L

À chaque étape ii, on cherche le minimum parmi les éléments d'indices i,i+1,,n1i, i+1, \ldots, n-1 (la partie non encore triée), puis on l'échange avec l'élément d'indice ii.

Exemple résolu : trier [5,2,8,1,9][5, 2, 8, 1, 9].

- i=0i=0 : minimum de [5,2,8,1,9][5,2,8,1,9] est 11 (indice 33) → échange : [1,2,8,5,9][1,2,8,5,9]
- i=1i=1 : minimum de [2,8,5,9][2,8,5,9] (indices 11 à 44) est 22, déjà en place : [1,2,8,5,9][1,2,8,5,9]
- i=2i=2 : minimum de [8,5,9][8,5,9] est 55 (indice 33) → échange : [1,2,5,8,9][1,2,5,8,9]
- i=3i=3 : minimum de [8,9][8,9] est 88, déjà en place : [1,2,5,8,9][1,2,5,8,9] (trié)

### 3. Tri par insertion (alternative)

Le tri par insertion construit la partie triée en insérant chaque nouvel élément à sa bonne place parmi les éléments déjà triés :

> def tri_insertion(L):
> for i in range(1, len(L)):
> cle = L[i]
> j = i - 1
> while j >= 0 and L[j] > cle:
> L[j+1] = L[j]
> j = j - 1
> L[j+1] = cle
> return L

### 4. Calcul de moyenne et de somme

> def somme(L):
> s = 0
> for x in L:
> s = s + x
> return s
>
> def moyenne(L):
> return somme(L) / len(L)

### 5. Calcul itératif du PGCD (algorithme d'Euclide)

Le PGCD (plus grand commun diviseur) de deux entiers aa et bb se calcule par l'algorithme d'Euclide, fondé sur la propriété pgcd(a,b)=pgcd(b,amodb)\text{pgcd}(a,b)=\text{pgcd}(b, a \bmod b), avec pgcd(a,0)=a\text{pgcd}(a,0)=a :

> def pgcd(a, b):
> while b != 0:
> a, b = b, a % b
> return a

Exemple résolu : pgcd(48,18)\text{pgcd}(48, 18).

- (a,b)=(48,18)(a,b)=(48,18) : 48mod18=1248 \bmod 18 = 12 → nouveau couple (18,12)(18, 12)
- (18,12)(18,12) : 18mod12=618 \bmod 12 = 6(12,6)(12, 6)
- (12,6)(12,6) : 12mod6=012 \bmod 6 = 0(6,0)(6, 0)
- b=0b=0 : on renvoie a=6a=6.

Donc pgcd(48,18)=6\text{pgcd}(48,18)=6.

### 6. Résumé des complexités

| Algorithme | Complexité (pire cas) |
|---|---|
| Recherche séquentielle | O(n)O(n) |
| Tri par sélection | O(n2)O(n^2) |
| Tri par insertion | O(n2)O(n^2) |
| Algorithme d'Euclide | O(log(min(a,b)))O(\log(\min(a,b))) |

Exercices de la leçon

Exercice 1

Que renvoie recherche([3, 7, 1], 7) pour la fonction de recherche séquentielle du cours ?

Corrigé

La valeur 77 est présente dans la liste (à l'indice 11), donc la fonction recherche (qui renvoie un booléen, pas un indice) renvoie True.

Exercice 2

Quel est le pgcd de 4848 et 1818 ?

Corrigé

Par l'algorithme d'Euclide détaillé dans le cours : pgcd(48,18)=pgcd(18,12)=pgcd(12,6)=pgcd(6,0)=6\text{pgcd}(48,18)=\text{pgcd}(18,12)=\text{pgcd}(12,6)=\text{pgcd}(6,0)=6.

Exercice 3

Vrai ou faux : le tri par sélection trouve, à chaque étape, le plus petit élément restant et le place en tête de la partie non triée.

Corrigé

Vrai, c'est exactement le principe du tri par sélection : à l'étape ii, on sélectionne le minimum de la sous-liste L[i:]L[i:] et on l'échange avec L[i]L[i].

Exercice 4

Quelle valeur renvoie la fonction indice(L, v) du cours si v n'est pas présent dans L ?

Corrigé

Par convention, la fonction renvoie 1-1 lorsque la valeur recherchée n'a été trouvée à aucun indice de la liste, après avoir parcouru tous les éléments.

Exercice 5

Quelle est la complexité (en pire cas) de la recherche séquentielle dans une liste de longueur nn ?

Corrigé

Dans le pire cas (valeur absente, ou en dernière position), la recherche séquentielle examine les nn éléments de la liste : complexité linéaire O(n)O(n).

Exercice 6

Après la première itération (i=0i=0) du tri par sélection sur [5,2,8,1,9][5,2,8,1,9], quel est l'état de la liste ?

Corrigé

Le minimum de toute la liste est 11, à l'indice 33. On l'échange avec l'élément d'indice 00 (qui est 55) : [1,2,8,5,9][1,2,8,5,9].

Exercice 7

Vrai ou faux : a, b = b, a % b en Python échange et met à jour simultanément les deux variables, en utilisant l'ancienne valeur de a pour calculer le modulo.

Corrigé

Vrai. En Python, l'affectation multiple a, b = expr1, expr2 évalue d'abord toutes les expressions à droite avec les valeurs courantes, puis effectue les affectations. Donc a % b utilise bien l'ancienne valeur de a (et de b) avant toute modification.

Exercice 8

Quelle est la complexité (pire cas) du tri par sélection sur une liste de nn éléments ?

Corrigé

Le tri par sélection effectue, pour chacune des nn étapes, une recherche du minimum parmi en moyenne n/2n/2 éléments restants : le nombre total de comparaisons est de l'ordre de n2/2n^2/2, soit O(n2)O(n^2).

Exercice 9

Calculer la moyenne de la liste [4,8,6,2][4, 8, 6, 2] à l'aide des fonctions somme et moyenne du cours.

Corrigé

somme=4+8+6+2=20\text{somme}=4+8+6+2=20. moyenne=20/4=5,0\text{moyenne}=20/4=5{,}0.

Exercice 10

Modifier la fonction recherche pour qu'elle compte le nombre de comparaisons effectuées avant de trouver la valeur (ou avant de conclure son absence). Donner le code modifié.

Corrigé

Une solution possible :

def recherche_comptee(L, v):

compteur = 0

for x in L:

compteur = compteur + 1

if x == v:

return True, compteur

return False, compteur

On incrémente un compteur à chaque comparaison effectuée (à chaque tour de boucle), et on le renvoie en plus du résultat booléen. Python permet de renvoyer plusieurs valeurs sous forme de tuple avec return a, b.

Exercice 11

Dérouler le tri par insertion sur la liste [3,1,4,2][3, 1, 4, 2] étape par étape (i=1,2,3i=1,2,3) et donner le résultat final.

Corrigé

i=1i=1 : clé =L[1]=1=L[1]=1. On compare à L[0]=3>1L[0]=3 > 1, donc on décale 33 vers la droite et on insère 11 en tête : [1,3,4,2][1,3,4,2].

i=2i=2 : clé =L[2]=4=L[2]=4. On compare à L[1]=3L[1]=3 : 3<43 < 4, pas de décalage : [1,3,4,2][1,3,4,2] inchangée.

i=3i=3 : clé =L[3]=2=L[3]=2. On compare à L[2]=4>2L[2]=4>2 : décalage, puis à L[1]=3>2L[1]=3>2 : décalage, puis à L[0]=12L[0]=1 \leq 2 : on insère 22 juste après : [1,2,3,4][1,2,3,4].

Le résultat final est la liste triée [1,2,3,4][1,2,3,4].

Exercice 12

Démontrer que l'algorithme d'Euclide se termine toujours en un nombre fini d'étapes, pour tous entiers ab>0a \geq b > 0 donnés au départ.

Corrigé

À chaque itération de la boucle while b != 0, le couple (a,b)(a,b) devient (b,amodb)(b,\, a\bmod b). Or, par définition du reste de la division euclidienne, 0amodb<b0 \leq a\bmod b < b (strictement).

Donc la nouvelle valeur de bb (qui est amodba \bmod b) est strictement inférieure à l'ancienne valeur de bb. La suite des valeurs successives de bb est donc une suite strictement décroissante d'entiers naturels.

Une telle suite ne peut pas décroître indéfiniment (elle est minorée par 00) : elle atteint nécessairement la valeur 00 après un nombre fini d'étapes, ce qui termine la boucle while. \square

Exercice 13

Pourquoi le tri par sélection effectue-t-il toujours environ n2/2n^2/2 comparaisons, indépendamment de l'ordre initial de la liste (contrairement au tri par insertion) ?

Corrigé

La boucle interne du tri par sélection (recherche du minimum dans la partie restante) s'exécute toujours sur tous les éléments restants, sans condition d'arrêt anticipée, quel que soit leur ordre. C'est pourquoi sa complexité est toujours Θ(n2)\Theta(n^2), alors que le tri par insertion peut s'arrêter plus tôt (boucle while) si la liste est déjà presque triée.

Exercice 14

Écrire une fonction est_trie(L) qui renvoie True si la liste L est triée par ordre croissant, False sinon.

Corrigé

Une solution possible :

def est_trie(L):

for i in range(len(L) - 1):

if L[i] > L[i+1]:

return False

return True

On parcourt les paires d'éléments consécutifs ; dès qu'on en trouve une dans le mauvais ordre (L[i]>L[i+1]L[i]>L[i+1]), on renvoie False immédiatement. Si aucune paire n'est dans le mauvais ordre après le parcours complet, la liste est triée : on renvoie True. On utilise range(len(L)-1) pour éviter une erreur d'indice hors limites en comparant le dernier élément à un suivant inexistant.

Exercice 15

On veut calculer le PGCD de trois entiers a,b,ca, b, c. Proposer une fonction pgcd3(a, b, c) réutilisant la fonction pgcd du cours, en justifiant l'identité mathématique utilisée.

Corrigé

Une solution possible :

def pgcd3(a, b, c):

return pgcd(pgcd(a, b), c)

Justification : un entier dd divise simultanément a,b,ca, b, c si et seulement si dd divise pgcd(a,b)\text{pgcd}(a,b) et dd divise cc (car dad \mid a et db    dpgcd(a,b)d\mid b \iff d \mid \text{pgcd}(a,b)). Donc l'ensemble des diviseurs communs à a,b,ca,b,c est exactement l'ensemble des diviseurs communs à pgcd(a,b)\text{pgcd}(a,b) et cc, ce qui donne pgcd(a,b,c)=pgcd(pgcd(a,b),c)\text{pgcd}(a,b,c) = \text{pgcd}(\text{pgcd}(a,b),\,c). Cette associativité permet de réduire le calcul du pgcd de nn nombres à des appels successifs de la fonction à deux arguments.

AlphaMath Académie · Algorithmes simples · Informatique L1 — Introduction à Python pour les mathématiques