Pour chacun des programmes suivants, on prouvera la validité de la réponse et on évaluera la complexité dans le pire cas.
Exercice 1 : Donner des exemples d’algorithme (de préférence classique) dont la complexité est logarithmique (resp. linéaire, enO(n2),O(n3), expo- nentiel).
Exercice 2 : Écrire une fonctionbinom(n,k)qui calcule¡n
k
¢pour 0ÉkÉn.
On cherchera la meilleure complexité possible.
Exercice 3 : Écrire un programmesomchiffre(n)qui renvoie la somme des chiffres décimaux de l’entiern.
Exercice 4 : Écrire un programmenbchiffre(n)qui renvoie le nombre de chiffres décimaux de l’entiern. Écrire une variantenbchiffre(n,b)qui renvoie le nombre de chiffre dendans la basebÊ2.
Exercice 5 : Écrire un programmedecomp(n)d’argument un entier stricte- ment positifnet qui renvoie l’unique couple d’entiers (s,t) avectimpair tel quen=2st.
Exercice 6 : La suite de Fibonacci est la suite définie par récurrence par F0=0,F1=1 etFn+2=Fn+1+Fn pournÊ0. Écrire un programmefiboqui renvoie lenenombre de Fibonacci.
Exercice 7 : Écrire un programmenombre_palindromequi renvoieTruesi un nombre entier est un palindrome en base 10 etFalsesinon. (Exemple : 1534351 est un palindrome car son écriture est symétrique.)
Écrire un programmepalindromequi renvoieTruesi une chaîne de caractère est un palindrome etFalsesinon.
Exercice 8 : Écrire un programmesomdivproprequi calcule la somme des diviseurs propres (i.e.différents den) de l’entiernÊ1. Écrire ensuite un pro- grammeparfaitqui renvoieTruesin est parfait (i.e.égal à la moitié de la somme de ses diviseurs) etFalsesinon.
Exercice 9 : Un diviseur d’un entier naturel aest propre s’il est différent de a. Un couple d’entiers (a,b) estamiable si aest égal à la somme des diviseurs propres debetbla somme des diviseurs propres deaavecaet bdistincts. Écrire un programmeamiabled’argument un entierN qui ren- voie l’ensemble des couples amiables (a,b) aveca<bÉN oùN est donné.
Arrivez-vous à une complexité enO(N2) ? (Le calcul de la somme des divi- seurs propres deNa un coût enO(N).)
Exercice 10 – Multiplication du paysan russe: En s’inspirant de l’algorithme d’exponentiation rapide, écrire un programmerusse(a,b)qui prend en argu- ment deux entiers naturelsa,bet qui renvoie le produitaben n’utilisant que des additions, des multiplications par 2 et des divisions par 2. Le programme doit être linéaire en log2b.
Exercice 11 : Que fait le programme suivant ?
1 defmystere(n):
2 L=[]
3 d=2
4 a=n
5 whilea>1:
6 whilea%d != 0 :
7 d= d+1
8 v=0
9 whilea%d == 0:
10 v= v+1
11 a= a//d
12 L+=[[d,v]]
13 return(L)
Exercice 12 : Que fait le programme suivant ? Majorer le plus finement pos- sible sa complexité.
1 defprotocole(n:int):
2 L = [ Trueforkin range(n+1)]
3 L[0], L[1] = False, False
4 forkin range(2, n+1):
5 L[k] = True
6 forkin range(2, n+1):
7 if L[k] == True:
8 j = 2*k
9 whilej <= n:
10 L[j ] = False
11 j += k
12 k += 1
13 returnL
Exercice 13 – Golomb: La suite de Golomb est l’unique suite d’entiers crois- sante (uk)kÊ1telle queu1=1 et auto-descriptive au sens suivant :unest le nombre de fois où l’entiernapparaît dans la suite (uk).
1. Déterminer les 10 premiers termes de la suite de Golomb.
2. Écrire un programme Golomb(n) qui renvoie la liste desn premiers termes de la suite de Golomb. (Leslicingest autorisé.)
Exercice 14 – Le problème de Flavius Josephus:
On dispose lesn entiers 0, 1, 2, . . . ,n−1 en cercle. On en raye un sur deux en commençant par le 1. On noteJ(n) le dernier rayé. Par exemple,J(4)=0 et J(5)=2. Écrire un programmeJosephqui renvoieJ(n). (Indication : on pourra utiliserdelqui efface un terme d’une liste.)
Exercice 15 : (Difficile) On se donne une listeLd’entiers naturel de longueur n. Un élément deLest dit prévalent s’il apparait au moinsbn/2c +1 fois dans L. Écrire un programmeprevalent(L)d’argument la listeLet qui renvoie un élément prévalent s’il existe etNonesinon, enO(N) temporel etO(1) spatial.
Solutions
Exercice 1. — Pour une complexité logarithmique : la recherche dichoto- mique dans un tableau trié.
— Linéaire : L’évaluation de P(x) par la méthode de Horner. Ou la re- cherche du maximum d’un tableau non trié.
— Quadratique : le calcul du produit de deux polynômes par la méthode standard.
— Cubique : l’algorithme du pivot de Gauss pour résoudre un système li- néaire.
— Exponentiel : le test de primalité standard qui consiste à tester les divi- seurs potentiels. Ou le problème du sac à dos.
Exercice 3. Pour les chiffres décimaux :
1 defsomchiffre(n):
2 a = n
3 S = 0
4 whilea>0:
5 S += a%10
6 a = a//10
7 returnS
La terminaison vient du fait que dans la seule bouclewhile, la variableaest décroît strictement au cours des appels, sauf quanda=0 et alors on sort.
Pour la correction, on dispose de l’invariant de boucleS(a)+S oùS(a) est la somme des chiffres dea. C’est bien un invariant, car entre deux appels consécutifs de la bouclewhile,S(a) devientS(a)+r oùr est le chiffre des unités dea, tandis queaperd son chiffre des unités. La complexité est loga- rithmique, vu qu’on appelle la boucle whileO(ln10n) fois, et qu’on effectue un nombre fini fixé d’opérations à chaque passage.
Exercice 4. Pour les chiffres décimaux :
1 defnbchiffre(n):
2 a = n
3 c = 0
4 whilea>0:
5 a = a//10
6 c += 1
7 returnc Et en baseb:
1 defnbchiffre(n,b):
2 a = n
3 c = 1
4 whilea>b:
5 a = a//b
6 c += 1
7 returnc
La terminaison de la bouclewhilene pose aucun problème : l’entierkest au moins divisé parbà chaque étape, jusqu’à tomber sur un nombre de [[0,b− 1]], qui fait sortir de la boucle. Pour la correction, le nombre de chiffre en base bdebc−1aest un invariant de boucle. Sur l’argumentα, il vaut le nombre de chiffre deαà l’entrée dans la boucle, donc aussi en sortie.
Exercice 5. On commence par déterminers:
1 defdecomp(n):
2 k=n
3 s=0
4 whilek%2==0:
5 s+=1
6 k//=2
7 return([s,k])
La terminaison de la bouclewhilene pose aucun problème : l’entierkest au moins divisé par 2 à chaque étape, jusqu’à tomber sur un nombre impair, qui fait sortir de la boucle. Pour la correction, on dispose de l’invariant de boucle 2s×k, qui vautnà l’entrée.
Exercice 6. Pour calculer lenenombre de Fibonacci avec une solution naïve :
1 deffibo(n):
2 if n== 0:
3 return(0)
4 elif n==1:
5 return(1)
6 else:
7 a=0
8 b=1
9 forkin range(n−1):
10 a,b=b,a+b
11 return(b)
Il y a un invariant simple :a=Fketb=Fk+1, qui prouve la correction. La terminaison d’une boucleforest automatique.
On peut faire beaucoup mieux en remarquant que µun+2
un+1
¶
= A µun+1
un
¶ où A=
µ0 1 1 1
¶
. On a donc (récurrence immédiate) que µun+1
un
¶
=An µu1
u0
¶
=A µ1
0
¶ . Il suffit de calculerAnpar exponentiation rapide. Chaque multiplication ma- tricielle a un coût constant, et on a un nombre d’étape logarithmique enn.
D’où une complexité logarithmique enn.
Exercice 7. L’idée du programme suivant est de retourner l’écriture décimale et de comparer ensuite avec l’argument. En notantret(k) l’entier obtenu en inversant les chiffres décimaux dek, on a l’invariantp·ret(k) où·est l’opé- rateur de concaténation.
1 defnombre_palindrome(n):
2 k=n
3 p=0
4 whilek!=0:
5 r=k%10
6 p=p*10+r
7 k=k//10
8 if n==p:
9 returnTrue
10 else:
11 returnFalse
On adapte l’algorithme précédent pour une chaîne de caractères. Mais atten- tion : les chaînes de caractères sont des objets non-mutables (i.e.non modi- fiable).
1 defstring_palindrome(m):
2 l =len(w)
3 forkin range(l//2):
4 if w[k]!=w[l−k−1]:
5 return(False)
6 return(True)
On prend comme invariant le booléen “west un palindrome etw[p]=w[l− 1−p] pour toutpÉk” oùlest la longueur dew.
Exercice 8. On s’arrête bien entendu à n/2 pour trouver les diviseurs propres. Un invariant ests+ X
k|n,d+1ÉkÉE(n/2)
k. La correction vient du fait que daugmente strictement et donc dépasseE(n/2) en un nombre fini détapes.
1 defsomdivpropre(n):
2 d=1
3 s=0
4 whiled<=n//2:
5 if n%d==0:
6 s+=d
7 d+=1
8 return(s)
1 defparfait(n):
2 return(n==somdivpropre(n))
Exercice 9. On utilise la fonctionsomdivpropresprécédente.
1 defamiable(n):
2 L=[]
3 forain range(1,n+1):
4 b=somdivpropre(a)
5 if a==somdivpropre(b)anda<b:
6 L.append([a,b])
7 returnL
L’idée est que si (a,b) est un couple amiable,best uniquement déterminé para. La solution proposée ne fait intervenir qu’une seule boucleforet à chaque itération, on appelle deux fois la fonctionsomdivpropre.
Exercice 10. Le programme suivant convient.
1 defrusse(a,b):
2 r = 0
3 whileb > 0:
4 if b%2 == 1:
5 r = r + a
6 a = a * 2
7 b = b // 2
8 returnr
Terminaison : tant quebest strictement positif, la variablebdécroît stricte- ment. Elle prend donc la valeur 0. On sort alors de la boucle et on effectue le return.
Complexité : chaque étape de la boucle while comporte 2 tests et au plus 3 calculs avec affectations, soit un nombre borné. Or bétant divisé par 2 à chaque étape, il atteint 0 en log2b étapes. On aO(lnb) opérations, soit une compléxité linéaire en la taille deb. (Remarque : on peut optimiser en O(min(lna, lnb)) en utilisant la commutativité.)
Correction : la quantitéI=I(r,a,b)=r+abest un invariant de boucle. En effet, si à une étapeb>0 est pair, alors la nouvelle valeur deI estr+2a∗ (b/2)=r+ab. De même sibest impair,Idevient (r+a)+2a∗(b−1)/2= r+ab. À l’entrée,rvaut 0 et à la finbest nul. Donc si on appelle (a0,b0) les arguments, qui sont donc les valeurs initiales deaetb,rvauta0b0à la fin.
Exercice 11. Ce programme renvoie une liste contenant les couples (=liste à deux éléments) des diviseurs premiers et leur multiplicité.
Exercice 12. Il s’agit de l’implantation usuelle du crible d’Érathostène qui renvoie le tableau de booléensP tel queP[k]=Truesi et seulement siP[k] est premier.
Étudions sa complexité. La création deL et la première boucleforcontri- buent chacune pourn−1 opérations élémentaires. NotonsC(n) le nombre d’opérations élémentaires effectuées dans la seconde boucle pour l’entréen.
Dans le pire cas (siL[k] vautTrue), dans la seconde bouclefor, la bouclewhile sera exécutée (p−1)-fois oùp=cn/kb. Chaque passage dans cette boucle donne lieu à quatre opérations élémentaires (incrémentation de j et test
jÉninclus). On a donc C(n)É4
n
X
k=2
(bn/kc −1)=
n
X
k=2
bn/kc −4n+4.
Vu quen+k−1É bn/kc Én/k, on a
n
X
k=2
(n/k−1)É
n
X
k=2
bn/kc É
n
X
k=2
n/k.
On e ndéduit que
4nHn+O(n)É
n
X
k=2
bn/kc É4nHn+O(n).
En ajoutant les termesO(n) trouvés au début, la complexité du crible d’Éra- thostène est majorée par unO(nlnn).
Exercice 13. On se convainc facilement que le deuxième terme de la suite de Golomb ne peut être que 2. En effet, ce n’est pas 1, et si le terme suivant était p>2, il y auraitpfois le terme 2, qui ne peuvent être situés qu’après le 1 par croissance.
On met à part les deux cas particuliern=1 etn=2. DoncLcommence par 1, 2, 2. On lit donc sur le début de la liste le nombre de 3 à ajouter, puis le nombre de 4,etc. Après les avoir ajoutés, on risque de dépassern. On enlève alors les termes surnuméraires.
1 defGolomb(n):
2 if n == 1:
3 return[1]
4 if n == 2:
5 return[1,2]
6 L = [1,2,2]
7 c = 2
8 while len(L) < n:
9 c = c+1
10 nb = L[c−1]
11 L = L + nb *[c]
12 t =len(L)
13 forkin range(t−n):
14 L.pop()
15 returnL
Exercice 14. Une première solution :
On crée une liste L contenant successivement les entiers 0, 1, . . . ,n−1. À chaque étape, on supprime un élément deLpuis on stocke l’indicepedu prochain éliminédans la liste actualisée L. La terminaison ne pose pas de problème : on a exactementn−1 itération de la bouclefor.
Pour la correction, on constate qu’à chaque étape on supprime l’entier d’in- dicepepuis que le prochain éliminé a pour indice
— pe+1 sipe<len(L)−1 ;
— 0 sipe=len(L)−2 (avant-dernier terme) ;
— 1 sipe=len(L)−1 (dernier terme) car le successeur deL[len(L)−1] estL[0].
1 defJoseph(n):
2 L=list(range(n))
3 pe=1
4 while len(L)>1:
5 if pe<len(L)−2:
6 delL[pe]
7 pe+=1
8 elif pe==len(L)−2:
9 delL[pe]
10 pe=0
11 else:
12 delL[pe]
13 pe=1
14 return(L[0])
Une deuxième solution, qui ne fait plus jouer de rôle particulier aux deux derniers indices de la liste. La congruence est clairement adaptée au carac- tère circulaire du problème.
1 defJoseph_bis(n):
2 L=list(range(n))
3 k=0
4 fori in range(n−1):
5 k=(k+1)%(n−i)
6 delL[k]
7 return(L[0])
Enfin, une solution sans liste ! Seulement des congruences.
1 defJoseph_ter(n):
2 r=0
3 forkin range(2,n+1):
4 r=(r+2)%k
5 return(r)
Les deux programmes précédents sont corrects car basés sur la formule de récurrenceJ(k+1)=J(k)+2 modk+1... qui reste à prouver.
Exercice 15.