100 lines
6.3 KiB
Markdown
100 lines
6.3 KiB
Markdown
# Exercices : Diviser pour régner
|
|||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
Les trois premiers exercices se traitent sur papier avant de toucher au clavier. Un algorithme que vous ne savez pas dérouler à la main est un algorithme que vous ne saurez pas déboguer à l'écran. Les derniers sont volontairement plus exigeants.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Exercice 1 : Dérouler le tri fusion
|
||
|
|
|
||
|
|
On trie la liste suivante avec le tri fusion vu en cours :
|
||
|
|
|
||
|
|
```python
|
||
|
|
[5, 3, 8, 1, 9, 2, 7]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Questions :**
|
||
|
|
|
||
|
|
1. Dessiner l'arbre des divisions, jusqu'à n'obtenir que des listes d'un seul élément.
|
||
|
|
2. Écrire, étage par étage, les fusions successives qui reconstituent la liste triée.
|
||
|
|
3. Combien d'étages de division compte-t-on pour une liste de 8 éléments ? De 16 éléments ? De 1024 éléments ?
|
||
|
|
4. En déduire la relation entre le nombre d'étages et la taille de la liste. Quel est le rapport avec la complexité annoncée en cours ?
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Exercice 2 : La fusion vue de près
|
||
|
|
|
||
|
|
On reprend la fonction `fusionner` du cours, celle qui réunit deux listes déjà triées.
|
||
|
|
|
||
|
|
**Questions :**
|
||
|
|
|
||
|
|
1. Dérouler l'appel `fusionner([2, 5, 9], [1, 3, 12])` dans un tableau, en notant à chaque tour de boucle les valeurs de `i`, de `j` et de `resultat`.
|
||
|
|
2. Combien de comparaisons entre éléments cette fusion a-t-elle demandées ?
|
||
|
|
3. Donner deux listes de trois éléments chacune dont la fusion ne demande que trois comparaisons. Expliquer ce qui se produit.
|
||
|
|
4. Quel est le nombre maximal de comparaisons pour fusionner une liste de *p* éléments et une liste de *q* éléments ? Justifier.
|
||
|
|
5. La fonction se termine par `return resultat + gauche[i:] + droite[j:]`. Pourquoi peut-on ajouter d'un seul coup tout ce qui reste, sans le comparer à quoi que ce soit ?
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Exercice 3 : Recherche dichotomique
|
||
|
|
|
||
|
|
Chercher une valeur dans une liste **triée** est le deuxième exemple canonique du diviser pour régner : on compare avec l'élément du milieu, et on élimine la moitié de la liste à chaque étape.
|
||
|
|
|
||
|
|
**Questions :**
|
||
|
|
|
||
|
|
1. Écrire une fonction récursive `recherche(tab, x)` qui renvoie `True` si `x` figure dans la liste triée `tab`, et `False` sinon.
|
||
|
|
2. Dérouler `recherche([1, 4, 6, 9, 12, 15, 20], 12)`. Quelles sous-listes sont successivement examinées, et combien d'appels sont nécessaires ?
|
||
|
|
3. On veut maintenant connaître l'**indice** de `x`, et plus seulement sa présence. Expliquer pourquoi la fonction de la question 1, qui découpe la liste en tranches, ne permet pas d'y répondre directement.
|
||
|
|
4. Écrire une fonction `indice(tab, x, debut, fin)` qui renvoie l'indice de `x` dans `tab`, ou `-1` s'il est absent, **sans jamais découper la liste**.
|
||
|
|
5. Sur une liste d'un million d'éléments, combien d'appels au maximum ? Combien pour une liste deux fois plus longue ?
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Exercice 4 : Le maximum, avec et sans diviser pour régner
|
||
|
|
|
||
|
|
**Questions :**
|
||
|
|
|
||
|
|
1. Écrire une fonction récursive `maximum(tab)` qui applique le schéma du cours : si la liste ne contient qu'un élément, le renvoyer ; sinon la couper en deux, chercher le maximum de chaque moitié, et renvoyer le plus grand des deux résultats.
|
||
|
|
2. Combien de comparaisons cette fonction effectue-t-elle sur une liste de 8 éléments ? Et la boucle `for` que vous écririez spontanément ?
|
||
|
|
3. Le découpage apporte-t-il un gain ici ? Justifier.
|
||
|
|
4. Un tel découpage ne fait donc pas toujours gagner. À quelle condition sur l'étape « combiner » la méthode devient-elle intéressante ? Comparer avec le tri fusion.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Exercice 5 : Compter les occurrences dans une liste triée
|
||
|
|
|
||
|
|
Un serveur de jeu conserve les scores de ses 500 000 joueurs dans une liste **triée par ordre croissant**. Plusieurs joueurs peuvent avoir exactement le même score, et l'on veut savoir combien.
|
||
|
|
|
||
|
|
**Questions :**
|
||
|
|
|
||
|
|
1. Écrire une fonction `compter_naif(tab, x)` qui parcourt la liste et compte les occurrences de `x`. Quelle est sa complexité ?
|
||
|
|
2. Écrire une fonction `premiere_occurrence(tab, x)` qui renvoie, par dichotomie, l'indice de la **première** apparition de `x`, ou `-1` s'il est absent. On prendra garde à ne pas s'arrêter à la première correspondance trouvée.
|
||
|
|
3. Écrire de même `derniere_occurrence(tab, x)`.
|
||
|
|
4. En déduire une fonction `compter(tab, x)` et donner sa complexité.
|
||
|
|
5. Sur les 500 000 scores, combien d'opérations séparent les deux méthodes, en ordre de grandeur ?
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Exercice 6 : Compter les inversions
|
||
|
|
|
||
|
|
Une **inversion** dans une liste `tab` est un couple d'indices (*i*, *j*) tel que *i* < *j* et `tab[i] > tab[j]`, autrement dit deux éléments rangés dans le mauvais ordre l'un par rapport à l'autre. Le nombre d'inversions mesure le désordre d'une liste : il vaut 0 pour une liste triée, et il est maximal pour une liste rangée à l'envers. Les sites de recommandation s'en servent pour estimer à quel point deux classements se ressemblent.
|
||
|
|
|
||
|
|
**Questions :**
|
||
|
|
|
||
|
|
1. Donner les inversions de la liste `[2, 4, 1, 3, 5]`. Combien y en a-t-il ?
|
||
|
|
2. Quelle liste de *n* éléments en compte le plus ? Combien exactement ?
|
||
|
|
3. Écrire une fonction `inversions_naif(tab)` qui les compte avec deux boucles imbriquées. Quelle est sa complexité ?
|
||
|
|
4. On veut faire mieux en s'appuyant sur le tri fusion. Pendant la fusion de `gauche` et de `droite`, toutes deux déjà triées, on prend parfois un élément de `droite` alors qu'il reste encore des éléments dans `gauche`. Combien d'inversions ce seul choix révèle-t-il d'un coup ? Justifier soigneusement.
|
||
|
|
5. Écrire `fusionner_et_compter(gauche, droite)` qui renvoie le couple formé de la liste fusionnée et du nombre d'inversions découvertes pendant cette fusion.
|
||
|
|
6. En déduire `tri_fusion_inversions(tab)` qui renvoie le couple formé de la liste triée et du nombre total d'inversions. Attention à ne pas oublier les inversions comptées dans chaque moitié.
|
||
|
|
7. Quelle est la complexité obtenue ? Sur une liste de 100 000 éléments, quel facteur sépare cette version de celle de la question 3 ?
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
Auteur : Florian Mathieu
|
||
|
|
|
||
|
|
Licence CC BY-SA
|
||
|
|
|
||
|
|
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International</a>.
|