1 Calcul de la moyenne de deux nombres
1.1 Sp´ ecifications du probl` eme
L’objet initial : deux nombresaetb
Le r´esultat attendu : un nombre ´egal `a (a+b)/2
1.2 code
1 def moyenne(a, b):
2 return (a + b) / 2
1.3 Terminaison
Aucun probl`eme en vue : une seule instruction avec unsimple calcul.
1.4 Correction partielle
Aucun probl`eme en vue : le calcul correspondexactement `a la formule
1.5 Correction totale
L’algorithme termine et il est partiellement correct donc l’algorithme est correct.
1.6 Remarques
La correction de l’algorithme d´epend de la fa¸con dont sont g´er´es les nombres, notamment les nombres r´eels.
2 Somme des n premiers entiers
2.1 Sp´ ecifications du probl` eme
L’objet initial : un nombre entiernpositif ou nul Le r´esultat attendu : un nombre entier ´egal `a Pn−1
k=0k sin >0 et 0 sin= 0.
2.2 Code
1 def somme(n):
2 S = 0
3 for i in range(n):
4 S += i
5 return S
2.3 Terminaison
— Il y a une boucle formais le nombre de passages dans la boucle est connu a priori (c’estn).
— L’algorithme se termine toujours (apr`esnex´ecutions de la boucle).
Plus formellement,iest un variant de la boucle qui est contraint par l’instruction in range(n) de prendre successivement les valeurs : 0,1, . . . , n−1.
L’algorithme termine pour le cas limiten= 0. Dans ce cas in range(n) renvoie la liste vide ([], et la boucle n’est pas pas ex´ecut´ee.
2.4 Correction partielle
Comme on retourne S, on v´erifie que S contient `a la fin de l’ex´ecution le r´esultat attendu, c’est-`a-dire la somme des n premiers entiers :
— on v´erifie que l’initialisation deS est correcte ;
— on v´erifie que les modifications deS dans la boucle sont correctes ;
— on v´erifie que le nombre d’it´erations est correct ;
alors l’algorithme est correct.
Plus formellement, l’invariant de la boucle est la propri´et´e ”S est ´egale `a la somme des i premiers entiers positifs” : P(S=Pi
k=0k). Soit la suiteSinit =Si−1, S0, . . . , Sn−1 des valeurs successives de la variableS;
— initialement (avant d’entrer dans la boucle), la propri´et´eP est v´erifi´ee, la somme des 0 premiers entiers positifs est
´egale `a 0 (Sinit=Si−1= 0) ;
— D’une it´eration sur l’autre de la boucle,P est conserv´ee :Si=i+Si−1=i+Pi−1
k=0k=Pi k=0k)) ;
— A la fin de la boucle, la propri´et´eP est v´erifi´ee eti=n−1, doncSf inal=Sn−1=Pn−1 k=0k.
2.5 Correction totale
— Sin= 0, l’algorithme termine et renvoie 0
— Sin >0, l’algorithme termine et renvoiePn−1 k=0k
2.6 Remarques
Sinest contraint en pr´e-condition `a ˆetre strictement sup´erieur `a 0, on n’a pas `a g´erer le cas d´eg´en´er´en= 0.
Si on voulait calculer la somme des n premiers entiers strictement positifs (Pn−1
k=1k, si n > 0 et 0 si n = 0, il faudrait modifier la boucle : for i in range(1,n+1). La d´emonstration est sensiblement identique, avec des sommes qui d´emarrent
` a 1.
3 Le Zune date bug
3.1 Sp´ ecifications du probl` eme
L’objet initial : un nombre entier j positif ou nul qui est le nombre de jours depuis le premier janvier 1980 et le jour courant.
Le r´esultat attendu : un couple de nombres entiers positifs ´egal `a (j, a) qui est le nombre de jours depuis le premier janvier de l’ann´ee courante et le jour courant.
3.2 Code
1 def jour_de_l_annee(jour, annee=1980):
2 while jour > 365:
3 if bissextile(annee):
4 if jour > 366:
5 jour -= 366
6 annee += 1
7 else:
8 jour -= 365
9 annee += 1
10 return (annee, jour)
3.3 Terminaison
Soitjk et ak les suites des valeurs prises respectivement par la variable jour et par la variable annee. Les instructions de la boucle permettent de dire que si bissextile(ak) est vrai et si jk > 366 alors jk+1 = jk−366 et si bissextile(ak) est faux (else de if bissextile(annee)) alors jk+1 =jk −365. Il existe une valeur de ak et jk telle que jk >365 et bissextile(ak)estvrai et jk <= 366. Par exemple :ak = 1980 et jk = 366. Dans ce casjk+1 =jk et ak+1 =ak. Donc jk ne peut ˆetre pris comme variant de la boucle.
Pour que l’algorithme termine, deux solutions peuvent envisag´ee :
— Sortir les cas probl´ematiques de la pr´e-condition. C’est ce que fait Microsoft en conseillant de ne pas allumer l’appareil les 31 d´ecembre des ann´ees biessextiles. Il est en effet possible de montrer que la valeur qui met en d´efaut la terminaison ne peut survenir que lorsque le jour correspond au 31 d´ecembre d’une ann´ee biessextilebissextile(ak) est vrai etjk<= 366).
— Capturer la valeur probl´ematique dans la condition de la boucle while :
1 def jour_de_l_annee(jour, annee=1980):
2 while (not bissextile(annee) and jour > 365)
3 or (bissextile(annee) and jour > 366):
4 if bissextile(annee):
6 jour -= 366
7 annee += 1
8 else:
9 jour -= 365
10 annee += 1
11 return (annee, jour)
Dans ce cas, la terminaison est garantie ´egalement pour les valeurs qui v´erifientjk= 365 etbissextile(ak) est vrai.
Il est ´egalement possible d’effectuer unbreak`a l’int´erieur de la boucle :
1 def jour_de_l_annee(jour, annee=1980):
2 while jour > 365 :
3 if bissextile(annee):
4 if jour > 366:
5 jour -= 366
6 annee += 1
7 else :
8 break
9 else:
10 jour -= 365
11 annee += 1
12 return (annee, jour)
Souvent d´ecri´ee, parce que la preuve de l’algorithme est plus compliqu´ee (il faut d´emontrer que le cas probl´ematique est bien captur´e par leelse) et parce que le code est moins lisible (plus difficile de voir les sorties de boucle), cette solution (suvent qualifi´ee de paresse algorithmique) a l’avantage d’ˆetre rapide `a mettre en place et ´evite de faire des erreurs dans la d´efinition de la condition d’arrˆet.
3.4 Correction partielle
La difficult´e ici est que le r´esultat attendu n’est pas caract´eris´e math´ematiquement. Il faut montrer que la valeur de retour de l’algorithme correspond `a l’ann´ee et au jour courant, en prenant en compte les ann´ees bissextiles.
Soit la propri´et´e :P0= (j0=jk+ (ak−a0)×365 +nbk), avecjk,ak, les valeurs dejouretanneeapr`eskpassages dans la boucle,j0 et a0, les valeurs initiales de de jouret annee, etnbk le nombre d’ann´ees bissextiles apr`esk passages dans la boucle.jk est le nombre de jours entre le premier janvier de l’ann´eeak et le jour courant.
La propri´et´eP0 est v´erifi´ee (tautologiej0=j0). Que se passe-t-il entre l’it´erationket l’it´erationk+ 1 ?
— Si bissextile(annee) et jour > 366 sont vrais,ak+1=ak+ 1,jk+1=jk−366, etnbk+1=nbk+ 1. Donc :
jk+1+ (ak+1−a0)×365 +nbk+1= jk−366 + (ak+ 1−a0)×365 +nbk+ 1 = jk+ (ak−a0)×365 +nbk=j0
Pk+1 est donc toujours v´erifi´ee.
— Si bissextile(annee) est faux (et jour > 365 est vrai, sinon la boucle est termin´ee),ak+1=ak+1,jk+1=jk−365, etnk+1=nk. Donc :
jk+1+ (ak+1−a0)×365 +nbk+1= jk−365 + (ak+ 1−a0)×365 +nk= jk+ (ak−a0)×365 +nbk=j0
Pk+1 est donc toujours v´erifi´ee.
— Dans les autres cas,ak+1=ak,jk+1=jk, etnk+1=nk, Pk+1 est donc toujours v´erifi´ee.
Si la boucle termine, on a bien (j0=jpost+ (apost−a0)×365 +nbpost), avec apost, le nombre d’ann´ees depuis 1980 et jpost≤366, si la derni`ere ann´ee est bissextile etjpost≤365 sinon.
3.5 Correction totale
Le code corrig´e est correct, il termine avec les bonnes valeurs deanneeetjour.
4 Un exemple : le calcul du PGCD par la m´ ethode d’Euclide
4.1 Code
1 def euclide(a, b):
2 while b != 0:
3 a, b = b, a % b
4 return a
4.2 Terminaison
La variable b est un variant de la boucle. En effet, bk+1 = akmodbk et donc par d´efinition du modulo bk+1 < bk et bk+1>= 0.
4.3 Correction partielle
Soitak et bk la valeur des variables aet bapr`esk passages dans la boucle. La propri´et´ePk :pgcd(a0, b0) =pgcd(ak, bk) est un invariant de la boucle :
— Elle est trivialement v´erifi´ee pourk= 0
— pgcd(ak+1, bk+1) =pgcd(bk, akmodbk) =pgcd(ak, bk). SiPk est verifi´ee,pgcd(ak+1, bk+1) =pgcd(a0, b0), doncPk+1 est v´erifi´ee.
— `a la derni`ere it´eration,bk = 0 (condition de sortie de la boucle), etpgcd(a0, b0) =pgcd(ak, bk) =pgcd(ak,0) =ak.
4.4 Correction totale
L’algorithme termine et renvoie bien la valeur dupgcd deaet deb.
5 Exponentiation It´ erative avec une boucle for
5.1 Code
1 def expo_ite_naive(x,n):
2 p = 1
3 for i in range(n):
4 p *= x
5 return p
5.2 Terminaison
i est un variant de la boucle qui est contraint par l’instruction in range(n) de prendre successivement les valeurs : 0,1, . . . , n−1.
L’algorithme termine pour le cas limiten= 0. Dans ce cas in range(n) renvoie la liste vide ([], et la boucle n’est pas pas ex´ecut´ee.
5.3 Correction partielle
L’invariant de la boucle est :pk =xk.
— Il est v´erifi´e avant d’entrer dans la boucle (k= 0 etp0= 1 =x0).
— il est conserv´e de l’it´erationk `a l’it´erationk+ 1 :pk+1=x×pk =x×xk =xk+1.
— Il est v´erifi´e apr`es la l’it´erationk=n(ivariant de 0 `an−1, il y a nit´erations), doncpn =xn.
6 Exponentiation rapide avec une boucle while
1 def expo_ite_rapide(x,n):
2 m = n
3 res = 1
4 puiss = x
5 while m != 0:
6 if m % 2 == 1:
7 res = res * puiss
8 m = m // 2
9 puiss = puiss ** 2 return res
6.1 Terminaison
Comme d’une it´eration sur l’autre,mk+1=bmk/2c< mk, simk+1>0. Donc la suitemk atteint 0.
6.2 Correction partielle
Un invariant de la boucle est :xn =res×puissm:
— C’est vrai avant la boucle :res×puissm= 1×xn =x.
— La propri´et´e est conserv´ee d’une it´erationk`a une it´erationk+ 1 :
— Simk est pair, il peut s’´ecriremk= 2a, mk+1=a, et :
resk+1×(puissk+1)mk+1 = resk×((puissk)2)a= resk×(puissk)2a= resk×(puissk)mk =xn
— Simk est pair, il peut s’´ecriremk= 2a+ 1,mk+1=a, et :
resk+1×(puissk+1)mk+1= resk×puissk×((puissk)2)a = resk×puissk×(puissk)2a= resk×(puissk)2a+1= resk×(puissk)mk=xn
7 Recherche dichotomique dans un tableau tri´ e
1 def dichotomie(t, v):
2 a = 0
3 b = len(t) - 1
4 while a <= b:
5 m = (a + b) // 2
6 if t[m] == v:
7 # on a trouv´e v
8 return True
9 elif t[m] < v:
10 a = m + 1
11 else:
12 b = m - 1
13 # on a a > b
14 return False
8 Tri par insertion
1 def tri_insertion(T):
2 for index in range(len(T)):
3 item = T[index]
4 j = index
5 while j>0 and liste[j-1] > item:
6 liste[j] = liste[j-1]
7 j=j-1
8 liste[j]=item
9 Vote ` a la majorit´ e na¨ıf
1 def vote_majorite(t):
2 for x in t:
3 c = 0
4 for y in t:
5 if x == y :
6 c = c +1
7 if c > len(t) // 2 :
8 return x
9 return None
10 Vote ` a la majorit´ e (Boyer-Moore)
1 def vote_majorite_BM(t):
2 m = 0
3 i = 0
4 for x in t:
5 if i == 0 :
6 m = x
7 i = 1
8 elif m == x :
9 i = i + 1
10 else :
11 i = i -1
12 return m
11 Multiplication russe
1 def mult(m, n):
2 r = 0
3 while n > 0:
4 if n%2 == 1:
5 r += m
6 m *= 2
7 n //= 2
8 return r
12 Recherche du maximum par dichotomie
1 def maxdic(tab, beg, end):
2 if beg + 1 < end:
3 mid = (beg + end) // 2
4 maxl = maxdic(tab, beg, mid)
5 maxr = maxdic(tab, mid, end)
6 if maxl > maxr:
7 return maxl
8 else:
9 return maxr
10 else:
11 return tab[beg]
12
13 def maxtab(tab):
14 return maxdic(tab, 0, len(tab))