une liste l d`entiers 1, 2 - Dr Philippe J. Giabbanelli

publicité
Annales en Algorithmique
1) Séquence avec répétitions
Données : un ensemble d’objets S = {x1, x2, …, xn}, et un entier naturel k tel que k ≤ n.
Exercice : Ecrire un algorithme abstrait (une fonction SAR) qui retourne le nombre de séquences avec
répétition sur S de longueur k. Exemple : S = {1, 2, 3}, k = 2 et 11 12 13 22 21 23 33 32 31 ; 9 séquences.
Contraintes : L’algorithme doit être en O(log n) dans le pire des cas, et constant en espace.
Solution :
Avec un petit peu de connaissances en mathématiques discrètes, on voit qu’il y a nk séquences avec répétition de
longueur k parmi n nombres. Le but est donc de calculer nk en O(log n) : dichotomie !
1. k ≠ 1 ^ k ≡ 0 (2)
→ SAR(n, k) = SAR(n*n, k/2)
2. k ≠ 1 ^ k ≡ 0 (2)
→ SAR(n, k) = n * SAR(n*n, (k-1)/2)
3. k = 1
→ SAR(n, k) = n
0
En effet, n = 1. Si k est pair, alors on fait (n²)k/2 et sinon n.(n²) k/2. On met (k-1)/2 car notre division est en fait floor.
2) Un diviseur pour tous les éléments
Données : un ensemble S d’entiers naturels > 1.
Exercice : On dit que S est safe s’il existe x ≥ 2 tel que x divise tout élément de S. Ecrire cet algorithme.
Solution :
On sait que l’on devra faire un test sur chaque élément. L’algorithme sera donc déjà en Θ(n), où n = | S |.
La question est de savoir si tous les éléments sont multiples d’un même nombre. Autrement, dit deux éléments
consécutifs doivent toujours être multiples d’un même nombre. On utilise le pgcd !
Initialisation : safe(x.L) = safe(L, x)
1. y ≠ 1 →
safe(x.L, y) = safe(L, pgcd(x, y))
2. y = 1 →
safe(x.L, y) = false
3.
safe(Ø, y) = true
Trace de l’algorithme sur 50 125 15 5 30 :
- safe({125, 15, 5, 30}, 50)
- pgcd(125, 50) = 25 ≠ 1 ; safe({15, 5, 30}, pgcd(125, 25))
- pgcd(125, 25) = 5 ≠ 1 ; safe({5, 30}, pgcd(15, 5))
- pgcd(15, 5) = 5 ≠ 1 ; safe({30}, pgcd(5, 5))
- pgcd(5, 5) = 5 ≠ 1 ; safe({}, pgcd(30, 5))
- Ø : true
3) Deuxième plus grand diviseur
Etant donné un entier naturel n > 1, on veut son deuxième plus grand diviseur. Pour 28 il s’agit de 14.
Solution :
Maximiser un diviseur revient à diviser le quotient. On sait que ce quotient ne peut excéder √n, d’où l’algorithme :
Initialisation : DPGD(n, i) = DPGD(n, 2)
1. i ≤ √n ^ n ≡ 0 [i] → DPGD(n, i) = n/i
2. i ≤ √n ^ n ≡ 0 [i] → DPGD(n, i) = DPGD(n, i+1)
3. i > √n → DPGD(n, i) = 1
Complexité en temps : O(√n). Complexité en espace : récursif terminal, donc sans consommation de pile, d’où O(1).
Philippe Giabbanelli et Julien Rosati
- 39 -
4) Nombre d’éléments supérieurs et inférieurs en tableau trié
Données : On considère un tableau trié d’entiers distincts L[1… n], et un entier x.
Exercice : L’algorithme retourne le couple (a, b) où a est le nombre d’entiers inférieurs ou égaux à x, et b
le nombre d’entiers strictement plus grands que x.
Solution :
Ce tableau est trié et on cherche simplement l’indice de l’élément. On fait une recherche par dichotomie pour
trouver l’indice i. L’indice a de l’énoncé est donc égal à la position où on a trouvé l’élément. L’indice b est égal à ce
qu’il reste entre cette position et la fin n du tableau.
En complexité, la recherche par dichotomie coûte O(log n) en temps et O(1) en espace (pas de pile).
Initialisation : LG(L, 1, n, x)
1. g ≤ d ^ L[(g+d)/2] ≥ x
→ LG(L, g, d, x) = LG(L, (g+d)/2 + 1, d, x)
2. g ≤ d ^ L[(g+d)/2] < x
→ LG(L, g, d, x) = LG(L, g, (g+d)/2 – 1, x)
3. g > d
→ LG(L, g, d, x) = (g – 1, n – g + 1)
Cette dichotomie recherche la borne supérieure. A la fin, pour avoir l’indice a on doit donc descendre de 1. Pour
avoir le reste, on fait (n – a) = (n – (g – 1)) = (n – g + 1).
5) Nombres heureux
Données : une liste L d’entiers 1, 2, …, n.
Exercice : A partir de L, chaque second nombre est supprimé pour produire une nouvelle liste L2. On
supprime ensuite chaque 3ème nombre de la liste L2, puis chaque 4ème nombre de la liste résultante, etc.
Les nombres qui restent après ce processus sont appelés les nombres heureux. Trouver ces nombres.
Exemple : LUC({1, 2, 3, 4, 5, 6, 7, 8, 9}) = {1, 3, 7}
En supprimant les seconds il reste {1, 3, 5, 7, 9}.
En supprimant les troisièmes il reste {1, 3, 7, 9}.
En supprimant les quatrièmes il reste {1, 3, 7}. On ne peut pas supprimer de cinquième, fin.
Solution :
On ne voit pas d’autre solution que d’implémenter tel quel l’énoncé de l’exercice est de rendre son résultat.
Initialisation : LUC(L, K) = LUC(L, 2)
} K est l’intervalle entre les nombres à supprimer
1. K ≤ |L| → LUC(L, K) = LUC(sup(L, K, 1), K+1)
} Soit la liste où on a fait la suppression, on passe à K+1
2. K > |L|→ LUC(L, K) = L
} Si on ne peut plus supprimer, on rend la liste résultante
Complexité en temps de l’algorithme :
i = k → sup(c.L, K, i) = sup(L, K , 1)
Tsup(n) = Tsup(n-1) + O(1) = O(n).
i ≠ k → sup(c.L, K, i) = c.sup(L, K , i+1)
Donc T(n) = T(n-1) + Tsup = T(n-1) + O(n) = O(n²)
sup(Ø, K, i) = Ø
Pour l’espace, pas besoin de pile, on est en O(1).
6) Quantité de parties à r éléments d’un ensemble S
Données : Un large ensemble S = {x1, x2, …, xn} d’objets.
Exercice : On veut connaître le nombre de sous-ensembles de S de cardinalité r, avec r ≤ |S|.
Solution :
C’est de la combinatoire : on cherche le nombre de façon de prendre n éléments parmi r. La réponse est le coefficient binomial Cr|S|.
Pour le calcul, on peut utiliser le fait que Cr|S| = Cr-1|S|-1 + Cr|S|-1, mais c’est assez lent (on peut optimiser en programmation dynamique
mais au prix d’un sacrifice en complexité spatiale). D’autres façons existent (avec un vecteur et le triangle de Pascal). Donnons la 1ère :
Initialisation : NBS(S, r) = combi(|S|, r)
1. p = 0 v p = n → combi(n, p) = 1
2. p ≠ 0 ^ p ≠ n → combi(p, n) = combi(n-1, p-1) + combi(n-1, p)
Philippe Giabbanelli et Julien Rosati
- 40 -
7) Premier élément commun à trois tableaux triés
Données : On a trois listes de taille potentiellement infinies contenant dans l’ordre alphabétique les noms
des employés à IBM, Sophia Antipolis et Valrose. On sait qu’au moins une personne est sur les trois listes.
Exercice : On veut le première personne commune à ces trois listes.
Solution :
On peut penser à la méthode fusion d’un mergeSort. Ici, on parcourt simplement les trois listes en parallèle et on regarde à chaque
fois si on a notre élément. Si ce n’est pas notre élément, on regarde ce qu’il nous faut éliminer (i.e. la façon dont on va parcourir).
1.
2.
3.
4.
c1 = c2 = c3 → TC(c1.L1, c2.L2, c3.L3) = c1
c1 < c2 ^ c1 < c3 → TC(c1.L1, c2.L2, c3.L3) = TC(L1, c2.L2, c3.L3)
c2 < c1 ^ c2 < c3 → TC(c1.L1, c2.L2, c3.L3) = TC(c1.L1, L2, c3.L3)
c3 < c1 ^ c3 < c2 → TC(c1.L1, c2.L2, c3.L3) = TC(c1.L1, c2.L2, L3)
Dans le pire des cas, la personne que l’on cherche est à la fin d’une des trois listes. Si on note n la taille de la plus petite des trois
listes, alors l’algorithme est en O(n). Il n’y a pas création de variable, simplement des parcours : on est constant en espace, O(1).
8) Partitionner un ensemble en deux sous-ensembles de sommes égales
Données : Un ensemble S = {x1, x2, …, xn} d’entiers naturels positifs.
Exercice : Partitionner l’ensemble en deux sous-ensembles de somme égale si c’est possible, ou
déterminer si c’est impossible. Exemple : {2, 12, 1, 8, 7, 5, 3} → {2, 15, 5} ; {1, 8, 7, 3}.
Solution :
On veut S1 et S2 tels que somme(S1) + somme(S2) = somme(S). Comme les sommes doivent être égales, on obtient
2somme(S1) = somme(S). Autrement dit, si la somme des éléments de S est impaire, alors il n’y a pas de solution.
Si la somme n’est pas impaire, alors on cherche un ensemble S1 dont la somme soit la moitié de celle de S ; son
complémentaire dans S sera S2.
Initialisation : PART(S, n) = PART(S, somme(S)
1. n ≡ 0 [mod 2] → PART(S, n) = SE(S, n/2)
2. n ≡ 0 [mod 2] → PART(S, n) = Ø
Ce problème relève de l’optimisation combinatoire ; c’est un cas où on cherche à minimiser la différence entre la
somme de deux sous-ensembles. On considère que, sans approximation, l’algorithme est exponentiel.
On peut essayer d’affiner les cas où il n’y a pas de solution. Par exemple, en triant l’ensemble, s’il y a des éléments
qui sont strictement supérieurs à n/2, alors ils ne pourront être pris dans aucun sous-ensemble : sans solution.
9) Trouver un élément qui n’appartient pas à un ensemble
Données : Un ensemble très large S = {x1, x2, …, xn} de réels.
Exercice : On veut un réel qui n’appartient pas à S. Exemple S = {-2, 12.5, -8.8, -5, 1.5} on peut avoir 4.5.
Solution :
Le problème est en réalité extrêmement simple. Considérons que S contient les réels de l’intervalle [a, b]. Plutôt
que de chercher un réel du même intervalle, mais qui n’est pas contenu dans S (c.f. l’exemple), on peut se
contenter de sortir de l’intervalle. Autrement dit, puisque les éléments de S sont dans [min(S), max(S)] il suffit de
prendre quelque chose de plus petit que le minimum, ou de plus grand que le maximum. Faisons avec le maximum.
Initialisation : NDS(x, c.L) = NDS(c, L)
1. c ≤ x → NDS(x, c.L) = NDS(x, L)
2. c > x → NDS(x, c.L) = NDS(c, L)
3. L = Ø → NDS(x, c.L) = x+1
(complexité en temps : O(|L|) ; complexité en espace : l’algorithme est récursif terminal, pas de consommation de pile, donc du O(1) ).
Philippe Giabbanelli et Julien Rosati
- 41 -
10) Nombre de régions déterminées par n cercles
Données : On a n cercles qui s’intersectent deux à deux, tels que deux cercles quelconques ne sont pas
tangents et trois quelconques n’ont pas d’intersection commune (i.e. l’intersection est stricte 2 à 2).
Exercice : On veut le nombre de régions que déterminent ces n cercles.
NCE(1) = 2
NCE(2) = 4
NCE(4) = 14
Solution :
Il faut considérer le problème en terme de récurrence.
- Cas de base. On a un cercle et 2 zones, soit R(1) = 2.
- Récurrence. A l’étape n, le nouveau cercle intersecte les n-1 précédents en 2(n-1) points.
Ces 2(n-1) points divisent le nième cercle en 2(n-1) régions. Le nombre de région est donc Rn+1 = Rn + 2(n-1)
Pour être en récursif terminal et ne pas utiliser de pile, on fait rentrer la quantité de cercle à l’itérée n en variable.
Initialisation : R(n, r) = R(n, 2)
1. R(n, r) = R(n-1, r + 2(n-1))
} j’applique ma récurrence en augmentant le total et en passant à n-1
2. R(1, r) = r
} r est un accumulateur. Si je suis arrivé au cas de base, je rends le total
On peut également résoudre directement la relation de récurrence, ce qui est tout à fait faisable à la main.
Dans ce cas, l’algorithme devient R(n) = 2 + n² - n, ce qui est évidemment constant en temps et en espace. L’idéal !
11) Nombre de séquences d’éléments consécutifs égaux
Données : Une liste L d’objets, de longueurs n.
Exercice : On veut la quantité de séquences d’éléments consécutifs égaux dans L.
Exemple : L = abbcaaadd, alors il y a 3 séquences avec des éléments consécutifs égaux.
Solution (linéaire en temps et constante en espace) :
Il n’y a pas à réfléchir beaucoup sur ce problème, c’est un simple test sur l’écriture des algorithmes abstraits.
On prend une variable booléenne mode, par défaut à ‘faux’. Dès que l’on repère un début de séquence, on
l’enclenche à vrai et on incrémente le compteur. Quand on sort de la séquence, on la remet à faux.
Initialisation : PL(L, mode, total) = PL(L, ┴, 0)
1. c1 = c2 ^ mode = ┴ → PL(c1.c2.L, mode, total) = PL(c2.L, ┬, total +1)
2. c1 = c2 ^ mode = ┬ → PL(c1.c2.L, mode, total) = PL(c2.L, ┬, total)
3. c1 ≠ c2 → PL(c1.c2.L, mode, total) = PL(c2.L, mode, total)
4. PL(Ø, mode, total) = total
Notons bien qu’après un c1 = c2, on ne peut pas directement passer à L. En effet, si on avait abbaa, alors on lirait
a ≠ b puis b = b, j’entre dans une séquence. Et si je passe directement à a = a, je ne vois pas la différence !
Philippe Giabbanelli et Julien Rosati
- 42 -
12) Multiple de tout élément d’un ensemble et inférieur au produit de tous
Données : Une liste L = {x1, x2, …, xn}, n ≥ 2, de très grands entiers positifs.
Exercice : On veut savoir s’il existe un entier k tel que k < x1 * x2 * … * xn, et tout xi de L divise k.
Solution :
On sait que k est un commun multiple à tous les entiers de l’ensemble. Or, s’il est inférieur au produit de ces
entiers, cela signifie qu’ils ne sont pas premiers deux à deux. Toute la question est alors de savoir s’il existe un
diviseur commun à tous ces éléments. Et ceci était l’exercice n°2 de notre ensemble d’annales…
La complexité est T(n) = T(n-1) + O(calcul du pgcd de deux grands nombres), soit vraisemblablement en O(n²).
13) Complexité du pgcd
Exercice : Rappeler l’algorithme d’Euclide du pgcd de n et m et calculer sa complexité
Solution :
pgcd(n, m) = pgcd(m, n mod m) On a n mod m < n / 2 d’où la complexité T(n) = T(n/2) + O(1) = O(log n)
pgcd(n, 0) = n
Annales du 15 Septembre 2005
Pour les trois premiers exercices, on justifiera la validité de l’algorithme et on calculera formellement sa
complexité en temps et en espace. Dans les deux derniers exercice, on répondra Oui/Non et justifiera.
1) Split d’un tas (4 points)
Données : Un ensemble S d’objets, sous-ensemble d’un univers U muni d’une relation d’ordre total ≤, est
stocké dans la mémoire d’un ordinateur par un tas A à l’aide de pointeurs père vers leurs 2 fils. La
cardinalité de S est n = 2k – 2, où k > 1.
Exercice : Ecrire un algorithme abstrait Split, qui divise le tas A en deux tas A1 et A2 tels que |A1| = |A2|.
Contraintes : L’algorithme doit être dans le pire des cas en O(log n), et doit être constant en espace.
2) Majorité d’un élément en tableau trié (6 points)
Données : Un tableau trié L[1…n] d’objets.
Exercice : Ecrire un algorithme MAJ qui teste s’il y a un élément x qui apparaît plus que n/2 fois dans L.
Contraintes : L’algorithme doit être en O(log n) dans le pire des cas, et doit être constant en espace.
3) Trouver le suivant dans un ABR (6 points)
Données : Un ensemble S d’objets, de cardinalité n, sous-ensemble d’un univers U muni d’une relation
d’ordre total ≤, est stocké dans la mémoire d’un ordinateur par un arbre binaire de recherche T à hauteur h.
Exercice : Ecrire un algorithme abstrait (une fonction TS sur les arbres) qui permette de trouver le suivant
de x dans S, i.e. le plus petit élément de S parmi ceux qui sont plus grands que x. On que x est dans S.
Contraintes : L’algorithme doit tourner en O(h) dans le pire des cas, et doit être constant en espace.
4) Commutativité de l’insertion dans un ABR (2 points)
Données : Un ensemble S d’objets, de cardinalité n, sous-ensemble d’un univers U muni d’une relation
d’ordre total ≤, est stocké dans la mémoire d’un ordinateur par un arbre binaire de recherche A.
Question : l’opération d’insertion d’un objet dans A est-elle commutative ? C’est-à-dire, a-t-on
ins(x, ins(y, A)) = ins(y, ins(x, A)) ? Une autre formulation : l’arbre obtenu en insérant l’objet y d’abord
et l’objet x ensuite est-il identique à l’arbre obtenu en insérant x d’abord et y ensuite ?
5) Transformer un tas en ABR (2 points)
Données : Un ensemble S d’objets, sous-ensemble d’un univers U muni d’une relation d’ordre total ≤, est
stocké dans la mémoire d’un ordinateur par un tas A à l’aide de pointeurs père vers leurs fils.
Question : peut-on transformer le tas S en un arbre binaire de recherche en temps O(n) ?
Philippe Giabbanelli et Julien Rosati
- 43 -
Téléchargement