• Aucun résultat trouvé

La preuve en informatique

N/A
N/A
Protected

Academic year: 2022

Partager "La preuve en informatique"

Copied!
41
0
0

Texte intégral

(1)

La preuve en informatique

(2)

Preuve de terminaison

• Si la suite d'instructions n'est pas récursive et n'utilise pas de boucle, ou seulement des boucles for s’exécutant un nombre fini de fois, alors elle termine.

• Dans le cas contraire, on s’appuiera sur les propriétés suivantes :

Une suite d’entiers naturels strictement décroissante est finie.

Une suite d’entiers naturels strictement croissante tend vers +∞ (et sera supérieur à n’importe quel nombre A au bout d’un certain rang).

• On appelle convergent, ou parfois « variant » de boucle, une

expression du type entier naturel qui décroit strictement à chaque exécution d’une boucle ou lors de chaque appel récursif.

(3)

Preuve de terminaison : exemples

d’algorithmes qui ne se terminent pas

def algostupide():

n = 10

while n > 0:

n = n + 1

def algostupide2():

lst = [1]

for i in lst:

lst.append(1)

(4)

Exemple de preuve de terminaison : algorithme d’Euclide

Exemple : def pgcd(a,b) :

"Calcule le pgcd de a et b"

while b != 0 : a, b = b, a%b return a

(5)

Exemple de preuve de terminaison : algorithme d’Euclide

Exemple : def pgcd(a,b) :

"Calcule le pgcd de a et b"

while b != 0 : a, b = b, a%b return a

Dans l’algorithme d’Euclide appliqué à deux entiers positifs a et b, la boucle s’exécute tant que b ≠ 0, et b est un entier qui décroit strictement à chaque exécution, donc va forcément finir par prendre la valeur 0. L’algorithme va donc s’arrêter.

(6)

Exemple de preuve de terminaison : algorithme d’Euclide

Exemple : def pgcd(a,b) :

"Calcule le pgcd de a et b"

while b != 0 : a, b = b, a%b return a

Dans l’algorithme d’Euclide appliqué à deux entiers positifs a et b, la boucle s’exécute tant que b ≠ 0, et b est un entier qui décroit strictement à chaque

exécution, donc va forcément finir par prendre la valeur 0. L’algorithme va donc s’arrêter.

b décroit strictement à chaque exécution, car b est remplacé à chaque étape par le reste r de la division euclidienne de a par b et qu’il vérifie a = bq + r avec r < b.

(7)

Exemple de preuve de

terminaison/correction : le tri à bulles

def tribulles(lst):

pastrie = True while pastrie:

pastrie = False

for i in range(len(lst)-1):

if lst[i] > lst[i+1]:

lst[i], lst[i+1] = lst[i+1], lst[i]

pastrie = True return lst

(8)

Exemple de preuve de terminaison/correction : le tri à bulles

def tribulles(lst):

pastrie = True while pastrie:

pastrie = False

for i in range(len(lst)-1):

if lst[i] > lst[i+1]:

lst[i], lst[i+1] = lst[i+1], lst[i]

pastrie = True return lst

Expliquer en français le

principe de cet algorithme de tri.

Comment peut-on être sûr qu’il se termine ? et qu’il renvoie bien la liste triée ?

(9)

Exemple de preuve de terminaison : le tri à bulles

def tribulles(lst):

pastrie = True while pastrie:

pastrie = False

for i in range(len(lst)-1):

if lst[i] > lst[i+1]:

lst[i], lst[i+1] = lst[i+1], lst[i]

pastrie = True return lst

Comment peut-on être sûr qu’il se termine ?

On cherche un « variant de

boucle », un entier qui diminue à chaque exécution de la

boucle.

(10)

Exemple de preuve de terminaison : le tri à bulles

def tribulles(lst):

pastrie = True while pastrie:

pastrie = False

for i in range(len(lst)-1):

if lst[i] > lst[i+1]:

lst[i], lst[i+1] = lst[i+1], lst[i]

pastrie = True return lst

On considère le nombre d’inversions, c’est-à-dire le nombre de couples (m, n) où lst[m] > lst[n].

(11)

Exemple de preuve de terminaison : le tri à bulles

def tribulles(lst):

pastrie = True while pastrie:

pastrie = False

for i in range(len(lst)-1):

if lst[i] > lst[i+1]:

lst[i], lst[i+1] = lst[i+1], lst[i]

pastrie = True return lst

À chaque permutation, ce

nombre d’inversions diminue.

S’il ne change pas lors d’une exécution de la boucle for, c’est que l’algorithme n’a pas effectué de permutation, et pastrie garde la valeur False et l’algorithme se termine.

(12)

Exemple de preuve de terminaison : le tri à bulles

def tribulles(lst):

pastrie = True while pastrie:

pastrie = False

for i in range(len(lst)-1):

if lst[i] > lst[i+1]:

lst[i], lst[i+1] = lst[i+1], lst[i]

pastrie = True return lst

De plus, le nombre

d’inversions ne peut pas diminuer indéfiniment puisque c’est un nombre entier, minoré par 0, donc l’algorithme se termine nécessairement.

(13)

Exemple de preuve de terminaison/correction : le tri à bulles

def tribulles(lst):

pastrie = True while pastrie:

pastrie = False

for i in range(len(lst)-1):

if lst[i] > lst[i+1]:

lst[i], lst[i+1] = lst[i+1], lst[i]

pastrie = True return lst

Lorsqu’il se termine, cela signifie que pour tout i, lst[i] < lst[i+1],

donc la liste est triée.

(14)

Exemple de preuve de correction : le tri à bulles

def tribulles(lst):

pastrie = True while pastrie:

pastrie = False

for i in range(len(lst)-1):

if lst[i] > lst[i+1]:

lst[i], lst[i+1] = lst[i+1], lst[i]

pastrie = True return lst

On peut rédiger cette preuve avec des notations plus

mathématiques en utilisant un invariant de boucle.

On note ni le nombre

d’inversions après i exécutions de la boucle while.

Que peut-on prendre comme invariant de boucle ?

(15)

Exemple de preuve de correction : le tri à bulles

def tribulles(lst):

pastrie = True while pastrie:

pastrie = False

for i in range(len(lst)-1):

if lst[i] > lst[i+1]:

lst[i], lst[i+1] = lst[i+1], lst[i]

pastrie = True return lst

On note ni le nombre

d’inversions après i exécutions de la boucle while.

On peut prendre comme invariant de boucle :

« ni+1 ≤ ni »,

tant que i + 1 ne dépasse par le nombre d’exécutions de la boucle.

(16)

Exercice 2 : surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

(17)

Exercice 2 : surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Que renvoie surprise(a,n) ?

(18)

Exercice 2 : terminaison de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Que peut-on prendre comme variant de boucle?

(19)

Exercice 2 : terminaison de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Que peut-on prendre comme variant de boucle?

La variable n.

(20)

Exercice 2 : terminaison de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Que peut-on prendre comme variant de boucle?

La variable n.

En effet, la valeur de l’entier naturel n décroit strictement à chaque exécution de la boucle, donc la boucle se termine,

lorsque n = 0, après k exécutions.

(21)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Que peut-on prendre comme invariant de boucle?

(22)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Que peut-on prendre comme invariant de boucle?

On cherche une relation entre les variables a, n, r vraie à

chaque exécution de la boucle, et en lien avec ce qu’on veut démontrer.

(23)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Que peut-on prendre comme invariant de boucle?

On cherche une relation entre les variables a, n, r.

À la fin, on veut que le « r final » soit égal au

« an initial ».

(24)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Introduisons des notations.

(25)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Introduisons des notations.

Soient pour tout entier naturel i inférieur ou égal à k, ai, ni, ri les valeurs des variable a, n et r après la i-ième exécution de la boucle, en notant a0, n0 et r0 les valeurs initiales.

(26)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Soient pour tout entier naturel i inférieur ou égal à k, ai, ni, ri les valeurs des variable a, n et r

après la i-ième exécution de la boucle, en notant a0, n0 et r0 les valeurs initiales.

Invariant de boucle : quelle relation en lien avec ce qu’on veut démontrer vérifient ai, ni, ri à chaque exécution de la boucle?

(27)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle : quelle relation en lien avec ce qu’on veut démontrer

vérifient ai, ni, ri à chaque exécution de la boucle?

On veut trouver 𝑎𝑎𝑛𝑛, soit 𝑎𝑎0𝑛𝑛0 avec les valeurs initiales.

(28)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle : quelle relation en lien avec ce qu’on veut démontrer

vérifient ai, ni, ri à chaque exécution de la boucle?

On veut trouver 𝑎𝑎𝑛𝑛, soit 𝑎𝑎0𝑛𝑛0 avec les valeurs initiales.

Si n diminue de 1, r est multiplié par a.

(29)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle : quelle relation en lien avec ce qu’on veut démontrer

vérifient ai, ni, ri à chaque exécution de la boucle?

On veut trouver 𝑎𝑎𝑛𝑛, soit 𝑎𝑎0𝑛𝑛0 avec les valeurs initiales.

Si a est élevé au carré, n est divisé par 2.

(30)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle : quelle relation en lien avec ce qu’on veut démontrer

vérifient ai, ni, ri à chaque exécution de la boucle?

Invariant de boucle :

(31)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle : quelle relation en lien avec ce qu’on veut démontrer

vérifient ai, ni, ri à chaque exécution de la boucle?

Invariant de boucle : pour tout i∈[[0, k]],

(32)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle : quelle relation en lien avec ce qu’on veut démontrer

vérifient ai, ni, ri à chaque exécution de la boucle?

Invariant de boucle :

pour tout i∈[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0

(33)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle :

pour tout i∈[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0

Une fois l’invariant de boucle formulé, le plus dur est fait.

(34)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle :

pour tout i∈[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0 Initialisation : Vrai pour n = 0.

(35)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle :

pour tout i∈[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0

Hérédité : Supposons la propriété précédente vraie pour un entier i.

(36)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle :

pour tout i∈[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0

Hérédité : Supposons la propriété précédente vraie pour un entier i.

Si ni est impair, alors ri+1 = ri × a,

ni+1 = ni – 1

et ai+1 = ai.

(37)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle :

pour tout i[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0

Hérédité : Supposons la propriété précédente vraie pour un entier i.

Si ni est impair, alors ri+1 = ri × a, ni+1 = ni – 1 et ai+1 = ai.

𝑎𝑎𝑖𝑖+1𝑛𝑛𝑖𝑖+1𝑟𝑟𝑖𝑖+1 = 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖−1𝑟𝑟𝑖𝑖 × 𝑎𝑎 = 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖 = 𝑎𝑎0𝑛𝑛0 d’après l’hypothèse de récurrence.

(38)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle :

pour tout i[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0

Hérédité : Supposons la propriété précédente vraie pour un entier i.

Si ni est pair, alors ri+1 = ri, ai+1 = 𝑎𝑎𝑖𝑖2,

ni+1 = 𝑛𝑛2𝑖𝑖.

(39)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle :

pour tout i[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0

Hérédité : Supposons la propriété précédente vraie pour un entier i.

Si ni est pair, alors ri+1 = ri, ai+1 = 𝑎𝑎𝑖𝑖2, ni+1 = 𝑛𝑛2𝑖𝑖.

𝑎𝑎𝑖𝑖+1𝑛𝑛𝑖𝑖+1𝑟𝑟𝑖𝑖+1 = 𝑎𝑎𝑖𝑖2 𝑛𝑛𝑖𝑖2 ri = 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0.

(40)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle :

pour tout i∈[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0

Hérédité : Supposons la propriété précédente vraie pour un entier i.

L’hérédité est vérifiée dans les deux cas.

L’invariant de boucle est donc établi pour tout entier i∈[[0, k]].

(41)

Exercice 2 : correction de surprise(a,n)

def surprise(a,n):

r = 1

while n>0:

if (n%2):

r = r * a n = n - 1 else:

a = a * a n = n / 2 return r

Invariant de boucle :

pour tout i∈[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0

Quand l’algorithme se termine, ni = 0, et donc 𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0.

Références

Documents relatifs

On définit une structure de données Encodeur permettant de représenter, sous forme d’un enregistrement, l’état du codeur de longueurs de séquences. Cet état correspond à

un sous-ensemble de sommets de G dont chaque deux ´el´ements sont reli´es par une arˆete 2. une ´equation de la forme un polynˆ

L'augmentation de la demande sociale et le fait que la société civile agisse de plus en plus pour défendre une planète qui en a besoin font que l'éducation relative à

Les participants à l’étude étaient 12 étudiants de licence STAPS, représentatifs de différents niveaux de performance en gymnastique (niveau débutant à niveau régional

Cette communication propose de décrire la conception, le déroulé et l’évaluation de quatre modules de formation à la dynamique de groupe instaurés dans le

Notons tout de même que certaines de ces explications auraient pu être davantage développées : ainsi avant de bouger la règle de Farid pour montrer que l'on ne pouvait pas tracer

33 (a) Institute of High Energy Physics, Chinese Academy of Sciences, Beijing; (b) Department of Modern Physics, University of Science and Technology of China, Anhui; (c) Department

This explains the fact that the macro-cell load is first increasing (see figure 6). At this time instant, SBSs are indeed starting serving users, alleviating some load from the