• Aucun résultat trouvé

Un exemple

N/A
N/A
Protected

Academic year: 2022

Partager "Un exemple"

Copied!
38
0
0

Texte intégral

(1)

Récursion

Judicaël Courant

20 août 2015

(2)

Exemple introductif

On se donne :

Une matrice carrée 𝐴 de taille 𝑛, un entier 𝑝,

une fonction multipliant deux matrices On veut :

calculer 𝐴u�,

avec aussi peu de multiplications matricielles que possibles.

(3)

Algorithme naïf

1 Précondition : 𝐴 matrice carrée de taille 𝑛 et 𝑝 ∈ ℕ.

2 𝐵 ← 𝐼u�

3 Pour 𝑖 ∈ [[0, 𝑝[[

4 𝐵 ← 𝐵 × 𝐴

5 Postcondition: 𝐵 contient 𝐴u�.

(algorithme 𝒜0(𝐴, 𝑝) dans ce cours)

(4)

Un exemple

Pour calculer à la main

( 0 11 1 )

6

On effectue :

A. 0 multiplication matricielle (uniquement des additions) B. 3 multiplications matricielles

C. 5 multiplications matricielles D. 6 multiplications matricielles

(5)

De manière générale

Pour calculer 𝐴u� par l’algorithme 𝒜0 : 𝑝 multiplications.

Peut-on faire mieux ?

(6)

Diviser pour régner

Divide ut regnes Devise latine désignant la pratique politique consistant à diviser la population pour pouvoir régner, alors que si la population était unie, elle aurait les moyens de faire tom- ber le pouvoir en question.

(7)

Divisons par 2

Calcul de 𝐴u� par l’algorithme 𝒜0 : 𝑝 multiplications.

Meilleure idée :

1 Précondition : 𝐴 matrice carrée de taille 𝑛 et 𝑝 ∈ ℕ.

2 Si 𝑝 = 0, retourner 𝐼u�. 3 Si 𝑝 = 1, retourner 𝐴.

4 𝑘 ← ⌊𝑝/2⌋

5 𝐵 ← 𝒜0(𝐴, 𝑘) 6 Si 𝑝 pair:

7 Retourner 𝐵2. 8 Sinon:

9 Retourner 𝐵2 × 𝐴.

(8)

Division par 4

Encore mieux :

1 Précondition : 𝐴 matrice carrée de taille 𝑛 et 𝑝 ∈ ℕ.

2 Si 𝑝 = 0, retourner 𝐼u�. 3 Si 𝑝 = 1, retourner 𝐴.

4 𝑘 ← ⌊𝑝/2⌋

5 𝐵 ← 𝒜1(𝐴, 𝑘) 6 Si 𝑝 pair:

7 Retourner 𝐵2. 8 Sinon:

9 Retourner 𝐵2 × 𝐴.

(algorithme 𝒜2(𝐴, 𝑝))

Calcul de 𝒜2(𝐴, 𝑝) : ∼ 𝑝/4 multiplications (pour 𝑝 → +∞).

(9)

Division par 2

u�

Algorithme 𝒜u�(A,p) :

1 Précondition : 𝐴 matrice carrée de taille 𝑛 et 𝑝 ∈ ℕ.

2 Si 𝑝 = 0, retourner 𝐼u�. 3 Si 𝑝 = 1, retourner 𝐴.

4 𝑘 ← ⌊𝑝/2⌋

5 𝐵 ← 𝒜u�−1(𝐴, 𝑘) 6 Si 𝑝 pair:

7 Retourner 𝐵2. 8 Sinon:

9 Retourner 𝐵2 × 𝐴.

(10)

Un interlude anodin

Posons :

𝑓u� : ℝ → ℝ 𝑥 ↦ 𝑥u�𝑒−u�

Deux points de vue :

Définition d’une famille de fonction.

Définition d’une fonction de deux variables 𝐹 : (𝑛, 𝑥) ↦ 𝑥u�𝑒u�

(11)

Et notre famille d’algorithme ?

On pourrait très bien la voir comme un seul algorithme 𝒜′(𝛼, 𝐴, 𝑝) :

1 Précondition : 𝐴 matrice carrée de taille 𝑛 et 𝑝 ∈ ℕ.

2 Si 𝛼 = 0, retourner 𝒜0(𝐴, 𝑝).

3 Si 𝑝 = 0, retourner 𝐼u�. 4 Si 𝑝 = 1, retourner 𝐴.

5 𝑘 ← ⌊𝑝/2⌋

6 𝐵 ← 𝒜(𝛼 − 1, 𝐴, 𝑘) 7 Si 𝑝 pair:

8 Retourner 𝐵2. 9 Sinon:

10 Retourner 𝐵2 × 𝐴.

(12)

En python

1 def aprime(alpha, A, p):

2 """Calcul de A puissance p.

3 Précondition : A matrice carrée numpy, p entier naturel, 4 alpha entier naturel.

5 Postcondition : retourne A puissance p.

6 Algorithme utilisé : algorithme appelé A alpha dans le cours."""

7 if alpha == 0: return puissance_naive(A, p) 8 if p == 0: return id(A)

9 if p == 1: return A 10 k = p // 2

11 B = aprime(alpha-1, A, k) 12 if p % 2 == 0:

13 return mult(B, B)

14 else:

15 return mult(mult(B, B), A)

NB : id(A) retourne la matrice identité de mêmes dimensions que A.

(13)

Questions

Et ça marche ? Ben oui...

Ça porte même un nom : une fonction récursive.

(14)

Exponentiation rapide

Paramètre 𝛼 : on peut s’en passer.

Algorithme d’exponentiation rapide ℰ(𝐴, 𝑝) :

1 Précondition : 𝐴 matrice carrée de taille 𝑛 et 𝑝 ∈ ℕ.

2 Si 𝑝 = 0, retourner 𝐼u�. 3 Si 𝑝 = 1, retourner 𝐴.

4 𝑘 ← ⌊𝑝/2⌋

5 𝐵 ← ℰ(𝐴, 𝑘) 6 Si 𝑝 pair:

7 Retourner 𝐵2. 8 Sinon:

9 Retourner 𝐵2 × 𝐴.

(15)

Implantation en Python

1 def puiss(A, p):

2 """Calcul de A puissance p.

3 Précondition : A matrice carrée numpy, p entier naturel, 4 alpha entier naturel.

5 Postcondition : retourne A puissance p.

6 Algorithme utilisé : exponentiation rapide."""

7 if p == 0: return id(A) 8 if p == 1: return A

9 k = p // 2

10 B = puiss(A, k) 11 if p % 2 == 0:

12 return mult(B, B)

13 else:

14 return mult(mult(B, B), A)

(16)

Question

Lors de l’exécution de puiss(𝐴,15), on appelle la fonction mult A. 4 fois.

B. 5 fois.

C. 6 fois.

D. 14 fois.

(17)

Questions

Les questions à se poser :

Terminaison Comment justifier que l’appel de puiss(𝐴,𝑛) termine ? Correction Comment justifier que puiss(𝐴, 𝑛) rend la bonne va-

leur ?

Complexité Quelle est la complexité de puiss(𝐴, 𝑛) ? [en nombre de multiplications]

(18)

Terminaison et Correction

Se montre ici par récurrence (forte) sur 𝑛.

(19)

Complexité

Une méthode fonctionnant bien ici :

Montrer que pour tout entier naturel 𝑘, puiss(𝐴,2u�) effectue 𝑘 + 2 multiplications matricielles.

Montrer que pour tout entier 𝑛 appartenant à l’intervalle [[2u�, 2u�+1[[, puiss(𝐴, 𝑛) effectue au plus 2𝑘 + 2 multiplications matricielles.

En déduire que pour tout entier naturel 𝑛, puiss(𝐴,𝑛) effectue au plus 𝑂(log 𝑛) multiplications matricielles.

(20)

Remarque

La méthode d’exponentiation rapide peut aussi se faire par un algorithme purement itératif :

1 Précondition : 𝐴 matrice carrée de taille 𝑛 et 𝑝 ∈ ℕ.

2 𝐵 ← 𝐼u�

3 𝐶 ← 𝐴 4 𝑘 ← 𝑝

5 Tant que 𝑘 > 0:

6 Invariant : 𝐵 × 𝐶u� = 𝐴u�

7 Si 𝑘 impair:

8 𝐵 ← 𝐵 × 𝐶

9 𝐶 ← 𝐶 × 𝐶 10 𝑘 ← ⌊𝑘/2⌋

11 Postcondition: 𝐵 contient 𝐴u�.

(21)

Intérêt des algorithmes récursifs

Plus faciles à écrire (dès qu’on a compris le principe).

Beaucoup,

beaucoup

,

beaucoup

plus faciles à justifier.

Beaucoup d’algorithmes récursifs efficaces n’ont pas vraiment d’équivalent itératif.

(22)

Inconvénient principal : consommation mémoire

À chaque appel de fonction, Python stocke

l’endroit du programme où revenir après l’appel les valeurs des variables de la fonction appelée Ces informations :

sont stockées dans un espace appelé pile

ne sont gardées que le temps de l’appel de la fonction.

La pile est de taille limitée.1

(23)

Questions

Avec les définitions suivantes :

1 def g(n):

2 s = 0

3 for i in range(n):

4 s += f(2)

5 return s

67 def f(n):

8 if n == 0: return 0 9 else return 2 + f(n-1)

Les appels f(10000) et g(5000) A. Retournent tous les deux 20000.

B. Font tous deux déborder la pile.

(24)

Questions (bis)

L’exécution de l’implantation Python de l’algorithme récursif d’exponentiation rapide pour calculer 𝐴u�, où 𝐴 est carrée de taille 𝑛 :

A. Fait déborder la pile si 𝑛 > 1000.

B. Fait déborder la pile si 𝑝 > 1000.

C. Ne fait pas déborder la pile si 𝑛 < 106. D. Ne fait pas déborder la pile si 𝑝 < 106

(25)

Méthode pour écrire un algorithme récursif

Similaire à un raisonnement par analyse/synthèse en mathématiques : Analyse Supposer le problème résolu et en tirer des égalités.

Synthèse Tirer un programme des égalités et montrer sa correction.

(26)

Exemple bateau : la fonction factorielle

Pour tout ∈ ℕ :

𝑛! = { 1 si 𝑛 = 0 ; 𝑛 × (𝑛 − 1)! sinon.

(27)

Algorithme obtenu

1 Fonction fact(n):

2 Précondition : n entier naturel.

3 Postcondition : retourne la factorielle de n.

4 Si n = 0:

5 Retourner 1

6 Sinon:

7 Retourner (fact(n-1) * n).

(28)

Correction

Par récurrence : pout tout 𝑛 ∈ ℕ fact(n) termine.

fact(n) retourne 𝑛!.

(29)

À propos de l’analyse

On aurait pu écrire :

𝑛! = (𝑛 + 1)!𝑛 + 1

mais on aurait obtenu l’algorithme

1 Fonction fact(n):

2 Précondition : n entier naturel.

3 Postcondition : retourne la factorielle de n.

4 Retourner (fact(n+1)/(n+1)).

(30)

Moralité

À l’analyse du problème, identifier :

Des cas simples Pour lesquels on peut donner la réponse sans appel récursif (cas d’arrêt ou cas de base).

Des cas plus com-

plexes Pour lesquels on va exprimer la solution en fonction de cas « plus petits » (en un sens à préciser), pour que la récursion termine.

(31)

Structure type d’un programme récursif

1 Definition f(arguments):

2 Si on est dans un cas d’arrêt:

3 Retourner le résultat.

4 Sinon:

5 Appeler f une ou plusieurs fois sur des cas plus simples.

6 Calculer le résultat final à partir de ces résultats.

7 Retourner le résultat final.

(32)

Correspondance Itératif/Récursif

Algorithme itératif Algorithme récursif Passage au tour de boucle suivant Appel récursif

Test d’arrêt Cas d’arrêt

Invariant de boucle Pré/post-conditions

Le variant de boucle décroît Les appels récursifs se font sur des valeurs plus petites

Effectue les tours de boucle

séquentiellement Peut effectuer plusieurs appels récursifs

(33)

Un exemple important : la recherche dichotomique

1 def milieu(i, j):

2 """Retourne l'indice du milieu de l'intervalle range(i,j)"""

3 n = j - i # nombre d'éléments

4 mil = i + n//2 # n//2 éléments à gauche, n - n//2 - 1 à droite.

5 return mil

67 def dicho(t, i, j, v):

8 """Recherche v dans t[i:j] et retourne k dans range(i,j) 9 tel que t[k]=v. Si un tel k n'existe pas, retourne -1."""

10 n = j - i # nb d'éléments dans l'intervalle 11 if j <= i: return -1 # range(i:j) vide...

12 m = milieu(i, j)

13 if t[m] == v: return m

14 elif: t[m] < v: return dicho(t, m, j, v)

(34)

Complexité

Quelle est la complexité en temps de l’appel de dicho_rec(t, i, j, v) en fonction de 𝑗 − 𝑖,

dans le cas le pire.

(35)

Remarque

Dans ce problème :

complexité en temps dominée par le nombre d’appels récursifs.

Justification : temps 𝑂(1) passé dans chaque appel en dehors du coût de l’appel récursif.

Notons 𝐶(𝑗 −𝑖) le nombre d’appels récursifs à dicho_rec dans le cas le pire.

(36)

Première méthode

Regarder dans des cas simples

Voir comment en déduire un majorant dans le cas général.

Cas simple : 0, 1, 3, 7, 15, ... ? Cas général :

𝐶 croissante

en déduire un encadrement.

(37)

Deuxième méthode

Poser 𝐶′(𝑛) = maxu�≤u� 𝐶(𝑘).

Montrer l’équation 𝐶′(𝑛) ≤ 𝐶′ (⌈u�−12 ⌉) + 1.

Résoudre.

Résultat : 𝐶(𝑛)≤𝐶′(𝑛) = 𝑂(log 𝑛), donc 𝐶(𝑛) = 𝑂(log 𝑛).

(38)

Version itérative

Exercice : écrire la version itérative de la recherche dichotomique.

Démontrer sa correction à l’aide d’un invariant.

Références

Documents relatifs

TS 8 Interrogation 2A 16 septembre 2016 R´epondre aux questions sur la feuille... TS 8 Interrogation 2B 16 septembre 2016 R´epondre aux questions sur

Dans le cadre du problème, cela signifie que la quantité de fonds détenue par l’agence A se rapproche au fil des années de 5 millions d’euros.. D’après le théorème de

[r]

Le point A 7 est alors obtenu, dans le triangle OA B 6 6 , comme pied de la hauteur issue du sommet O (rappelons que dans un triangle équilatéral la hauteur, la bissectrice et

S’il ne présente pas de difficulté particulière, il convient de rédiger les réponses avec soin (raisonnement par récurrence, propriétés de l’intégrale utilisées, …).. On

seulement après quelques années que les valeurs des populations de jeunes animaux et d’animaux adultes seront proches de 270 et 450

Elle converge donc et sa limite α est positive ou nulle.. On en déduit le

[ On utilise le fait qu'un triangle rectangle est inscrit dans le cercle ayant pour diamètre son hypoténuse.. (Voir figure