• Aucun résultat trouvé

PC – Lycée René CASSIN Chapitre 3 - La preuve en informatique : correction des exercices

N/A
N/A
Protected

Academic year: 2022

Partager "PC – Lycée René CASSIN Chapitre 3 - La preuve en informatique : correction des exercices"

Copied!
4
0
0

Texte intégral

(1)

PC – Lycée René CASSIN Chapitre 3 - La preuve en informatique : correction des exercices 1. Prouver que le code suivant affiche la somme des entiers de 1 à 100.

s = 0

for i in range(1,101):

s+=i print(s)

Pas de problème de terminaison puisqu’il s’agit d’une boucle for.

Preuve de correction : on note, pour tout i ∈ [[1 ; 100]], si la valeur de s après la i-ième exécution de la boucle.

Invariant de boucle : pour tout i∈ℕ*, si = ∑𝑖𝑖𝑘𝑘=1𝑘𝑘. Initialisation : la propriété est vraie pour i = 1.

Hérédité : Supposons que pour un entier i ∈ [[1 ; 99]], si = ∑𝑖𝑖𝑘𝑘=1𝑘𝑘. Alors, si+1 = si + i+1 = ∑𝑖𝑖+1𝑘𝑘=1𝑘𝑘.

Ainsi, s100 = ∑100𝑘𝑘=1𝑘𝑘.

2. Après avoir deviné la fonction du programme suivant, prouver que la boucle s’arrête et que le programme remplit bien son rôle.

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

surprise(a,n) renvoie an, pour n entier naturel.

Preuve de terminaison : 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.

Preuve de correction : 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 : pour tout i∈[[0, k]], 𝑎𝑎𝑖𝑖𝑛𝑛𝑖𝑖𝑟𝑟𝑖𝑖= 𝑎𝑎0𝑛𝑛0. Initialisation : Vrai pour n = 0.

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

Si ni est pair, 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.

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

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

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

3. Recherche par dichotomie du zéro d’une fonction continue et monotone On considère le programme suivant :

def f(x):

return x**3-2

a,b=0,2

while (b-a)>0.000000001:

x=(a+b)/2 if f(x)<0:

a=x else:

b=x print(x)

On note a0 = 0, b0 = 2 et pour tout entier n ≥ 1, an et bn les valeurs respectives de a et b après la n-ième exécution de la boucle while.

(2)

Montrez P(n) pour tout entier naturel n:

« an ≤ an+1 ≤ bn+1 ≤ bn, bn+1 – an+1 = 𝑏𝑏𝑛𝑛−𝑎𝑎𝑛𝑛

2 , f(an) ≤ 0 et f(bn) ≥ 0 » En déduire la preuve de terminaison et de correction du

programme.

P(n) est notre invariant de boucle, mais la rédaction type vue dans les exercices précédents serait ici assez lourde.

Pour tout n∈ℕ, on a : an+1 = an et bn+1 = 𝑎𝑎𝑛𝑛+𝑏𝑏𝑛𝑛

2 , ou an+1 = 𝑎𝑎𝑛𝑛+𝑏𝑏𝑛𝑛

2 et bn+1 = bn. Dans les deux cas, si anbn, anan+1bn+1bn.

Or, a0 ≤ b0, donc par récurrence, pour tout n∈ℕ, an ≤ an+1 ≤ bn+1 ≤ bn. De même, pour tout n∈ℕ, bn+1 – an+1 = 𝑎𝑎𝑛𝑛+𝑏𝑏𝑛𝑛

2 – an = 𝑏𝑏𝑛𝑛−𝑎𝑎𝑛𝑛

2

ou bn+1 – an+1 = bn𝑎𝑎𝑛𝑛+𝑏𝑏𝑛𝑛

2 = 𝑏𝑏𝑛𝑛−𝑎𝑎𝑛𝑛

2

Enfin, f(a0) ≤ 0 et f(b0) ≥ 0 et si pour n ∈ℕ, f(an) ≤ 0 et f(bn) ≥ 0, alors soit x = 𝑎𝑎𝑛𝑛+𝑏𝑏𝑛𝑛

2 .

Si f(x) < 0, alors an+1 = x et donc f(an+1) ≤ 0, et bn+1 = bn donc f(bn+1) ≥ 0.

Si f(x) ≥ 0, alors an+1 = an et donc f(an+1) ≤ 0, et bn+1 = x donc f(bn+1) ≥ 0.

On en déduit que les suites (an) et (bn) sont adjacentes, elles convergent donc vers la même limite c, qui vérifie f(c) ≤ 0 et f(c) ≥ 0, donc f(c) = 0.

De plus, pour tout entier naturel n, c ∈ [an ; bn].

En particulier, bnan tend vers 0, donc sera inférieur à 0,000000001 au bout d’un certain rang, et la boucle s’arrêtera. Les valeurs de an et de bn sont alors des approximations de c à 0,000000001 près.

4. Le programme suivant s’arrête-t-il toujours ? seuil = float(input("Entrer un seuil : ")) s = 0

k = 1

while s<seuil:

s+=1/k

k+=1 print(k)

Si Python calculait en précision infinie, le programme s’arrêterait toujours, mais si 1/k est suffisamment petit devant s, alors s + k va rester égal à s pour Python. La suite des valeurs de s est donc stationnaire. Aussi, si le seuil choisi est suffisamment grand, le programme ne va pas s’arrêter.

5. Tri par sélection

Prouver que la liste T retournée par la fonction suivante contient les termes de la liste L classés dans l’ordre croissant.

def tri_selection(L):

T=[]

for i in range(0,len(L)):

min = L[0]

for x in L:

if x<min:

min = x T.append(min) L.remove(min) return T

Pas de problème de terminaison puisqu’il s’agit de boucles for.

La fonction contient deux boucles imbriquées.

On prouve par récurrence sur n ∈ [1 ; len(L)] qu’après la n-ième exécution de la boucle for x in L, la variable min contient le minimum de {L[0], L[1],

… , L[n]}). Ainsi, à la sortie de cette boucle, la variable min contient la plus petite valeur de la liste L.

Notons L0 la liste L lors de l’appel de la fonction, l la longueur de cette liste, et T0 la liste vide [].

Pour i ∈ [[1 ; l]], on note Li et Ti les listes L et T après la i-ième exécution de la première boucle for.

Invariant de boucle : pour tout i ∈ [[0 ; l ]], on note P(i) :

« Li contient les (l – i) plus grands éléments de L0 et et Ti contient les i plus petits éléments de L0 classés dans l’ordre croissant. »

(3)

Initialisation : trivial si i = 0.

Hérédité : supposons P(i).

On obtient Li+1 en supprimant de la liste Li son plus petit élément, que l’on ajoute à la fin de la liste Ti pour construire Ti+1. Or, la liste Ti était triée et constituée des i plus petits éléments de L0. La liste Ti+1 est donc également triée et constituée des (i+1) plus petits éléments de L0. La liste Li contenait les (l – i) plus grands éléments de L0, la liste Li+1 contient alors les (l – i – 1) plus grands éléments de L0.

6. Décrire le fonctionnement de la fonction suivante (les plus vaillants pourront tenter de rédiger une preuve).

def test_pal(a):

pal = True l = len(a)-1 i = 0

while i<l and pal==True:

if a[i]!= a[l]:

pal = False i+=1

l-=1 return pal

La fonction renvoie True si la chaine est un palindrome, False sinon.

Preuve de terminaison : A chaque exécution de la boucle, l’entier i est incrémenté et l’entier l décrémenté, donc au bout d’un certain nombre d’exécutions, l sera inférieur à i et la boucle s’arrêtera.

Preuve de correction : D’abord, définissons clairement un palindrome. En utilisant les notations de Python, la chaine de caractères a de longueur len(a) est un palindrome si ∀i∈[[0; len(a)//2–1]], a[i] = a[len(a)–i–1]

(rappel : le premier caractère de a est a[0] et le dernier est a[len(a)–1]).

Notons l0 = len(a) – 1, et pour tout entier i de [[1 ; l0//2 – 1]], li la valeur de l après la i-ième exécution de la boucle while.

Une récurrence immédiate permet de prouver que li = l0 – i = len(a) – 1 – i.

La boucle s’interrompe lorsque i ≥ li, soit i ≥ len(a) – 1 – i, et donc i ≥ 𝑙𝑙𝑙𝑙𝑛𝑛(𝑎𝑎)−1

2 . Ainsi, la variable i prend successivement toutes les valeurs de [[0; len(a)//2 – 1]]. Si, pour chacune de ces valeurs, a[i] = a[li], soit a[i] = a[len(a) – i – 1], alors la chaine de caractères a est un palindrome et la variable pal reste égale à True. S’il existe une valeur de i pour laquelle a[i] ≠ a[li], alors a n’est pas un palindrome et pal prend la valeur False.

Conclusion : cette fonction renvoie True si et seulement si a est un palindrome.

Remarque : j’ai peut-être un peu exagéré… une bonne description du fonctionnement du programme aurait pu être aussi convaincante que cette preuve.

7. Le redoutable Crible d’Eratosthène def eratosthene(N) :

P = []

L = [True]*(N+1)

for i in range(2,int(N**0.5)+1) : if L[i] :

L[2*i::i] = [False]*(N//i-1) for i in range(2,N+1) :

if L[i] : P.append(i) return P

Preuve (que eratosthene(N) retourne la liste P des nombre premiers inférieurs ou égaux à N)

La première boucle for s’exécute pour i allant de 2 à E(√N).

Notons, pour j ∈ [[2 ; N]] et k ∈ [[2 ; E(√N)]], Lj,k la valeur de L[j] après l’exécution de la première boucle for pour i = k.

Avant la première exécution de cette boucle, toutes les valeurs de L[j] étant initialisées à True, posons Lj,1 = True pour tout j ∈ [[2 ; N]].

(4)

Soit pour k ∈ [[1 ; E(√N)]] la proposition P(k) :

« Pour tout j ∈ [[2 ; N]], Lj,k = True si et seulement si j n’admet aucun diviseur dans [[1 ; k]] autre que 1 et lui-même (éventuellement). »

Si k = 1, [[1 ; k]] = {1} et Lj,1 = True pour tout j, donc P(1) est vraie.

Supposons que P(k) est vraie pour k ∈ [[1 ; E(√N)–1]].

Soit j ∈ [[2 ; N]].

Remarquons d’abord que la ligne L[2*i::i] = [False]*(N//i-1) fixe à False les valeurs de L[j] si i est un diviseur de j (et j ≠ i).

Lj,k+1 = True ⇔ Lj,k = True et si L[j] n’est pas modifiée par l’exécution de la boucle pour i = k+1

⇔ j n’admet aucun diviseur dans [[1 ; k]] autre que 1 et lui-même (d’après P(k)) et k+1 n’est pas un diviseur de j (sauf si k +1 = j)

⇔ j n’admet aucun diviseur dans [[1 ; k+1]] autre que 1 et lui-même Donc P(k+1) est vraie.

D’où, par récurrence, P(E(√N)) est vraie.

La deuxième boucle for construit alors la liste P de tous les nombres i de [[2 ; N]] tels que P[i] = True. Or, d’après P(E(√N)), P[i] = True si et seulement si i n’admet aucun diviseur dans [[1 ; E(√N)]] autre que 1 et lui- même, ce qui prouve que i est premier.

En effet, si i n’est pas premier, alors il existe deux entiers p, q différents de 1 tels que i = pq. Si p ≥ √𝑖𝑖, alors q ≤ √𝑖𝑖. Ainsi, si i n’est pas premier, il admet un diviseur différent de 1 inférieur ou égal à √𝑖𝑖, donc à √N.

La liste P retournée par la fonction eratosthene(N) contient bien la liste de tous les nombres premiers inférieurs ou égaux à N.

Références

Documents relatifs

Quels sont les noms des abonnés ayant rendu un livre avec un retard moyen supérieur à deux semaines.. Quels sont les titres des livres ayant été empruntés par Duchemin et

A contrario, s’il existe un livre que l’abonné n’a pas emprunté (il existe un livre pour lequel il n’existe pas de lien entre le livre et l’abonné), la requête (SELECT *

Remarque : fichier.name est sans parenthèse car name est une variable (encapsulée dans l’objet fichier), les autres opérations sont des méthodes (des fonctions qui

Créer une fonction temperature(), qui affiche les noms abrégés de chaque polymère suivis de la température de fusion ou de transition vitreuse, en précisant de laquelle il

• Tri : Pour trier une liste lst, si elle contient un seul élément, elle est déjà triée, sinon on la divise en deux listes que l’on triera de la même manière avant de

Ecrire une fonction solution, qui prend en paramètre une matrice labyrinthe, représentée par une liste de listes, et recherche un chemin solution.. Cette fonction renverra

Nous n’allons pas perdre de temps à vous faire rechercher le pivot de Gauss… c’est le programme de première année.. Vous devez

Écrire une fonction récursive donnant la liste de toutes les compositions d'un entier n (décomposition de cet entier en une somme d'entiers strictement positifs tenant compte