EXERCICES SUR L’ALGORITHMIQUE ÉLÉMENTAIRE Chapitre 2 Exercice 1 Soient A [1..m] et B [1..n] deux tableaux d’entiers. Les éléments de A sont tous distincts et les éléments de B sont tous distincts, mais il peut y avoir des éléments communs entre A et B. Esquisser quatre algorithmes efficaces en pire cas, deux se basant sur des techniques de tri et deux sur des techniques de fouille, pour décider si chaque élément du tableau A se trouve quelque part dans le tableau B. En considérant que les tableaux représentent des ensembles, vos algorithmes doivent donc décider si A ⊆ B. Donnez les temps d’exécution de vos algorithmes en pire cas, simplifiés au maximum, en fonction de m et de n (m ≤ n). Solution a) Premier algorithme basé sur une technique de tri. 1˚ Trier les éléments du tableau B [1..n] O (n log n) 2˚ Rechercher dans B chaque élément de A en utilisant une recherche dichotomique. O (m log n) Total: O (n log n). Second algorithme basé sur une technique de tri 1˚ Transférer les éléments de A et de B dans un tableau C de longueur m+n. O (m+n) (ou O(n) si on concatène B, de taille n, à A, inchangé) 2˚ Trier les éléments du tableau C O ((m+n) log (m+n)) 3˚ Calculer le nombre d’éléments de C répétés 2 fois; si cette valeur est égale à m alors A ⊆ B. O (m+n) Total: O ((m+n) log (m+n)) (ou O (n log n) si m << n) b) Premier algorithme basé sur une technique de fouille 1˚) Construire un arbre AVL à partir du tableau B O (n log n) 2˚) Rechercher dans B (dans l’arbre AVL) chaque élément de A. O (m log n) Total: Analyse d’algorithmes 2001 O (n log n) car m < n EXERCICES SUR L’ALGORITHMIQUE ÉLÉMENTAIRE 1 Second algorithme basé sur une technique de fouille (utilise des résultats du chapitre 5) 1˚ Construire les monceaux A et B. O (n) 2˚ TANT QUE non terminé FAIRE SI le premier élément de A est égal au premier élément de B ALORS Enlever le premier élément de A. Enlever le premier élément de B. SI A est vide ALORS Retourner {A ⊆ B} terminé. SINON SI B est vide ALORS Retourner {A ⊄ B} terminé. {SINON A ≠ Ø ≠ B: on boucle}. SINON SI le premier élément de A est supérieur au premier élément de B ALORS Retourner {A ⊄ B} terminé. SINON {le premier élément de A est inférieur au premier élément de B} Enlever le premier élément de B. SI B est vide ALORS Retourner {A ⊄ B} terminé. {SINON A ≠ Ø ≠ B: on boucle}. FIN TANT QUE. O (n log n) Total: O (n log n) car m < n Exercice 2 Étant donnée une liste K de n clefs dans le désordre, vous devez produire une liste L des clefs apparaissant en plus d’un exemplaire dans K. L’ordre des clefs dans la liste L est laissée à votre entière discrétion, mais vous devez y éviter les répétitions. Par exemple, étant donnée la liste 12 8 2 9 3 6 8 vous pouvez produire indifféremment 8 2 12 4 ou mais pas 8 2 12 2 4 8 9 12 8 2 12 4 2 4 2 12 4 2 13 2 4 8 12 (4 se répète dans la liste originale) (9 ne se répète pas dans la liste originale) (2 est répété dans la liste produite) Donnez deux algorithmes efficaces différents pour résoudre ce problème, l’un basé sur des techniques de fouille (hachage) et l’autre basé sur des techniques de tri. Vous pouvez supposer que des procédures de fouille et de tri sont déjà disponibles. Analysez le temps d’exécution de vos algorithmes en moyenne et en pire cas. Exercice 3 En étudiant le problème de la multiplication de grands entiers, nous avons montré que l’algorithme de multiplication à la russe, si l’on considère les opérandes comme de grands entiers, s’exécute en un temps dans l’ordre de (m+p) p bits (ou mots-machine). Précisez cela en détaillant ce qui se passe à chaque itération. Quelle différence cela entraîne-t-il selon que le multiplicateur est le plus petit ou le plus grand des deux opérandes? Comparez ces résultats à l’efficacité des deux autres algorithmes de multiplication de grands entiers. 2 EXERCICES SUR L’ALGORITHMIQUE ÉLÉMENTAIRE ©2001 R. Lelouche Solution Rappelons que, comme on travaille avec de grands entiers, on ne peut pas considérer une addition ou une multiplication comme une opération élémentaire. Concernant les multiplications ou divisions par deux, même si elles devaient se traduire par des décalages, ceux-ci devraient être propagés de mot en mot, et donc prendre un temps linéaire par rapport à la taille (nombre de mots, de chiffres, ou de bits) des grands entiers en question, comme toute autre addition ou multiplication. La seule chose favorisant ces «décalages étendus» est que la constante multiplicative (temps par motmachine) a des chances d’être plus faible pour les décalages que pour les additions (et plus faible pour les additions que pour les multiplications quelconques d’un mot ou chiffre par un autre). Dans le raisonnement qui suit, nous exprimons la taille en bits, plus faciles à appréhender, plutôt qu’en mots, d’autant plus que c’est ainsi qu’a été présenté l’algorithme. La ie itération (remplissage de la ie ligne du tableau), dans la colonne du multiplicande A, calcule un produit partiel double de celui calculé à la ligne précédente, donc ayant un bit de plus. Comme au départ le multiplicande comporte m bits, cette ie itération calcule un produit partiel de m + i bits (ou mots). Elle prend par conséquent un temps dans l’ordre de m + i. Puisque le multiplicateur B, qui diminue d’un bit à chaque itération, comprend au départ p bits ou mots, il y a p itérations au total. La succession de ces p itérations prend donc un temps T (m, p) = m + (m + 1) + (m + 2) + ... + (m + p – 1) p–1 =mp+ p ≈ p (m + 2 ) ∑ i = m p + p (p–1) 2 (1) i=1 Si on prend comme multiplicateur B le plus petit des deux opérandes (p < m), le terme dominant de la parenthèse est m, d’où un temps global dans l’ordre de p m. C’est dans le même ordre que pour l’algorithme classique, mais avec une constante multiplicative risquant d’être plus grande (p. ex., si B est le multiplicateur, il y a lgB itérations dans l’algorithme à la russe au lieu de log 10B itérations dans l’algorithme classique, soit lg10 fois plus). Si on prend comme multiplicateur B le plus grand des deux opérandes (p > m), le terme p dominant de la parenthèse est 2 , d’où un temps global dans l’ordre de p2. Cela est plus mauvais que pour l’algorithme classique, puisque l’algorithme est alors quadratique par rapport à la taille du plus grand opérande (au lieu d’être le produit des deux tailles)! Remarque. Dans le raisonnement qui précède, on s’est concentré sur le calcul des produits partiels successifs (colonne du multiplicande A). Alternativement, on pourrait prendre en compte aussi le temps de calcul des multiplicateurs successifs. Cela serait tout-à-fait justifié, puisque ces multiplicateurs successifs sont aussi de grands entiers, bien que leur taille diminue de p à 1. Dans ce cas, à la sommation (1) ci-dessus des produits partiels s’ajouterait une sommation p–1 T1 (p) = (p – 1) + (p – 2) + ... + 1 = p ≈ p (2 ) ∑ i = p (p–1) 2 (2) i=1 due aux multiplicateurs successifs qui vont en diminuant. La prise en compte de cette seconde comp posante aurait comme seul effet, en bout de ligne, de remplacer dans (1) la parenthèse (m + ) par 2 (m + p), et les conclusions ci-dessus (selon les tailles respectives de A et B) subsisteraient. Analyse d’algorithmes 2001 EXERCICES SUR L’ALGORITHMIQUE ÉLÉMENTAIRE 3