Files
TermNSI/Recursivité/Diviser_pour_regner/EXERCICES.md
T

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>.