Université Paris Dauphine DE MI2E - 1re année Algorithmique et Programmation 2 2014-2015 CORRIGE T.D. 2 Exercice 1: Recherche dans un tableau Soit T un tableau de n cellules contenant des valeurs numériques (dont certaines éventuellement identiques) et soit v une valeur numérique. 1. Ecrire un programme python qui renvoie le plus petit indice i tel que T [i] = v ou 0 s’il n’en existe pas (le tableau est parcouru par indice croissant). 2. Donner la complexité de ce programme en fonction de n. 1. def plusPetitIndice (T, v): for i in range (len (T)): if T [i] == v: return i return -1 T = [1, 5, 0, 13, 3, 5, 7, 9] print (plusPetitIndice (T, 13)) print (plusPetitIndice (T, 12)) En algorithmique : PlusPetitIndice(T, n, v) : entier Entrées : T un tableau, n sa dimension, v une valeur Sorties : valeur du plus petit indice contenant v ou 0 s’il n’en existe pas Données : i:entier pour i <- 1 à n faire si v = T[i] alors retourner i finsi finpour retourner 0 2. Au pire cas : Time(n) ≤ (1 + 1)n + 1 = 2n + 1 d’où Time(n)∈ O(n). Exercice 2: Recherche du plus grand indice Soit T un tableau de n cellules contenant des valeurs numériques (dont certaines éventuellement identiques) et soit v une valeur numérique. 1. Ecrire un programme python qui renvoie le plus grand indice i tel que T [i] = v ou 0 s’il n’en existe pas (le tableau est parcouru par indice croissant). 2. Donner la complexité de ce programme en fonction de n. 3. Quel est l’inconvénient de ce programme par rapport au précédent ? 4. La commande "pour indice ← valeur initiale à valeur finale faire" où valeur finale est inférieure ou égale à valeur initiale réalise des boucles dont l’indice est décroissant. Utiliser cette commande pour proposer un algorithme plus efficace "symétrique" de l’algorithme de la question 1. 1. def plusGrandIndice (T, v): indice = -1 for i in range (len (T)): if T [i] == v: indice = i return indice T = [1, 5, 0, 13, 3, 5, 7, 9] print (plusGrandIndice (T, 5)) print (plusGrandIndice (T, 12)) en algorithmique : PlusGrandIndice(T, n, v) : entier Entrées : T un tableau, n sa dimension, v une valeur Sorties : valeur du plus grand indice contenant v ou 0 s’il n’en existe pas Données : i, indice: entiers indice <- 0 pour i <- 1 à n faire si v = T[i] alors indice <- i finsi finpour retourner indice 2. Au pire cas : Time(n) ≤ 1 + (1 + 1 + 1)n + 1 = 3n + 2 d’où Time(n)∈ O(n). 3. Nous sommes obligés de parcourir tout le tableau. 4. def plusGrandIndiceDecroissant (T, v): i = len (T) - 1 while (i > -1): if T [i] == v: return i i = i - 1 return -1 T = [1, 5, 0, 13, 3, 5, 7, 9] print (plusGrandIndiceDecroissant (T, 5)) print (plusGrandIndiceDecroissant (T, 12)) en algorithmique : PlusGrandIndice2(T, n, v) : entier Entrées : T un tableau, n sa dimension, v une valeur Sorties : valeur du plus grand indice contenant v ou 0 s’il n’en existe pas Données : i:entier pour i <- n à 1 faire si v = T[i] alors retourner i finsi finpour retourner 0 Exercice 3: Recherche dans un tableau trié On suppose maintenant que le tableau T est trié. 1. Ecrire un programme python qui renvoie un indice i (quelconque) tel que T [i] = v ou -1 s’il n’en existe pas. Le principe du programme est le suivant : (a) Il calcule milieu = (n + 1)/2 et il compare v avec T [milieu]. S’il y a égalité, il renvoie milieu. (b) Sinon si v < T [milieu] et milieu > 1, il s’appelle récursivement avec pour entrée la partie de T allant de 0 à milieu − 1 et renvoie le résultat de cet appel. (c) Sinon si v > T [milieu] et milieu < n, il s’appelle récursivement avec pour entrée la partie de T allant de milieu + 1 à n − 1 et renvoie le résultat de cet appel. (d) Sinon il renvoie -1 (i.e., pas trouvé). 2. Modifier l’algorithme pour qu’il renvoie le plus petit indice comme dans la question 1. 1. def chercheTableauTrie (T, v, deb, fin): milieu = (deb+fin)/2 if T [milieu] == v: return milieu elif (v > T [milieu] and milieu < len (T) - 1): return chercheTableauTrie (T, v, milieu+1, fin) elif (v < T [milieu] and milieu > 0): return chercheTableauTrie (T, v, deb, milieu-1) else: return -1 T = [1, 2, 3, 4, 5, 6, 7, 8, print(chercheTableauTrie (T, print(chercheTableauTrie (T, print(chercheTableauTrie (T, 9, 10] 7, 0, len (T) - 1)) 10, 0, len (T) - 1)) 12, 0, len (T) - 1)) en algorithmique : Cherche_tableau_trie(T, v, deb, fin) : entier Entrées : T un tableau trié, v une valeur, deb une borne inf, fin une borne sup Sorties : valeur d’un indice contenant v ou 0 s’il n’en existe pas Données : milieu: entier milieu <- (deb+fin)/2 si T[milieu] = v alors retourner milieu sinon si v > T[milieu] et milieu < n alors retourner Cherche_tableau_trie(T, v, milieu+1, fin) sinon si v < T[milieu] et milieu > 1 alors retourner Cherche_tableau_trie(T, v, deb, milieu-1) sinon retourner 0 finsi 2. def chercheTableauTriePPI (T, v, deb, fin): milieu = (deb + fin)/2 print (milieu, deb, fin) if T [milieu] == v: while T [milieu] == v and milieu > -1: milieu = milieu - 1 return milieu + 1 elif (v > T [milieu] and milieu < len (T) - 1): return chercheTableauTriePPI (T, v, milieu+1, fin) elif (v < T [milieu] and milieu > 0): return chercheTableauTriePPI (T, v, deb, milieu-1) else: return -1 T = [1, 2, 3, 4, 5, 6, 7, 7, 7, print(chercheTableauTriePPI (T, print(chercheTableauTriePPI (T, print(chercheTableauTriePPI (T, 7, 7, 8, 9, 10] 7, 0, len (T) - 1)) 10, 0, len (T) - 1)) 12, 0, len (T) - 1)) Exercice 4: Complexité de la recherche dans un tableau trié On étudie la complexité du dernier algorithme. 1. Démontrer que lorsque l’algorithme s’appelle récursivement, la taille du nouveau tableau est inférieure ou égale à n/2. 2. Soit T ime(n), le pire temps d’exécution de cet algorithme pour un tableau de taille inférieure ou égale à n. En déduire que pour une constante c bien choisie : T ime(1) ≤ c ∀n ≥ 2, T ime(n) ≤ c + T ime(n/2) 3. Démontrer par récurrence que T ime(n) ≤ c(log2 (n)+1) et donc que la complexité de l’algorithme est en O(log2 (n)). 1. Taille du tableau gauche : milieu − 1 = Taille du tableau droit : n − milieu = n n+1 2 − − n+1 2 1= = n−1 n 2 ≤ 2 n−1 n 2 ≤ 2 2. Pour n = 1, Time(n) ≤ 13. Au pire cas, on exécute les 13 opérations suivantes : 1er appel: 1 - milieu <- (deb+fin)/2 2 - si T[milieu] = v alors 3 - si v > T[milieu] et 4 - milieu < n alors 5 - sinon si v < T[milieu] et 6 - milieu > 1 alors 2ème appel: 1 - milieu <- (deb+fin)/2 2 - si T[milieu] = v alors 3 - si v > T[milieu] et 4 - milieu < n alors 5 - sinon si v < T[milieu] et 6 - milieu > 1 alors 7 - retourner 0 Pour n ≥ 2, Time(n) ≤ 13+ Time( n2 ). Au pire cas, on effectue les opérations suivantes : 1 2 3 4 5 6 7 - milieu <- (deb+fin)/2 si T[milieu] = v alors si v > T[milieu] et milieu < n alors sinon si v < T[milieu] et milieu > 1 alors retourner Cherche_tableau_trie_PPI(T, v, deb, milieu-1) auquel s’ajoute le temps de calcul d’un appel récursif sur un tableau de taille n 2 (cf. question 1). 3. Par récurrence forte : • Pour n = 1, Time(1) ≤ c(log2 (1) + 1) = c • Supposons que pour tout i ≤ n, Time(i) ≤ c(log2 (i) + 1) et montrons que Time(n + 1) ≤ c(log2 (n + 1) + 1): On a Time(n + 1) ≤ c+Time( n+1 2 ) (cf. question 2). Comme utiliser l’hypothèse de récurrence : Time(n + 1) n+1 2 ≤ n (pour n > 0), on peut ≤ c + c(log2 ( n+1 2 ) + 1) ) + 2) = c(log2 ( n+1 2 = c(log2 (n + 1) − log2 (2) + 2) = c(log2 (n + 1) − 1 + 2) = c(log2 (n + 1) + 1) Exercice 5: Algorithme itératif de recherche Algorithme 1 : Un algorithme itératif de recherche par partition. Cherche(T, n, v) : entier Entrées : T un tableau trié, n sa dimension, v une valeur Sorties : l’indice d’une cellule de tableau contenant v ou 0 s’il n’en existe pas Données : bas, haut: entiers bas ← 1 haut ← n tant que bas ≤ haut faire milieu ← (bas + haut)/2 si v = T [milieu] alors retourner milieu sinon si v < T [milieu] alors haut ← milieu − 1 sinon bas ← milieu + 1 finsi fin tant que retourner 0 1. Expliquer comment l’algorithme 1 procède pour obtenir un fonctionnement similaire au premier programme de la question 3. En quoi est-il plus efficace? 2. Programmer en python l’algorithme 1. 3. Modifier le programme pour qu’il renvoie le plus petit indice comme dans la question 1. 4. Modifier le programme de la question précédente pour qu’il renvoie le nombre d’indices i tels que T [i] = v. 1. La version itérative utilise deux variables bas et haut et une boucle tant que. Le coût en espace mémoire est constant, pas de risque de saturation de la pile d’exécution. 2. def chercheTableauTriePPIIteratif (T, v): temp = 0 bas = 1 haut = len (T) - 1 while bas <= haut: milieu = (bas + haut) / 2 if v == T [milieu]: temp = milieu haut = milieu - 1 else: if v < T [milieu]: haut = milieu - 1 else: bas = milieu + 1 return temp T = [1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9, 10] print(chercheTableauTriePPIIteratif (T, 7)) print(chercheTableauTriePPIIteratif (T, 10)) print(chercheTableauTriePPIIteratif (T, 12)) en algorithmique : CherchePPI_it(T, n, v) : entier Entrées : T un tableau trié, n sa dimension, v une valeur Sorties : valeur du plus petit indice contenant v ou 0 s’il n’en existe pas Données : temp, bas, haut : entiers temp <- 0 bas <- 1 haut <- n tant que bas <= haut faire milieu <- (bas + haut)/2 si v = T [milieu] alors temp <- milieu haut <- milieu - 1 sinon si v < T [milieu] alors haut <- milieu - 1 sinon bas <- milieu + 1 finsi finsi fin tant que retourner temp 3. def nombreIndices (T, v): count = 0 bool = True bas = 1 haut = len (T) - 1 while bas <= haut and bool == True: milieu = (bas + haut) / 2 if v == T [milieu]: bool = False count = count + 1 for i in range (bas, milieu): if v == T [i]: count = count + 1 for i in range (milieu + 1, haut + 1): if v == T [i]: count = count + 1 else: if v < T [milieu]: haut = milieu - 1 else: bas = milieu + 1 return count T = [1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9, 10] print(nombreIndices (T, 7)) print(nombreIndices (T, 10)) print(nombreIndices (T, 12)) en algorithmique : NombreIndices(T, n, v) : entier Entrées : T un tableau trié, bas et haut deux bornes, v une valeur Sorties : le nombre d’indices i tels que T[i] = v. Données : count : entiers count <- 0 bool <- vrai bas <- 1 haut <- n tant que bas <= haut et bool = vrai faire milieu <- (bas + haut)/2 si v = T [milieu] alors bool <- faux count <- count + 1 pour i = bas a milieu-1 faire si v = T[i] alors count <- count + 1 finsi finpour pour i = milieu+1 a haut faire si v = T[i] alors count <- count + 1 finsi finpour sinon si v < T [milieu] alors haut <- milieu - 1 sinon bas <- milieu + 1 finsi finsi fin tant que retourner count