NSI Terminale • Programme français, lycées de Montréal

Exercices corrigés de NSI : diviser pour régner, programmation dynamique et graphes

Voici une série d'exercices corrigés de NSI pour la classe de Terminale, sur l'algorithmique avancée du programme français. Elle s'adresse aux élèves des lycées français de Montréal, le Lycée Marie de France et le Collège Stanislas.

Le fil de la série : accélérer un algorithme, ce n'est jamais faire les mêmes opérations plus vite, c'est en SUPPRIMER. Diviser pour régner en supprime en coupant le problème en deux ; la programmation dynamique en supprime en cessant de recalculer ce qu'on sait déjà ; un bon parcours de graphe en supprime en ne visitant chaque sommet qu'une fois.

Trois pièges sont désignés nommément dans le corrigé : appliquer une dichotomie à un tableau non trié, croire qu'une méthode gloutonne est optimale sans le démontrer, et comparer deux algorithmes par leur seul ordre de grandeur en oubliant les constantes.

Série autocorrigée Tape tes réponses sous chaque question : la page te dit juste ou faux avant d'ouvrir la correction. Avec un compte, chaque bonne réponse du premier coup rapporte des points.

Ce chapitre fait partie de NSI en Terminale
Avant de commencer Fiche de révision : les pièges et la méthode de ce chapitre

Avant ce chapitre

Ces notions sont supposées acquises ici. Si le premier exercice résiste, le blocage vient presque toujours de l'une d'elles, pas du chapitre lui-même.

Remonter plus loin : la chaîne complète (4 chapitres) ↓

Le chemin de remédiation, du plus ancien au plus proche. Un élève qui reprend ce chapitre de zéro le reprend dans cet ordre.

  1. 1Python : types, contrôle, fonctions et tableauxPremière
  2. 2Algorithmique : preuve, terminaison et coûtPremière
  3. 3Algorithmique : recherche, tris, récursivité et complexité
  4. 4Structures de données : piles, files, arbres et graphes

Rappel de cours

  • Recherche dichotomique : tableau TRIÉ obligatoire, coût log2n+1\lfloor \log_{2} n \rfloor + 1 comparaisons.
  • Tri fusion : C(n)=2C(n/2)+nC(n) = 2C(n/2) + n, coût nlog2nn \log_{2} n garanti, stable, mémoire supplémentaire linéaire.
  • Tri rapide : nlog2nn \log_{2} n en moyenne, n2n^{2} au pire, notamment sur un tableau déjà trié avec pivot fixe.
  • Méthode de l'arbre de récursion : aka^{k} sous-problèmes de taille n/bkn/b^{k} au niveau kk, logbn\log_{b} n niveaux, on somme le travail par niveau.
  • Une méthode gloutonne doit être DÉMONTRÉE. Contre-exemple : pièces 1, 3, 4 et somme 6.
  • Programmation dynamique : principe d'optimalité, une table remplie de bas en haut, chaque valeur calculée une seule fois.
  • Sac à dos : V(i,c)=max(V(i1,c), vi+V(i1,cpi))V(i, c) = \max(V(i-1, c),\ v_{i} + V(i-1, c - p_{i})) si l'objet rentre. Coût nCnC, pseudo-polynomial.
  • Parcours de graphe : même squelette, pile pour la profondeur, file pour la largeur. Marquer AVANT d'ajouter.
  • Coût d'un parcours : O(n+m)O(n + m) avec des listes d'adjacence, O(n2)O(n^{2}) avec une matrice.
  • Dijkstra : sélectionner le plus proche NON TRAITÉ, jamais le voisin le plus proche du dernier. Poids positifs obligatoires.

Partie A : Les bases (/50)

Exercice 1 : Diviser pour régner : la recherche dichotomique

Diviser pour régner tient en trois mots : couper, résoudre, recombiner. La recherche dichotomique en est le cas le plus simple, celui où la recombinaison est gratuite.

def dicho(t, v):
    g, d = 0, len(t) - 1
    while g <= d:
        m = (g + d) // 2
        if t[m] == v:
            return m
        if t[m] < v:
            g = m + 1
        else:
            d = m - 1
    return -1
  • a) Quelle précondition la fonction exige-t-elle ? Que renvoie-t-elle si la valeur est absente ?
  • b) Faites la trace de dicho([2, 5, 8, 12, 16, 23, 38, 56, 72, 91], 23) : donnez à chaque tour les valeurs de g, d et m.
  • c) Même travail pour la valeur 40. Combien de tours dans chaque cas ?
  • d) Combien de comparaisons au maximum pour un tableau de 1 000 éléments ? de un million ? Justifiez par la formule et comparez à une recherche séquentielle.
  • e) Un élève écrit m = (g + d) / 2 au lieu de la division entière. Décrivez l'erreur. Un autre écrit g = m au lieu de m + 1 : décrivez la sienne.

Tape tes réponses, la page te dit juste ou faux 0/8

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) Tableau trié ; 1-1 si absent
  • b) 23 trouvé en 3 tours
  • c) 40 absent : 4 tours
  • d) 10 et 20 comparaisons au plus
  • e) Division flottante bruyante ; g = m boucle

a) La précondition est que le tableau soit TRIÉ par ordre croissant. Sans elle, la comparaison t[m] < v ne permet plus de conclure dans quelle moitié chercher, et la fonction renvoie un résultat arbitraire sans jamais signaler d'erreur. Si la valeur est absente, la boucle se termine lorsque g dépasse d, et la fonction renvoie 1-1.

b) Le tableau a 10 éléments, d'indices 0 à 9. Tour 1 : g=0g = 0, d=9d = 9, m=4m = 4, t[4]=16<23t[4] = 16 < 23, donc gg devient 5. Tour 2 : g=5g = 5, d=9d = 9, m=7m = 7, t[7]=56>23t[7] = 56 > 23, donc dd devient 6. Tour 3 : g=5g = 5, d=6d = 6, m=5m = 5, t[5]=23t[5] = 23, trouvé : la fonction renvoie 5. Trois tours.

c) Pour la valeur 40. Tour 1 : g=0g = 0, d=9d = 9, m=4m = 4, 16<4016 < 40, gg devient 5. Tour 2 : g=5g = 5, d=9d = 9, m=7m = 7, 56>4056 > 40, dd devient 6. Tour 3 : g=5g = 5, d=6d = 6, m=5m = 5, 23<4023 < 40, gg devient 6. Tour 4 : g=6g = 6, d=6d = 6, m=6m = 6, t[6]=38<40t[6] = 38 < 40, gg devient 7. La condition gdg \le d est alors fausse et la boucle s'arrête : la fonction renvoie 1-1. Quatre tours. On retient que l'échec coûte à peine plus cher que le succès, contrairement à la recherche séquentielle où l'échec coûte le maximum.

d) Chaque tour divise par deux la taille de la zone restante, donc le nombre de tours est au plus log2n+1\lfloor \log_{2} n \rfloor + 1. Pour n=1 000n = 1\ 000 : log2(1 000)9,97\log_{2}(1\ 000) \approx 9{,}97, donc au plus 10 comparaisons. Pour n=106n = 10^{6} : log2(106)19,93\log_{2}(10^{6}) \approx 19{,}93, donc au plus 20 comparaisons. Une recherche séquentielle en demanderait respectivement 1 000 et 1 000 000. Le rapport vaut donc 100 dans le premier cas et 50 000 dans le second : c'est le propre d'un gain logarithmique, il grandit avec la taille du problème.

e) Avec une division non entière, m devient un FLOTTANT, par exemple 4,54{,}5. L'accès t[4.5] lève un TypeError, car un indice de liste doit être entier. L'erreur est donc immédiate et bruyante, ce qui est une chance. Avec g = m au lieu de m + 1, la boucle peut ne plus PROGRESSER : quand il ne reste qu'un élément, g=d=mg = d = m, la valeur n'est pas trouvée et g reste égal à m, si bien que le tour suivant recalcule exactement le même m. La boucle est infinie, et cette fois rien ne le signale : le programme se fige. Le remède est de vérifier, comme on l'a fait pour un variant, que la quantité dgd - g décroît STRICTEMENT à chaque tour, ce que garantit le passage à m+1m + 1 et m1m - 1.

Exercice 2 : Le tri fusion

La figure montre le découpage d'un tableau de quatre éléments. Le tri fusion coupe jusqu'à obtenir des tableaux d'un seul élément, qui sont triés par définition, puis fusionne deux à deux en remontant.

38 27 43 338 2743 33827433on découpe, puis on fusionne en remontant
  • a) Donnez le contenu de chaque case de l'arbre à la remontée, c'est-à-dire après chaque fusion, jusqu'au tableau final trié.
  • b) Écrivez l'algorithme de fusion de deux tableaux déjà triés, en français, et donnez son coût pour deux tableaux de tailles pp et qq.
  • c) Combien de niveaux l'arbre compte-t-il pour un tableau de nn éléments ? Quel travail est effectué à chaque niveau ? Déduisez-en le coût total.
  • d) Écrivez la relation de récurrence vérifiée par le coût C(n)C(n) et vérifiez que C(n)=nlog2nC(n) = n \log_{2} n la satisfait pour nn puissance de 2.
  • e) Le tri fusion est-il stable ? Combien de mémoire supplémentaire demande-t-il ? Comparez avec un tri par insertion sur ces deux critères et sur le coût.

Tape tes réponses, la page te dit juste ou faux 0/6

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) Fusions : 27, 38 et 3, 43, puis 3, 27, 38, 43
  • b) Fusion linéaire en p+qp + q
  • c) log2n\log_{2} n niveaux de travail nn
  • d) C(n)=2C(n/2)+nC(n) = 2C(n/2) + n vérifiée
  • e) Stable, mémoire nn

a) Au niveau le plus bas, les quatre tableaux d'un élément sont 38, 27, 43 et 3. Première fusion : 38 et 27 donnent 27, 38 ; 43 et 3 donnent 3, 43. Seconde fusion : on fusionne 27, 38 avec 3, 43. On compare 27 et 3, on prend 3 ; on compare 27 et 43, on prend 27 ; on compare 38 et 43, on prend 38 ; il ne reste que 43, on le prend. Le tableau final est 3, 27, 38, 43.

b) L'algorithme : on place un indice au début de chacun des deux tableaux et l'on crée un tableau résultat vide. Tant que les deux indices sont valides, on compare les deux éléments pointés, on ajoute le plus petit au résultat et on avance l'indice correspondant. Quand l'un des deux tableaux est épuisé, on recopie tout ce qui reste de l'autre. Le coût est de p+qp + q opérations au plus, car chaque élément est examiné et recopié exactement une fois : la fusion est LINÉAIRE, et c'est cette propriété qui fait tout l'intérêt de la méthode.

c) Chaque niveau divise par deux la taille des tableaux, donc l'arbre compte log2n\log_{2} n niveaux de découpage, plus le niveau des feuilles, soit log2n+1\log_{2} n + 1 niveaux. À chaque niveau, les fusions portent au total sur les nn éléments du tableau, quel que soit le nombre de tableaux à ce niveau : deux fusions de n/2n/2 éléments coûtent autant qu'une fusion de nn. Le travail par niveau vaut donc nn, et le coût total vaut nlog2nn \log_{2} n. C'est le raisonnement le plus important du chapitre : on ne compte pas les appels, on compte le travail PAR NIVEAU et on multiplie par le nombre de niveaux.

d) La récurrence : C(n)=2C(n/2)+nC(n) = 2 C(n/2) + n, avec C(1)=0C(1) = 0, puisqu'un tableau d'un élément est trié sans travail. Vérification avec C(n)=nlog2nC(n) = n \log_{2} n : le membre de droite vaut 2×(n/2)log2(n/2)+n=n(log2n1)+n=nlog2nn+n=nlog2n2 \times (n/2) \log_{2}(n/2) + n = n (\log_{2} n - 1) + n = n \log_{2} n - n + n = n \log_{2} n, qui est bien le membre de gauche. Et C(1)=1×log21=0C(1) = 1 \times \log_{2} 1 = 0, ce qui vérifie le cas de base.

e) Le tri fusion est STABLE, à condition que la comparaison de la fusion prenne l'élément du tableau de GAUCHE en cas d'égalité : deux éléments égaux conservent alors leur ordre initial. Il demande une mémoire supplémentaire de l'ordre de nn, pour le tableau résultat de chaque fusion, ce qui est son principal défaut. Comparaison avec le tri par insertion : celui-ci est également stable, ne demande AUCUNE mémoire supplémentaire, et coûte n2/4n^{2}/4 en moyenne. Pour n=10 000n = 10\ 000, l'insertion demande environ 2,5×1072{,}5 \times 10^{7} opérations contre 1,3×1051{,}3 \times 10^{5} pour la fusion, soit 188 fois plus. On garde donc l'insertion pour les très petits tableaux, où sa simplicité l'emporte, et la fusion au-delà : les bibliothèques réelles combinent d'ailleurs les deux, en basculant sur l'insertion sous une trentaine d'éléments.

Exercice 3 : Le tri rapide et le choix du pivot

La figure montre l'opération centrale du tri rapide : la partition. On choisit un pivot, ici la valeur 5, et l'on réorganise le tableau pour que tout ce qui lui est inférieur soit à sa gauche et tout ce qui lui est supérieur à sa droite.

3382215415487766plus petitspivotplus grandsavantaprès
  • a) Décrivez ce que garantit la partition. Le pivot est-il à sa place définitive ? Les deux zones sont-elles triées ?
  • b) Écrivez l'algorithme complet du tri rapide en trois lignes, en supposant la partition donnée. Où se situe le travail par rapport au tri fusion ?
  • c) Le tableau est déjà trié et l'on choisit systématiquement le premier élément comme pivot. Décrivez le découpage obtenu et donnez le coût total.
  • d) Donnez la relation de récurrence dans le meilleur cas et dans le pire cas, et le coût correspondant. Pour n=10 000n = 10\ 000, comparez les deux.
  • e) Citez deux stratégies de choix du pivot qui rendent le pire cas improbable, et expliquez pour chacune pourquoi elle fonctionne.

Tape tes réponses, la page te dit juste ou faux 0/6

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) Pivot placé, zones non triées
  • b) Travail avant les appels
  • c) Tableau trié : coût quadratique
  • d) nlog2nn \log_{2} n contre n2/2n^{2}/2
  • e) Pivot aléatoire ou médiane de trois

a) La partition garantit que tous les éléments à gauche du pivot lui sont inférieurs ou égaux, et tous ceux à sa droite supérieurs. Le pivot EST à sa place définitive : dans le tableau trié, il occupera exactement cette position, puisque le nombre d'éléments qui lui sont inférieurs ne changera plus. Les deux zones, en revanche, ne sont PAS triées : sur la figure, la zone gauche vaut 3, 2, 1, 4 et la droite 8, 7, 6, ni l'une ni l'autre n'étant en ordre. C'est précisément ce qui reste à faire, et c'est l'objet des appels récursifs.

b) L'algorithme : si le tableau a moins de deux éléments, il est trié, on s'arrête ; sinon on partitionne autour d'un pivot, ce qui donne sa position définitive ; puis on trie récursivement la zone de gauche et la zone de droite. Par rapport au tri fusion, le travail est situé AVANT les appels récursifs, dans la partition, alors que le tri fusion travaille APRÈS, dans la fusion. Le tri fusion découpe sans réfléchir et recombine intelligemment ; le tri rapide découpe intelligemment et recombine sans rien faire. Les deux stratégies sont symétriques, et c'est ce qui explique leurs profils d'erreur opposés.

c) Sur un tableau déjà trié avec le premier élément comme pivot, la partition ne trouve AUCUN élément plus petit : la zone gauche est vide et la zone droite contient les n1n - 1 éléments restants. Le découpage est donc totalement déséquilibré, un contre n1n - 1 à chaque étape, et la profondeur de récursion vaut nn au lieu de log2n\log_{2} n. Le coût total vaut (n1)+(n2)++1=n(n1)/2(n-1) + (n-2) + \dots + 1 = n(n-1)/2, c'est-à-dire QUADRATIQUE. Le paradoxe mérite d'être noté : l'entrée la plus favorable pour un humain est la pire pour cet algorithme, et c'est aussi l'entrée la plus fréquente en pratique, puisque les données arrivent souvent déjà classées.

d) Meilleur cas, partition équilibrée : C(n)=2C(n/2)+nC(n) = 2 C(n/2) + n, d'où C(n)=nlog2nC(n) = n \log_{2} n, exactement comme le tri fusion. Pire cas, partition dégénérée : C(n)=C(n1)+nC(n) = C(n-1) + n, d'où C(n)=n(n+1)/2C(n) = n(n+1)/2, quadratique. Pour n=10 000n = 10\ 000 : le meilleur cas donne environ 1,33×1051{,}33 \times 10^{5} opérations, le pire environ 5,00×1075{,}00 \times 10^{7}. Le rapport vaut environ 376 : c'est la différence entre un tri instantané et un tri qu'on voit passer.

e) Première stratégie, le PIVOT ALÉATOIRE : on tire au hasard l'indice du pivot. Elle fonctionne parce que le pire cas ne dépend plus des données mais du tirage : aucune entrée particulière ne peut le provoquer systématiquement, et la probabilité d'obtenir plusieurs partitions très déséquilibrées de suite décroît exponentiellement. On démontre que le coût moyen reste O(nlogn)O(n \log n) quelle que soit l'entrée. Seconde stratégie, la MÉDIANE DE TROIS : on prend la médiane du premier, du dernier et de l'élément central. Elle fonctionne parce qu'elle traite exactement les cas problématiques réels, tableau trié ou trié à l'envers, pour lesquels elle choisit l'élément central, donc une partition parfaitement équilibrée. Elle ne protège pas contre une entrée construite exprès pour la mettre en échec, ce que le pivot aléatoire, lui, garantit.

Exercice 4 : Lire une récurrence de coût

Diviser pour régner produit toujours la même forme de récurrence : le coût d'un problème de taille nn vaut le coût des sous-problèmes plus le travail de découpage et de recombinaison. Savoir la lire dispense de tout calcul.

  • a) Pour chacune des trois récurrences suivantes, identifiez l'algorithme et donnez le coût : C(n)=C(n/2)+1C(n) = C(n/2) + 1 ; C(n)=2C(n/2)+nC(n) = 2C(n/2) + n ; C(n)=C(n1)+nC(n) = C(n-1) + n.
  • b) Expliquez la méthode générale : combien de sous-problèmes à chaque niveau, quelle taille, quel travail par niveau, combien de niveaux ?
  • c) Appliquez cette méthode à C(n)=2C(n/2)+1C(n) = 2C(n/2) + 1. Quel algorithme classique a ce profil ?
  • d) Appliquez-la à C(n)=4C(n/2)+nC(n) = 4C(n/2) + n. Que vaut le travail au niveau kk, et quel niveau domine ?
  • e) Deux algorithmes coûtent respectivement 100n100 n et nlog2nn \log_{2} n. À partir de quelle taille le second devient-il plus coûteux ? Que faut-il en conclure sur l'usage des ordres de grandeur ?

Tape tes réponses, la page te dit juste ou faux 0/7

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) Dichotomie, tri fusion, tri rapide au pire
  • b) Arbre de récursion : travail par niveau
  • c) 2C(n/2)+12C(n/2) + 1 : linéaire
  • d) 4C(n/2)+n4C(n/2) + n : quadratique
  • e) Seuil 21002^{100} : les constantes comptent

a) Première récurrence, C(n)=C(n/2)+1C(n) = C(n/2) + 1 : un seul sous-problème de taille moitié et un travail constant. C'est la recherche DICHOTOMIQUE, de coût log2n\log_{2} n. Deuxième, C(n)=2C(n/2)+nC(n) = 2C(n/2) + n : deux sous-problèmes de taille moitié et un travail linéaire. C'est le TRI FUSION, de coût nlog2nn \log_{2} n. Troisième, C(n)=C(n1)+nC(n) = C(n-1) + n : un sous-problème à peine plus petit et un travail linéaire. C'est le tri rapide dans son PIRE CAS, ou le tri par sélection, de coût n2/2n^{2}/2.

b) La méthode générale, dite de l'arbre de récursion. On se demande d'abord combien de sous-problèmes chaque appel engendre, appelons ce nombre aa, et de quelle taille, disons n/bn/b. Au niveau kk, il y a donc aka^{k} sous-problèmes de taille n/bkn/b^{k}. On se demande ensuite quel travail chacun effectue en dehors des appels récursifs, et l'on multiplie par aka^{k} pour obtenir le travail du niveau. Enfin, le nombre de niveaux est le nombre de divisions nécessaires pour atteindre la taille 1, soit logbn\log_{b} n. Le coût total est la somme des travaux de tous les niveaux, et le résultat dépend de qui l'emporte : si tous les niveaux coûtent autant, on multiplie ; si le travail décroît, le premier niveau domine ; s'il croît, le dernier domine.

c) Pour C(n)=2C(n/2)+1C(n) = 2C(n/2) + 1 : il y a 2k2^{k} sous-problèmes au niveau kk, chacun coûtant 1, donc le niveau kk coûte 2k2^{k}. Le travail CROÎT avec la profondeur, et le dernier niveau, celui des feuilles, en compte 2log2n=n2^{\log_{2} n} = n. La somme 1+2+4++n1 + 2 + 4 + \dots + n vaut 2n12n - 1 : le coût est LINÉAIRE, et le dernier niveau porte à lui seul la moitié du total. Le profil correspond au parcours complet d'un arbre binaire équilibré, ou au calcul du maximum d'un tableau par découpage en deux.

d) Pour C(n)=4C(n/2)+nC(n) = 4C(n/2) + n : au niveau kk il y a 4k4^{k} sous-problèmes de taille n/2kn/2^{k}, chacun coûtant n/2kn/2^{k}, donc le niveau coûte 4k×n/2k=n×2k4^{k} \times n / 2^{k} = n \times 2^{k}. Le travail DOUBLE à chaque niveau : c'est donc le DERNIER qui domine. Le nombre de niveaux vaut log2n\log_{2} n, donc le dernier niveau coûte n×2log2n=n×n=n2n \times 2^{\log_{2} n} = n \times n = n^{2}, et la somme géométrique vaut environ 2n22n^{2}. Le coût est quadratique. C'est exactement le profil de la multiplication de matrices par blocs naïve, que l'algorithme de Strassen améliore en ramenant les 4 sous-problèmes à 7 sur des blocs deux fois plus petits.

e) On cherche nn tel que nlog2n>100nn \log_{2} n > 100 n, c'est-à-dire log2n>100\log_{2} n > 100, donc n>21001,27×1030n > 2^{100} \approx 1{,}27 \times 10^{30}. Autrement dit, JAMAIS en pratique : aucun jeu de données réel n'atteint cette taille. Il faut en conclure qu'un ordre de grandeur ne dit rien sur les tailles utiles s'il est pris seul. La constante multiplicative compte, et un algorithme asymptotiquement meilleur peut être plus lent sur toutes les entrées qu'on rencontrera. On compare donc les ordres de grandeur pour choisir une STRUCTURE d'algorithme, et l'on mesure pour trancher entre deux implémentations de même ordre.

Exercice 5 : Programmation dynamique : le rendu de monnaie

Rendre la monnaie avec le moins de pièces possible paraît trivial : on prend toujours la plus grosse pièce qui passe. Cette méthode gloutonne est fausse, et le contre-exemple tient en deux lignes.

  • a) Avec le système de pièces 1, 2, 5, 10, rendez 38 par la méthode gloutonne. Combien de pièces ?
  • b) Avec le système 1, 3, 4, rendez 6 par la méthode gloutonne, puis donnez la solution optimale. Quelle est la conclusion ?
  • c) On note m(s)m(s) le nombre minimal de pièces pour rendre la somme ss. Écrivez la relation de récurrence vérifiée par mm, avec son cas de base, pour un système de pièces donné.
  • d) Appliquez cette relation au système 1, 3, 4 pour ss allant de 0 à 6. Présentez le tableau des valeurs.
  • e) Écrivez l'algorithme qui remplit ce tableau de bas en haut, donnez son coût pour une somme ss et pp pièces, et expliquez pourquoi il est préférable à une version récursive naïve.

Tape tes réponses, la page te dit juste ou faux 0/7

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) 38 : 6 pièces
  • b) Glouton 3 pièces, optimum 2
  • c) m(s)=1+minm(sc)m(s) = 1 + \min m(s - c)
  • d) Tableau 0, 1, 2, 1, 1, 2, 2
  • e) Remplissage en s×ps \times p

a) Système 1, 2, 5, 10, somme 38. On prend trois pièces de 10, il reste 8 ; une pièce de 5, il reste 3 ; une pièce de 2, il reste 1 ; une pièce de 1. Total : 3+1+1+1=63 + 1 + 1 + 1 = 6 pièces. Ce résultat est bien optimal, et l'on démontre que la méthode gloutonne l'est toujours pour ce système, ce qui explique pourquoi personne ne s'en méfie au quotidien.

b) Système 1, 3, 4, somme 6. La méthode gloutonne prend une pièce de 4, il reste 2 ; puis deux pièces de 1 : total 3 pièces. La solution optimale est deux pièces de 3, soit 2 pièces. La conclusion est que la méthode gloutonne N'EST PAS optimale en général : elle l'est pour certains systèmes de pièces, dits canoniques, et fausse pour d'autres. Une méthode gloutonne doit donc toujours être justifiée, jamais supposée ; et le contre-exemple, ici, tient en une ligne.

c) La relation : m(0)=0m(0) = 0, car rendre zéro ne demande aucune pièce ; et pour s>0s > 0, m(s)=1+min{m(sc)}m(s) = 1 + \min\{m(s - c)\}, le minimum étant pris sur toutes les pièces cc du système telles que csc \le s. La lecture est directe : quelle que soit la solution optimale, elle utilise au moins une pièce ; si l'on connaissait laquelle, le reste serait une solution optimale pour scs - c. Comme on ne le sait pas, on essaie toutes les pièces et l'on garde la meilleure. C'est le principe d'optimalité, qui fonde toute la programmation dynamique.

d) Système 1, 3, 4. m(0)=0m(0) = 0. m(1)=1+m(0)=1m(1) = 1 + m(0) = 1, seule la pièce de 1 est utilisable. m(2)=1+m(1)=2m(2) = 1 + m(1) = 2. m(3)=1+min(m(2),m(0))=1+min(2,0)=1m(3) = 1 + \min(m(2), m(0)) = 1 + \min(2, 0) = 1, en prenant la pièce de 3. m(4)=1+min(m(3),m(1),m(0))=1+min(1,1,0)=1m(4) = 1 + \min(m(3), m(1), m(0)) = 1 + \min(1, 1, 0) = 1, en prenant la pièce de 4. m(5)=1+min(m(4),m(2),m(1))=1+min(1,2,1)=2m(5) = 1 + \min(m(4), m(2), m(1)) = 1 + \min(1, 2, 1) = 2. m(6)=1+min(m(5),m(3),m(2))=1+min(2,1,2)=2m(6) = 1 + \min(m(5), m(3), m(2)) = 1 + \min(2, 1, 2) = 2, en prenant une pièce de 3 puis m(3)=1m(3) = 1. Le tableau est donc 0, 1, 2, 1, 1, 2, 2 pour ss de 0 à 6, ce qui confirme le résultat de la question b.

e) L'algorithme : on crée un tableau de taille s+1s + 1, on met m[0]m[0] à 0 et toutes les autres cases à une valeur très grande ; puis, pour kk allant de 1 à ss, et pour chaque pièce cc inférieure ou égale à kk, on remplace m[k]m[k] par 1+m[kc]1 + m[k - c] si c'est plus petit ; on renvoie m[s]m[s]. Le coût est de s×ps \times p opérations, puisque chaque case examine chaque pièce : il est donc PSEUDO-POLYNOMIAL, linéaire en la somme et en le nombre de pièces. Il est préférable à la version récursive naïve pour la raison vue avec Fibonacci : celle-ci recalcule les mêmes sous-sommes un nombre exponentiel de fois, alors que le remplissage de bas en haut calcule chaque valeur exactement une fois et la conserve. On peut aussi mémoriser la pièce choisie dans un second tableau, ce qui permet de reconstituer la liste des pièces et pas seulement leur nombre.

Partie B : Problèmes et raisonnement (/50)

Exercice 6 : Programmation dynamique : le sac à dos

La table donne, pour chaque nombre d'objets considérés et chaque capacité, la valeur maximale atteignable. Les quatre objets ont pour couples poids et valeur : (2 ; 3)(2\ ;\ 3), (3 ; 4)(3\ ;\ 4), (4 ; 5)(4\ ;\ 5) et (5 ; 6)(5\ ;\ 6). La capacité du sac est 5.

00000001003333200344730034574003457012345icapacité
  • a) Expliquez ce que représente la case de la ligne ii et de la colonne cc. Pourquoi la ligne 0 est-elle remplie de zéros ?
  • b) Écrivez la relation de récurrence qui remplit la table, en distinguant les deux cas.
  • c) Vérifiez par le calcul les cases de la ligne 2, colonnes 3, 4 et 5.
  • d) Lisez la réponse au problème et reconstituez le contenu du sac en remontant la table. Détaillez la remontée.
  • e) Donnez le coût en temps et en mémoire pour nn objets et une capacité CC. Pourquoi ne dit-on pas que ce problème est résolu en temps polynomial ? Comment réduire la mémoire ?

Tape tes réponses, la page te dit juste ou faux 0/8

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) Case : meilleure valeur avec ii objets et capacité cc
  • b) Laisser ou prendre l'objet
  • c) Ligne 2 : 4, 4, 7
  • d) Valeur 7 : objets 1 et 2
  • e) Pseudo-polynomial, deux lignes suffisent

a) La case de la ligne ii et de la colonne cc contient la valeur maximale que l'on peut emporter en n'ayant le droit d'utiliser que les ii premiers objets et en ne dépassant pas la capacité cc. La ligne 0 est remplie de zéros parce qu'avec zéro objet disponible, on n'emporte rien, quelle que soit la capacité offerte. C'est le cas de base, et il est indispensable : c'est sur lui que s'appuient toutes les autres cases.

b) Notons pip_{i} et viv_{i} le poids et la valeur du ii-ième objet, et V(i,c)V(i, c) la case cherchée. Premier cas, si pi>cp_{i} > c : l'objet ne rentre pas, donc V(i,c)=V(i1,c)V(i, c) = V(i-1, c). Second cas, si picp_{i} \le c : on a le choix entre le laisser, ce qui donne V(i1,c)V(i-1, c), et le prendre, ce qui donne vi+V(i1,cpi)v_{i} + V(i-1, c - p_{i}) ; on garde le maximum des deux. La subtilité est le cpic - p_{i} : en prenant l'objet, on ne dispose plus que de la capacité restante pour les objets précédents.

c) Ligne 2, l'objet 2 pesant 3 et valant 4. Colonne 3 : l'objet rentre, on compare V(1,3)=3V(1, 3) = 3 à 4+V(1,0)=4+0=44 + V(1, 0) = 4 + 0 = 4 ; le maximum est 4, ce qui correspond à la table. Colonne 4 : on compare V(1,4)=3V(1, 4) = 3 à 4+V(1,1)=4+0=44 + V(1, 1) = 4 + 0 = 4 ; le maximum est 4, conforme. Colonne 5 : on compare V(1,5)=3V(1, 5) = 3 à 4+V(1,2)=4+3=74 + V(1, 2) = 4 + 3 = 7 ; le maximum est 7, conforme. Cette dernière case est la seule où les deux premiers objets tiennent ensemble, 2+3=52 + 3 = 5, exactement la capacité.

d) La réponse au problème est la case de la dernière ligne et de la dernière colonne, soit V(4,5)=7V(4, 5) = 7. Remontée. En (4,5)(4, 5), la valeur 7 est égale à V(3,5)=7V(3, 5) = 7 : l'objet 4 n'a donc pas été pris, on remonte en (3,5)(3, 5). En (3,5)(3, 5), la valeur 7 est égale à V(2,5)=7V(2, 5) = 7 : l'objet 3 n'a pas été pris, on remonte en (2,5)(2, 5). En (2,5)(2, 5), la valeur 7 diffère de V(1,5)=3V(1, 5) = 3 : l'objet 2 A été pris, on retire son poids et l'on va en (1,53)=(1,2)(1, 5 - 3) = (1, 2). En (1,2)(1, 2), la valeur 3 diffère de V(0,2)=0V(0, 2) = 0 : l'objet 1 a été pris, on va en (0,0)(0, 0) et l'on s'arrête. Le sac contient donc les objets 1 et 2, de poids total 2+3=52 + 3 = 5 et de valeur 3+4=73 + 4 = 7. La règle de la remontée : si la valeur est identique à celle de la ligne du dessus, l'objet n'a pas été pris ; sinon il l'a été.

e) Le coût en temps est de n×Cn \times C opérations, une par case, chacune valant un maximum de deux termes. Le coût en mémoire est également de n×Cn \times C cases. On ne dit pas que le problème est résolu en temps polynomial parce que la taille de l'ENTRÉE n'est pas CC mais le nombre de chiffres nécessaires pour écrire CC, soit environ log2C\log_{2} C bits : le coût nCnC est donc exponentiel en la taille de l'écriture de la capacité. On parle de coût PSEUDO-POLYNOMIAL, et le problème du sac à dos reste effectivement dans la classe des problèmes réputés difficiles. Pour réduire la mémoire, on remarque que le calcul d'une ligne n'utilise que la précédente : il suffit donc de conserver DEUX lignes, ce qui ramène la mémoire à 2C2C cases. On peut même n'en garder qu'une seule en parcourant les capacités par valeurs DÉCROISSANTES, mais on perd alors la possibilité de reconstituer le contenu du sac, qui exige toute la table.

Exercice 7 : Parcourir un graphe : profondeur, largeur, et à quoi ça sert

Les deux parcours de graphe ont le même squelette et diffèrent d'un seul mot : l'un utilise une pile, l'autre une file. Cette différence décide de tout ce qu'ils permettent de calculer.

  • a) Écrivez le squelette commun des deux parcours, en français, en cinq lignes, et dites où intervient le choix de la structure.
  • b) Pourquoi faut-il marquer un sommet AVANT de l'ajouter à la structure, et non au moment où on le retire ? Décrivez ce qui se passe si l'on s'y prend mal.
  • c) Donnez le coût d'un parcours en fonction du nombre de sommets et d'arêtes, pour une représentation par listes d'adjacence puis par matrice d'adjacence.
  • d) Pour chacun des cinq problèmes suivants, dites quel parcours employer et pourquoi : détecter un cycle, trouver le plus court chemin en nombre d'arêtes, compter les composantes connexes, tester si un graphe est biparti, sortir d'un labyrinthe sans se soucier de la longueur.
  • e) Le parcours en profondeur s'écrit aussi récursivement. Quelle structure remplace alors la pile explicite, et quelle limite cela introduit-il sur un graphe de un million de sommets ?

Tape tes réponses, la page te dit juste ou faux 0/7

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) Pile : profondeur ; file : largeur
  • b) Marquer avant d'ajouter
  • c) O(n+m)O(n + m) contre O(n2)O(n^{2})
  • d) Largeur pour distances et biparti
  • e) Récursion : limite de la pile d'appels

a) Le squelette : créer une structure et y placer le sommet de départ, en le marquant visité. Tant que la structure n'est pas vide : en retirer un sommet ; le traiter ; puis, pour chacun de ses voisins non encore visités, le marquer visité et l'ajouter à la structure. Le choix de la structure intervient à la troisième ligne, celle du retrait : avec une PILE, on retire le dernier ajouté et l'on obtient un parcours en profondeur ; avec une FILE, on retire le premier ajouté et l'on obtient un parcours en largeur. Tout le reste est identique, mot pour mot.

b) Il faut marquer AVANT l'ajout parce qu'un même sommet peut être voisin de plusieurs sommets traités successivement. Si l'on ne marque qu'au retrait, il sera ajouté une fois par voisin qui le découvre, et se retrouvera plusieurs fois dans la structure. Les conséquences : la structure enfle jusqu'à contenir de l'ordre du nombre d'arêtes au lieu du nombre de sommets, chaque sommet est traité plusieurs fois, et surtout, pour un parcours en largeur, la distance calculée peut être fausse puisqu'un sommet risque d'être atteint par un chemin plus long après l'avoir été par un plus court. Le programme donne encore un résultat, ce qui rend l'erreur difficile à voir : c'est le coût qui explose d'abord.

c) Avec des LISTES D'ADJACENCE : chaque sommet est retiré une fois de la structure et chacune de ses arêtes est examinée une fois depuis chaque extrémité, soit un coût de l'ordre de n+2mn + 2m pour un graphe non orienté, c'est-à-dire O(n+m)O(n + m), linéaire. Avec une MATRICE D'ADJACENCE : pour chaque sommet retiré, il faut parcourir sa ligne entière pour trouver ses voisins, soit nn cases, d'où un coût de O(n2)O(n^{2}) quel que soit le nombre d'arêtes. Sur un graphe creux, la différence est considérable : pour n=40 000n = 40\ 000 et m=90 000m = 90\ 000, on passe d'environ 2,2×1052{,}2 \times 10^{5} opérations à 1,6×1091{,}6 \times 10^{9}, soit plus de sept mille fois plus.

d) Détecter un CYCLE : parcours en profondeur, car un cycle se révèle par une arête qui ramène sur un sommet encore présent dans la descente en cours, information que la pile de récursion fournit naturellement. PLUS COURT CHEMIN en nombre d'arêtes : parcours en largeur, seul à découvrir les sommets par distance croissante. COMPOSANTES CONNEXES : l'un ou l'autre indifféremment, puisqu'on ne s'intéresse qu'à l'ensemble des sommets atteints ; on relance un parcours depuis chaque sommet non visité et l'on compte les relances. Tester si un graphe est BIPARTI : parcours en largeur, car il suffit de colorier chaque sommet selon la parité de sa distance à la source et de vérifier qu'aucune arête ne relie deux sommets de même couleur. Sortir d'un LABYRINTHE sans se soucier de la longueur : parcours en profondeur, qui trouve une sortie plus vite en moyenne et consomme beaucoup moins de mémoire, puisqu'il ne mémorise qu'un chemin au lieu d'un front d'onde entier.

e) La pile explicite est remplacée par la PILE D'APPELS du langage : chaque appel récursif empile un cadre, exactement comme au chapitre de la récursivité. La limite introduite est la profondeur maximale de récursion, de l'ordre de mille en Python par défaut. Sur un graphe de un million de sommets, un chemin long suffit à la dépasser : un simple chemin de mille sommets provoque une RecursionError, alors que la version itérative avec une pile explicite traiterait un million de sommets sans difficulté, la pile étant alors une structure ordinaire logée dans le tas. C'est la raison pour laquelle tout parcours destiné à de grands graphes s'écrit itérativement, malgré l'élégance de la version récursive.

Exercice 8 : Cinq affirmations à corriger

Chacune des cinq affirmations suivantes est FAUSSE. Dites pourquoi et donnez l'énoncé correct.

  • 1) « La recherche dichotomique fonctionne sur n'importe quel tableau. »
  • 2) « Le tri rapide est plus rapide que le tri fusion, comme son nom l'indique. »
  • 3) « La méthode gloutonne donne toujours le nombre minimal de pièces. »
  • 4) « Le sac à dos se résout en temps polynomial par programmation dynamique. »
  • 5) « Un algorithme en 100 n est toujours préférable à un algorithme en n log n, puisqu'il est d'un ordre de grandeur inférieur. »

Tape tes réponses, la page te dit juste ou faux 0/5

1)
2)
3)
4)
5)
Voir la correction

Réponses

  • 1) Dichotomie sur tableau trié
  • 2) Tri rapide sans garantie
  • 3) Glouton non optimal
  • 4) Sac à dos pseudo-polynomial
  • 5) Constantes et tailles réelles

1) FAUX. Elle exige que le tableau soit TRIÉ : c'est sur cette hypothèse que repose la conclusion « si l'élément du milieu est plus petit, la valeur cherchée est dans la moitié droite ». Sur un tableau non trié, la fonction renvoie un résultat arbitraire sans signaler d'erreur. Énoncé correct : la recherche dichotomique exige un tableau trié, et sa précondition doit être documentée, faute de quoi elle produit des faux négatifs silencieux.

2) FAUX en général. Les deux coûtent nlog2nn \log_{2} n en moyenne, mais le tri rapide dégénère en n2n^{2} dans son pire cas, notamment sur un tableau déjà trié avec un pivot mal choisi, alors que le tri fusion garantit nlog2nn \log_{2} n dans TOUS les cas. Énoncé correct : le tri rapide est souvent plus rapide en pratique, grâce à de meilleures constantes et à un usage du cache favorable, mais il n'offre aucune garantie ; le tri fusion en offre une, au prix d'une mémoire supplémentaire linéaire.

3) FAUX. Avec le système 1, 3, 4 et la somme 6, la méthode gloutonne donne 3 pièces, 4+1+14 + 1 + 1, alors que l'optimum en demande 2, 3+33 + 3. Énoncé correct : la méthode gloutonne n'est optimale que pour certains systèmes de pièces ; elle doit être démontrée pour le système considéré, et la programmation dynamique donne l'optimum quel que soit le système.

4) FAUX, au sens strict. Le coût nCnC est linéaire en la CAPACITÉ, mais la capacité s'écrit avec environ log2C\log_{2} C chiffres : rapporté à la taille de l'entrée, le coût est exponentiel. Énoncé correct : la programmation dynamique résout le sac à dos en temps PSEUDO-POLYNOMIAL ; le problème reste dans la classe des problèmes réputés difficiles, et l'algorithme devient impraticable dès que la capacité est un grand nombre.

5) FAUX. On a nlog2n<100nn \log_{2} n < 100 n tant que log2n<100\log_{2} n < 100, c'est-à-dire pour tout nn inférieur à 21002^{100}, soit 1,27×10301{,}27 \times 10^{30} : aucun jeu de données réel n'atteint ce seuil, donc l'algorithme en nlog2nn \log_{2} n est le plus rapide sur toutes les entrées qu'on rencontrera jamais. Énoncé correct : un ordre de grandeur ignore les constantes, donc il ne permet de comparer deux algorithmes que sur les grandes tailles ; sur les tailles réelles, seules une analyse fine et une mesure tranchent.

Exercice 9 : L'algorithme de Dijkstra, pas à pas

Le graphe donne six routeurs et le coût de chaque liaison. On cherche les plus courts chemins depuis A vers tous les autres. Tous les coûts sont positifs, ce qui est l'hypothèse indispensable de la méthode.

4215810263ABCDEF
  • a) Décrivez l'algorithme en cinq lignes, en insistant sur le critère de sélection du prochain sommet à traiter.
  • b) Déroulez l'algorithme depuis A. Donnez, à chaque étape, le sommet sélectionné, sa distance définitive et les distances provisoires mises à jour.
  • c) Donnez le plus court chemin de A à F et son coût. Combien de chemins de A à F existe-t-il, et pourquoi l'algorithme n'en a-t-il examiné qu'une petite partie ?
  • d) Énoncez et justifiez la propriété qui garantit qu'une distance sortie de la file est DÉFINITIVE. Où intervient l'hypothèse des poids positifs ?
  • e) On autorise maintenant des liens de coût négatif. Montrez sur un graphe orienté de trois sommets que l'algorithme peut alors se tromper, et nommez la méthode à employer à la place.

Tape tes réponses, la page te dit juste ou faux 0/8

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) Extraire la plus petite distance provisoire
  • b) Distances 0, 3, 2, 8, 10, 13
  • c) A, C, B, D, E, F : coût 13
  • d) Poids positifs : distance définitive
  • e) Arc négatif : Bellman-Ford

a) L'algorithme. Un, attribuer la distance 0 à la source et l'infini à tous les autres sommets, et placer tous les sommets dans une file de priorité. Deux, tant que la file n'est pas vide, en extraire le sommet de distance provisoire MINIMALE. Trois, sa distance est alors définitive. Quatre, pour chacun de ses voisins encore dans la file, calculer la distance obtenue en passant par lui, et la remplacer si elle est meilleure que la distance provisoire connue : c'est le relâchement. Cinq, recommencer. Le critère de sélection est le point décisif : on choisit toujours le plus proche non encore traité, jamais le voisin le plus proche du dernier traité.

b) Étape 1 : on sélectionne A, distance définitive 0. Relâchements : B passe à 4 par A, C passe à 2 par A. Étape 2 : le minimum provisoire est C à 2, on le sélectionne, distance définitive 2. Relâchements : B passe de 4 à 2+1=32 + 1 = 3 par C, D passe à 2+8=102 + 8 = 10, E passe à 2+10=122 + 10 = 12. Étape 3 : le minimum est B à 3, sélectionné, définitif. Relâchement : D passe de 10 à 3+5=83 + 5 = 8. Étape 4 : le minimum est D à 8, sélectionné, définitif. Relâchements : E passe de 12 à 8+2=108 + 2 = 10, F passe à 8+6=148 + 6 = 14. Étape 5 : le minimum est E à 10, sélectionné, définitif. Relâchement : F passe de 14 à 10+3=1310 + 3 = 13. Étape 6 : F à 13, sélectionné, définitif. Distances finales : A 0, B 3, C 2, D 8, E 10, F 13.

c) Le plus court chemin de A à F est A, C, B, D, E, F, de coût 2+1+5+2+3=132 + 1 + 5 + 2 + 3 = 13. On le reconstitue en remontant les prédécesseurs enregistrés à chaque relâchement retenu. Le nombre de chemins simples de A à F est bien plus grand : on en compte plusieurs dizaines dans ce petit graphe, et ce nombre croît de façon combinatoire avec la taille. L'algorithme n'en a examiné qu'une petite partie parce qu'il n'énumère aucun chemin : il ne fait que relâcher des arêtes, chacune au plus une fois, et la garantie de la question d lui permet de ne jamais revenir sur une décision.

d) La propriété : au moment où un sommet est extrait avec la plus petite distance provisoire, cette distance est la distance définitive. Justification : supposons qu'il existe un chemin plus court vers ce sommet. Ce chemin devrait passer par au moins un sommet encore dans la file ; or tous ceux-là ont une distance provisoire supérieure ou égale à celle du sommet extrait, puisque c'est le minimum. Le chemin en question aurait donc déjà une longueur supérieure ou égale avant même d'atteindre sa destination, et il ne pourrait que s'allonger ensuite : il ne peut pas être plus court. C'est là qu'intervient l'hypothèse des POIDS POSITIFS, dans le mot « s'allonger » : si des arêtes pouvaient être négatives, la suite du chemin pourrait au contraire raccourcir le total, et le raisonnement s'effondre.

e) Prenons trois sommets et trois arcs : A vers B de coût 2, A vers C de coût 1, B vers C de coût 3-3. L'algorithme sélectionne A à 0 et relâche B à 2 et C à 1. Il sélectionne ensuite C, le plus proche, et déclare sa distance 1 DÉFINITIVE. Il sélectionne enfin B à 2 et découvre que le chemin A, B, C coûte 23=12 - 3 = -1, moins que 1 : mais C est déjà sorti de la file, et la distance fausse reste. L'erreur tient exactement à l'argument de la question d : la fin du chemin, par l'arc négatif, a raccourci le total au lieu de l'allonger. La méthode à employer en présence de poids négatifs est l'algorithme de Bellman-Ford, qui relâche toutes les arêtes n1n - 1 fois au lieu de figer les sommets un par un, et qui détecte au passage les cycles de poids négatif, cas où la notion même de plus court chemin perd son sens.

Exercice 10 : Problème : organiser un plan de révision

Un élève dispose de 12 heures avant le baccalauréat. Six chapitres sont possibles, chacun avec un temps de révision et un gain de points estimé : A 2 h et 4 points, B 3 h et 5 points, C 4 h et 8 points, D 5 h et 9 points, E 1 h et 2 points, F 6 h et 11 points. Un chapitre se révise entièrement ou pas du tout.

  • a) Reconnaissez le problème. Quelle méthode gloutonne vient à l'esprit ? Appliquez-la et donnez le résultat.
  • b) La méthode gloutonne est-elle optimale ici ? Cherchez une meilleure combinaison et concluez.
  • c) Décrivez la table de programmation dynamique à construire : dimensions, signification d'une case, relation de récurrence.
  • d) Donnez le nombre de cases à remplir et le coût total. Comparez au nombre de combinaisons qu'une recherche exhaustive devrait examiner.
  • e) On change une hypothèse : un chapitre peut désormais être révisé partiellement, le gain étant proportionnel au temps passé. Le problème change de nature. Quelle méthode devient optimale, et pourquoi ? Donnez la solution.

Tape tes réponses, la page te dit juste ou faux 0/9

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) Sac à dos : glouton à 23 points
  • b) Optimum 23, glouton chanceux
  • c) Table 7×137 \times 13
  • d) 91 cases contre 64 combinaisons
  • e) Fractionnaire : glouton optimal, 23,1723{,}17

a) C'est un problème de SAC À DOS : une capacité, ici 12 heures, des objets indivisibles avec un poids et une valeur, et une valeur totale à maximiser. La méthode gloutonne qui vient à l'esprit consiste à classer les chapitres par RENDEMENT, c'est-à-dire par points par heure, et à prendre dans cet ordre tant que le temps le permet. Rendements : A vaut 2, B environ 1,671{,}67, C vaut 2, D vaut 1,81{,}8, E vaut 2, F environ 1,831{,}83. Ordre décroissant : A, C et E à 2, puis F à 1,831{,}83, puis D à 1,81{,}8, puis B à 1,671{,}67. On prend A, 2 h et 4 points, il reste 10 h ; C, 4 h et 8 points, il reste 6 h ; E, 1 h et 2 points, il reste 5 h ; F demande 6 h, refusé ; D demande 5 h, accepté, 9 points, il reste 0 h. Total : 2+4+1+5=122 + 4 + 1 + 5 = 12 heures et 4+8+2+9=234 + 8 + 2 + 9 = 23 points.

b) Ici la méthode gloutonne tombe juste, mais rien ne le garantissait. Vérifions qu'aucune combinaison ne fait mieux : le total des gains de tous les chapitres vaut 39 points pour 21 heures, et l'on ne peut en retenir que 12. En essayant les regroupements à 12 heures exactement, on trouve A, C, E, D à 23 points ; A, C, F à 23 points également ; B, C, E, A à 19 ; D, F à 20 ; C, F à 19 plus E, soit 21. Le maximum est donc 23, atteint par deux combinaisons différentes. La conclusion est que le glouton était optimal sur CETTE instance, ce qui ne prouve rien : il suffirait de remplacer F par un chapitre de 7 heures rapportant 15 points pour qu'il échoue : le glouton prend ce chapitre, de meilleur rendement, puis A et E, pour 21 points, alors que C, E et ce chapitre en rapportent 25 en 12 heures.

c) La table a 77 lignes, de 0 à 6 chapitres considérés, et 1313 colonnes, de 0 à 12 heures. La case de la ligne ii et de la colonne tt contient le gain maximal atteignable en n'utilisant que les ii premiers chapitres et au plus tt heures. La récurrence : si le temps du chapitre ii dépasse tt, la case vaut celle du dessus, V(i1,t)V(i-1, t) ; sinon elle vaut le maximum entre V(i1,t)V(i-1, t), on ne le révise pas, et gi+V(i1,thi)g_{i} + V(i-1, t - h_{i}), on le révise. La ligne 0 est nulle partout, et la réponse se lit en bas à droite.

d) Il y a 7×13=917 \times 13 = 91 cases, chacune demandant une comparaison de deux termes, soit un coût de l'ordre de 91 opérations, disons quelques centaines en comptant tout. Une recherche exhaustive devrait examiner tous les sous-ensembles de 6 chapitres, soit 26=642^{6} = 64 combinaisons, ce qui est ici du même ordre. L'écart n'apparaît qu'en grandissant : avec 40 chapitres et 200 heures, la table compte 41×201=8 24141 \times 201 = 8\ 241 cases alors que la recherche exhaustive devrait examiner 2401,10×10122^{40} \approx 1{,}10 \times 10^{12} combinaisons, soit environ 133 millions de fois plus. C'est exactement le passage de l'exponentiel au pseudo-polynomial.

e) Si les chapitres deviennent divisibles, le problème devient le sac à dos FRACTIONNAIRE, et la méthode GLOUTONNE par rendement décroissant devient optimale. La raison est qu'on n'a plus jamais à refuser un objet faute de place : on en prend la fraction qui tient, si bien que la capacité est toujours remplie exactement, et qu'aucune heure n'est occupée par un chapitre de rendement inférieur à un autre encore disponible. Un échange local ne peut donc qu'aggraver le total, ce qui est la démonstration standard d'optimalité d'un glouton. Solution : on prend A, C et E, de rendement 2, soit 2+4+1=72 + 4 + 1 = 7 heures et 4+8+2=144 + 8 + 2 = 14 points ; il reste 5 heures. On prend ensuite F, de rendement 1,831{,}83, mais il demande 6 heures : on en prend les cinq sixièmes, ce qui donne 55 heures et 11×5/69,1711 \times 5 / 6 \approx 9{,}17 points. Le total vaut environ 14+9,17=23,1714 + 9{,}17 = 23{,}17 points, légèrement supérieur aux 23 du problème indivisible, ce qui est normal : relâcher une contrainte ne peut qu'améliorer l'optimum.

Partie C : les classiques (/50)

Exercice 11 : La distance d'édition

Un correcteur orthographique propose les mots du dictionnaire les plus proches du mot mal tapé. La distance d'édition entre deux mots est le nombre minimal d'opérations pour passer de l'un à l'autre, chaque opération coûtant 1 : insérer une lettre, supprimer une lettre, ou remplacer une lettre par une autre.

On note D(i,j)D(i, j) la distance entre les ii premières lettres du premier mot et les jj premières lettres du second. On compare CHAT et CHIEN.

  • a) Que valent D(0,j)D(0, j) et D(i,0)D(i, 0) ? Justifiez.
  • b) Écrivez la relation de récurrence qui donne D(i,j)D(i, j) à partir de D(i1,j)D(i-1, j), D(i,j1)D(i, j-1) et D(i1,j1)D(i-1, j-1), en interprétant chacun des trois cas.
  • c) Remplissez le tableau des D(i,j)D(i, j) pour CHAT, en lignes, et CHIEN, en colonnes. Donnez la dernière ligne.
  • d) Quelle est la distance d'édition entre CHAT et CHIEN ? Donnez une suite d'opérations qui la réalise.
  • e) Quel est le coût du calcul pour deux mots de longueurs mm et nn ? Pourquoi une fonction récursive directe, sans mémoire, serait-elle inutilisable ? Un correcteur compare un mot à 300 000300\ 000 mots de longueur moyenne 8 : estimez le nombre de cases calculées.

Tape tes réponses, la page te dit juste ou faux 0/8

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) D(0,j)=jD(0, j) = j, D(i,0)=iD(i, 0) = i
  • b) Supprimer, insérer, remplacer
  • c) Dernière ligne 4, 3, 2, 2, 2, 3
  • d) Distance 3
  • e) O(mn)O(m\,n) ; 2,4×107\approx 2{,}4 \times 10^{7} cases

a) D(0,j)=jD(0, j) = j : pour passer du mot vide aux jj premières lettres du second, il faut exactement jj insertions. De même D(i,0)=iD(i, 0) = i : passer des ii premières lettres au mot vide demande ii suppressions. Ce sont les cas de base, la première ligne et la première colonne du tableau.

b) D(i,j)=min(D(i1,j)+1, D(i,j1)+1, D(i1,j1)+δ)D(i, j) = \min\big(D(i-1, j) + 1,\ D(i, j-1) + 1,\ D(i-1, j-1) + \delta\big), où δ\delta vaut 0 si la ii-ième lettre du premier mot est égale à la jj-ième du second, et 1 sinon. Le premier terme correspond à SUPPRIMER la dernière lettre du premier mot, le deuxième à INSÉRER la dernière lettre du second, le troisième à faire correspondre les deux dernières lettres, gratuitement si elles sont égales, par un REMPLACEMENT sinon. Une solution optimale se termine forcément par l'un de ces trois cas, et l'on garde le meilleur : c'est le principe d'optimalité de l'exercice 5.

c) Première ligne, mot vide : 0, 1, 2, 3, 4, 5. Ligne C : 1, 0, 1, 2, 3, 4. Ligne H : 2, 1, 0, 1, 2, 3. Ligne A : 3, 2, 1, 1, 2, 3. Ligne T : 4, 3, 2, 2, 2, 3. Par exemple, la case de la ligne A et de la colonne I vaut min(0+1,1+1,0+1)=1\min(0 + 1, 1 + 1, 0 + 1) = 1 : on remplace A par I après avoir fait correspondre CH et CH.

d) La distance est la dernière case, D(4,5)=3D(4, 5) = 3. Une suite d'opérations : garder C et H ; remplacer A par I ; remplacer T par E ; insérer N. On obtient CHIEN en trois opérations, et le tableau prouve qu'on ne peut pas faire moins.

e) Le tableau compte (m+1)(n+1)(m + 1)(n + 1) cases, chacune calculée en temps constant : coût O(mn)O(m\,n). Une fonction récursive directe relancerait trois appels à chaque étape et recalculerait les mêmes sous-problèmes un nombre exponentiel de fois, exactement comme Fibonacci naïf. Pour le correcteur : environ 9×9=819 \times 9 = 81 cases par comparaison, donc 300 000×812,4×107300\ 000 \times 81 \approx 2{,}4 \times 10^{7} cases, soit une fraction de seconde. Les vrais correcteurs réduisent encore ce nombre en ne comparant que des mots de longueur voisine.

Exercice 12 : La multiplication de Karatsuba

Multiplier deux nombres de nn chiffres à la main demande de multiplier chaque chiffre de l'un par chaque chiffre de l'autre. En 1960, Karatsuba a trouvé mieux. On écrit x=10x1+x0x = 10\,x_{1} + x_{0} et y=10y1+y0y = 10\,y_{1} + y_{0}, puis on calcule a=x1y1a = x_{1} y_{1}, d=x0y0d = x_{0} y_{0} et e=(x1+x0)(y1+y0)e = (x_{1} + x_{0})(y_{1} + y_{0}). On a alors xy=100a+10(ead)+dx\,y = 100\,a + 10\,(e - a - d) + d.

  • a) Combien de multiplications de deux chiffres la méthode de l'école fait-elle pour deux nombres de 2 chiffres ? de nn chiffres ?
  • b) Calculez 47×8347 \times 83 par la méthode de Karatsuba : donnez aa, dd, ee, puis eade - a - d et le produit.
  • c) Démontrez la formule xy=100a+10(ead)+dx\,y = 100\,a + 10\,(e - a - d) + d.
  • d) Pour des nombres de nn chiffres, on coupe chaque nombre en deux moitiés de n/2n/2 chiffres et l'on applique la même idée récursivement. Écrivez la récurrence du nombre M(n)M(n) de multiplications élémentaires et résolvez-la pour n=2kn = 2^{k}. Calculez M(1 024)M(1\ 024) et comparez à la méthode de l'école.
  • e) Montrez que M(n)=nlog23M(n) = n^{\log_{2} 3} et donnez l'exposant au millième. Pourquoi les bibliothèques de calcul utilisent-elles tout de même la méthode de l'école pour les petits nombres ?

Tape tes réponses, la page te dit juste ou faux 0/10

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) École : n2n^{2} multiplications
  • b) 47×83=3 90147 \times 83 = 3\ 901 en 3 produits
  • c) eade - a - d : terme du milieu
  • d) M(1 024)=310=59 049M(1\ 024) = 3^{10} = 59\ 049
  • e) nlog23n1,585n^{\log_{2} 3} \approx n^{1{,}585}

a) Chacun des 2 chiffres du premier nombre est multiplié par chacun des 2 chiffres du second : 4 multiplications. Pour nn chiffres : n2n^{2} multiplications, un coût quadratique.

b) x1=4x_{1} = 4, x0=7x_{0} = 7, y1=8y_{1} = 8, y0=3y_{0} = 3. a=4×8=32a = 4 \times 8 = 32, d=7×3=21d = 7 \times 3 = 21, e=(4+7)(8+3)=11×11=121e = (4 + 7)(8 + 3) = 11 \times 11 = 121. Puis ead=1213221=68e - a - d = 121 - 32 - 21 = 68. Le produit vaut 100×32+10×68+21=3 200+680+21=3 901100 \times 32 + 10 \times 68 + 21 = 3\ 200 + 680 + 21 = 3\ 901, et l'on vérifie que 47×83=3 90147 \times 83 = 3\ 901. Trois multiplications au lieu de quatre.

c) xy=(10x1+x0)(10y1+y0)=100x1y1+10(x1y0+x0y1)+x0y0x\,y = (10\,x_{1} + x_{0})(10\,y_{1} + y_{0}) = 100\,x_{1} y_{1} + 10\,(x_{1} y_{0} + x_{0} y_{1}) + x_{0} y_{0}. Or e=(x1+x0)(y1+y0)=x1y1+x1y0+x0y1+x0y0=a+(x1y0+x0y1)+de = (x_{1} + x_{0})(y_{1} + y_{0}) = x_{1} y_{1} + x_{1} y_{0} + x_{0} y_{1} + x_{0} y_{0} = a + (x_{1} y_{0} + x_{0} y_{1}) + d, donc x1y0+x0y1=eadx_{1} y_{0} + x_{0} y_{1} = e - a - d. En remplaçant, xy=100a+10(ead)+dx\,y = 100\,a + 10\,(e - a - d) + d. Toute l'astuce est là : le terme du milieu, qui semblait exiger deux produits, s'obtient à partir d'un seul produit supplémentaire et de soustractions, beaucoup moins coûteuses.

d) Chaque multiplication de nombres de nn chiffres se ramène à 3 multiplications de nombres de n/2n/2 chiffres : M(n)=3M(n/2)M(n) = 3\,M(n/2), avec M(1)=1M(1) = 1. Pour n=2kn = 2^{k}, on déroule : M(2k)=3M(2k1)==3kM(2^{k}) = 3\,M(2^{k-1}) = \dots = 3^{k}. Pour n=1 024=210n = 1\ 024 = 2^{10} : M=310=59 049M = 3^{10} = 59\ 049, contre 1 0242=1 048 5761\ 024^{2} = 1\ 048\ 576 pour la méthode de l'école, soit environ 18 fois moins. On a négligé les additions, dont le coût est linéaire à chaque niveau.

e) 3k=(2log23)k=(2k)log23=nlog233^{k} = (2^{\log_{2} 3})^{k} = (2^{k})^{\log_{2} 3} = n^{\log_{2} 3}, avec log231,585\log_{2} 3 \approx 1{,}585. Le coût passe donc de n2n^{2} à environ n1,585n^{1{,}585}, un gain qui grandit sans limite avec nn. Pour les petits nombres, la méthode récursive paie des additions, des soustractions et des découpages supplémentaires, dont le coût constant dépasse le gain : les bibliothèques basculent donc sur la méthode de l'école en dessous d'un seuil de quelques dizaines de chiffres, comme le tri fusion bascule sur le tri par insertion pour les petits tableaux.

Exercice 13 : Colorier un graphe pour un calendrier d'examens

Un lycée doit placer six examens, mathématiques M, physique P, SVT S, histoire H, anglais A et philosophie F, sur des demi-journées. Deux examens qui ont des candidats communs ne peuvent pas avoir lieu en même temps. Les paires en conflit sont : M et P, M et S, P et S, P et A, S et H, H et A, A et F, H et F.

On modélise par un graphe dont les sommets sont les examens et les arêtes les conflits. Donner un créneau à chaque examen revient à COLORIER les sommets de sorte que deux sommets voisins aient des couleurs différentes. L'algorithme glouton traite les sommets dans un ordre donné et attribue à chacun la plus petite couleur, 1, 2, 3 et ainsi de suite, non utilisée par ses voisins déjà coloriés.

  • a) Combien le graphe a-t-il d'arêtes ? Donnez le degré de chaque sommet.
  • b) Appliquez l'algorithme glouton dans l'ordre M, P, S, H, A, F. Donnez la couleur de chaque examen et le nombre de créneaux utilisés.
  • c) Montrez qu'il est impossible de faire mieux sur ce graphe.
  • d) Appliquez l'algorithme glouton dans l'ordre M, P, S, A, F, H. Combien de créneaux obtient-on ? Que conclure sur l'algorithme ?
  • e) Montrez que l'algorithme glouton n'utilise jamais plus de Δ+1\Delta + 1 couleurs, où Δ\Delta est le degré maximal. Que vaut cette borne ici ? Que sait-on de la recherche du nombre minimal de couleurs dans un graphe quelconque ?

Tape tes réponses, la page te dit juste ou faux 0/10

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) 8 arêtes, degrés 2 à 3
  • b) Ordre M, P, S, H, A, F : 3 créneaux
  • c) Triangle : 3 couleurs nécessaires
  • d) Autre ordre : 4 créneaux
  • e) Au plus Δ+1=4\Delta + 1 = 4 couleurs

a) Le graphe a 8 arêtes, une par paire en conflit. Degrés : M 2, P 3, S 3, H 3, A 3, F 2. On vérifie que la somme des degrés, 16, vaut bien deux fois le nombre d'arêtes.

b) M reçoit 1. P, voisin de M, reçoit 2. S, voisin de M et P, reçoit 3. H, dont seul le voisin S est déjà colorié, reçoit 1. A, voisin de P de couleur 2 et de H de couleur 1, reçoit 3. F, voisin de A de couleur 3 et de H de couleur 1, reçoit 2. On utilise 3 créneaux : M et H ensemble, P et F ensemble, S et A ensemble.

c) M, P et S sont deux à deux en conflit : ils forment un TRIANGLE, et leurs trois couleurs doivent être différentes. Il faut donc au moins 3 couleurs, et la coloration de la question b en utilise 3 : elle est optimale.

d) M reçoit 1, P reçoit 2, S reçoit 3. A, dont seul P est colorié parmi ses voisins, reçoit 1. F, voisin de A, reçoit 2. H est voisin de S de couleur 3, de A de couleur 1 et de F de couleur 2 : il reçoit 4. On obtient 4 créneaux. L'algorithme glouton n'est donc PAS optimal : son résultat dépend de l'ordre de traitement des sommets, et un mauvais ordre peut coûter une couleur de plus sur le même graphe.

e) Quand l'algorithme traite un sommet, celui-ci a au plus Δ\Delta voisins, donc au plus Δ\Delta couleurs sont interdites : parmi les couleurs 1 à Δ+1\Delta + 1, l'une au moins est libre. Ici Δ=3\Delta = 3, donc au plus 4 couleurs, ce qui est exactement ce qu'a produit le mauvais ordre. Déterminer le nombre MINIMAL de couleurs d'un graphe quelconque est un problème difficile : on ne connaît aucun algorithme efficace dans tous les cas, et l'on se contente en pratique d'heuristiques gloutonnes, par exemple traiter d'abord les sommets de plus fort degré.

Exercice 14 : La pyramide de nombres

On descend une pyramide de nombres depuis le sommet jusqu'à la base, en passant à chaque étage à l'un des deux nombres situés juste en dessous, à gauche ou à droite. On veut maximiser la somme des nombres traversés.

La pyramide est rangée dans la liste t = [[4], [8, 5], [1, 3, 9], [6, 2, 7, 8]] : t[i][j] est le jj-ième nombre de l'étage ii, et depuis t[i][j] on peut descendre en t[i+1][j] ou en t[i+1][j+1].

  • a) Combien de chemins différents mènent du sommet à la base de cette pyramide de 4 étages ? D'une pyramide de nn étages ? Estimez ce nombre pour 100 étages.
  • b) La méthode gloutonne descend à chaque étage vers le plus grand des deux nombres. Quel chemin suit-elle, et quelle somme obtient-elle ?
  • c) On note S(i,j)S(i, j) la meilleure somme obtenue en partant de t[i][j]. Écrivez la relation de récurrence et le cas de base, puis calculez SS en remontant depuis la base, étage par étage.
  • d) Quelle est la somme maximale ? Reconstituez le chemin optimal et comparez au résultat glouton.
  • e) Combien de cases la méthode de la question c calcule-t-elle pour 100 étages ? Comparez à l'exploration de tous les chemins.

Tape tes réponses, la page te dit juste ou faux 0/9

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) 2n12^{n-1} chemins
  • b) Glouton : 22
  • c) S(i,j)=t[i][j]+maxS(i, j) = t[i][j] + \max
  • d) Optimum 26 par 4, 5, 9, 8
  • e) 5 0505\ 050 cases contre 6,3×10296{,}3 \times 10^{29} chemins

a) À chacun des 3 passages d'un étage au suivant, on a 2 choix : 23=82^{3} = 8 chemins. Pour nn étages, 2n12^{n-1} chemins. Pour 100 étages : 2996,3×10292^{99} \approx 6{,}3 \times 10^{29} chemins, bien au-delà de ce qu'aucun ordinateur pourra jamais parcourir.

b) Depuis 4, on choisit 8 plutôt que 5 ; depuis 8, on choisit 3 plutôt que 1 ; depuis 3, on choisit 7 plutôt que 2. Chemin 4, 8, 3, 7, de somme 22.

c) Cas de base, la dernière ligne : S(3,j)=t[3][j]S(3, j) = t[3][j]. Relation : S(i,j)=t[i][j]+max(S(i+1,j), S(i+1,j+1))S(i, j) = t[i][j] + \max\big(S(i+1, j),\ S(i+1, j+1)\big). Étage 3 : 6, 2, 7, 8. Étage 2 : 1+max(6,2)=71 + \max(6, 2) = 7 ; 3+max(2,7)=103 + \max(2, 7) = 10 ; 9+max(7,8)=179 + \max(7, 8) = 17. Étage 1 : 8+max(7,10)=188 + \max(7, 10) = 18 ; 5+max(10,17)=225 + \max(10, 17) = 22. Étage 0 : 4+max(18,22)=264 + \max(18, 22) = 26.

d) La somme maximale est S(0,0)=26S(0, 0) = 26. On reconstitue le chemin en suivant à chaque étage le fils qui a réalisé le maximum : de 4, on va vers 5, dont la valeur SS vaut 22 contre 18 ; de 5, vers 9, avec 17 contre 10 ; de 9, vers 8, avec 8 contre 7. Chemin 4, 5, 9, 8, de somme 26, contre 22 pour le glouton. Le glouton s'est laissé attirer par le 8 du deuxième étage, qui fermait l'accès au 9 et au 8 ; la programmation dynamique, en partant du bas, voit d'abord ce que chaque choix rend possible.

e) Chaque case de la pyramide est calculée une fois : 1+2++100=100×1012=5 0501 + 2 + \dots + 100 = \frac{100 \times 101}{2} = 5\ 050 cases, chacune par un maximum de deux termes. À comparer aux 6,3×10296{,}3 \times 10^{29} chemins : la programmation dynamique exploite le fait que les chemins partagent leurs fins, et qu'il suffit de connaître la meilleure fin depuis chaque case, au lieu de toutes.

Exercice 15 : Peut-on trier plus vite que n log n ?

Un tri par comparaison n'obtient d'information sur les données qu'en comparant deux éléments. On peut représenter ses exécutions par un ARBRE DE DÉCISION : chaque noeud est une comparaison, chaque branche une réponse, et chaque feuille un ordre final. Pour nn éléments distincts, l'arbre doit avoir au moins une feuille par ordre possible, soit n!n! feuilles.

  • a) Un arbre binaire de hauteur hh a au plus 2h2^{h} feuilles. Déduisez-en le nombre minimal de comparaisons, dans le pire cas, d'un tri par comparaison de nn éléments. Calculez ce minimum pour n=3n = 3 et pour n=10n = 10.
  • b) Montrez qu'un tri par insertion trie toujours 3 éléments en au plus 3 comparaisons. Que conclure pour n=3n = 3 ?
  • c) Pour n=106n = 10^{6}, on admet que log2(n!)1,85×107\log_{2}(n!) \approx 1{,}85 \times 10^{7}. Comparez à nlog2nn \log_{2} n. Que conclure sur le tri fusion ?
  • d) Un lycée doit trier 1 000 0001\ 000\ 000 de copies par note entière de 0 à 20. Le tri par dénombrement compte, dans un tableau de 21 cases, combien de copies ont chaque note, puis réécrit les notes dans l'ordre. Donnez son coût, et expliquez pourquoi il ne contredit pas la question a.
  • e) Appliquez le tri par dénombrement à la liste [3, 1, 4, 1, 5, 2, 3, 1], de valeurs entre 0 et 5 : donnez le tableau des effectifs et la liste triée. Dans quel cas ce tri devient-il inutilisable ?

Tape tes réponses, la page te dit juste ou faux 0/9

a)
b)
c)
d)
e)
Voir la correction

Réponses

  • a) hlog2(n!)h \geq \log_{2}(n!) : 3 et 22
  • b) Insertion optimale pour 3 éléments
  • c) Tri fusion optimal
  • d) Dénombrement linéaire, sans comparaison
  • e) Effectifs 0, 3, 1, 2, 1, 1

a) Un tri qui fait au plus hh comparaisons correspond à un arbre de hauteur au plus hh, donc à au plus 2h2^{h} feuilles. Il en faut au moins n!n!, donc 2hn!2^{h} \geq n!, soit hlog2(n!)h \geq \log_{2}(n!). Pour n=3n = 3 : 3!=63! = 6 et log262,58\log_{2} 6 \approx 2{,}58, donc au moins 3 comparaisons dans le pire cas. Pour n=10n = 10 : 10!=3 628 80010! = 3\ 628\ 800 et log2(10!)21,8\log_{2}(10!) \approx 21{,}8, donc au moins 22 comparaisons. Aucun algorithme, aussi astucieux soit-il, ne peut descendre en dessous.

b) Le tri par insertion place le deuxième élément en 1 comparaison, puis le troisième en au plus 2 comparaisons, face au plus grand puis au plus petit des deux premiers : au plus 1+2=31 + 2 = 3 comparaisons. Il atteint la borne de la question a : pour 3 éléments, il est OPTIMAL dans le pire cas.

c) nlog2n=106×log2(106)106×19,931,99×107n \log_{2} n = 10^{6} \times \log_{2}(10^{6}) \approx 10^{6} \times 19{,}93 \approx 1{,}99 \times 10^{7}, du même ordre que la borne 1,85×1071{,}85 \times 10^{7}. Plus généralement, log2(n!)\log_{2}(n!) est de l'ordre de nlog2nn \log_{2} n : aucun tri par comparaison ne fait mieux qu'un nombre de comparaisons de cet ordre, et le tri fusion, qui l'atteint dans tous les cas, est optimal à un facteur constant près.

d) Un premier parcours des copies remplit les 21 compteurs, un second passage réécrit les notes : environ 1 000 000+211\ 000\ 000 + 21 opérations, un coût LINÉAIRE, vingt fois moins que nlog2n2×107n \log_{2} n \approx 2 \times 10^{7}. Il ne contredit pas la borne, parce qu'il ne COMPARE jamais deux copies : il utilise la valeur de la note comme indice de tableau, ce qui suppose que les valeurs sont des entiers dans un petit intervalle connu. La borne ne concerne que les tris qui n'apprennent rien d'autre que le résultat de comparaisons.

e) Effectifs des valeurs 0 à 5 : 0, 3, 1, 2, 1, 1. On réécrit trois fois 1, une fois 2, deux fois 3, une fois 4 et une fois 5 : [1, 1, 1, 2, 3, 3, 4, 5]. Le tri devient inutilisable quand l'intervalle des valeurs est immense devant le nombre d'éléments : trier mille nombres compris entre 0 et un milliard exigerait un tableau d'un milliard de compteurs, pour mille valeurs seulement.

Chapitre précédent Récursivité, objet, mise au point et calculabilité

Voir aussi

Vous cherchez un tuteur en NSI à Montréal ?

Contactez-moi pour une première séance. C'est le chapitre le plus rentable de la Terminale NSI : les mêmes quatre schémas de raisonnement reviennent d'un sujet à l'autre, et une fois qu'on les reconnaît, les questions se ressemblent toutes.

Site par Studio Squalli