19.04.2015 Views

Exemples de programmes en python - xavierdupre.fr

Exemples de programmes en python - xavierdupre.fr

Exemples de programmes en python - xavierdupre.fr

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Algorithmes illustrés<br />

Table <strong>de</strong>s matières<br />

1 Calcul d'une intégrale avec la métho<strong>de</strong> <strong>de</strong>s rectangles 5<br />

1.1 Enoncé E1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />

1.2 Programme, explications <strong>de</strong> l'exemple E1 . . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />

2 Tri d'une liste 7<br />

2.1 Enoncé E2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

2.1.1 Tri par sélection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

2.1.2 Tri par fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

2.1.3 Tri par insertion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />

2.2 Programme, explications <strong>de</strong> l'exemple E2 . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />

2.2.1 Démonstration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />

2.2.2 Programme du tri par sélection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />

2.2.3 Programme du tri par fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11<br />

2.2.4 Programme du tri par insertion dichotomique . . . . . . . . . . . . . . . . . . . . . 12<br />

3 Partie <strong>de</strong> dames, tableaux à <strong>de</strong>ux dim<strong>en</strong>sions 13<br />

3.1 Enoncé E3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />

3.2 Programme, explications <strong>de</strong> l'exemple E3 . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />

4 Tracer une ligne sans multiplication ni division 16<br />

4.1 Enoncé E4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16<br />

4.1.1 L'algorithme <strong>de</strong> Bres<strong>en</strong>ham . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16<br />

4.1.2 Ext<strong>en</strong>sion aux ellipses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />

4.1.3 Ext<strong>en</strong>sion aux coniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22<br />

4.2 Programme, explications <strong>de</strong> l'exemple E4 . . . . . . . . . . . . . . . . . . . . . . . . . . . 23<br />

5 Gênes porteurs <strong>de</strong> maladie, simulation <strong>de</strong> la consanguinité 27<br />

5.1 Enoncé E5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />

5.2 Programme, explications <strong>de</strong> l'exemple E5 . . . . . . . . . . . . . . . . . . . . . . . . . . . 28<br />

6 Psychokinèse, les ampoules grill<strong>en</strong>t à distance, les d'att<strong>en</strong>te 29<br />

6.1 Enoncé E6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />

6.2 Programme, explications <strong>de</strong> l'exemple E6 . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

6.2.1 File d'att<strong>en</strong>te, cas M/M/1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

6.2.2 File d'att<strong>en</strong>te, cas M/M/S . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32<br />

1


6.2.3 Retour aux ampoules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33<br />

6.2.4 Programme informatique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35<br />

7 Plus court chemin dans un graphe 38<br />

7.1 Enoncé E7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38<br />

7.2 Programme, explications <strong>de</strong> l'exemple E7 . . . . . . . . . . . . . . . . . . . . . . . . . . . 42<br />

7.2.1 Théorie <strong>de</strong>s graphes, un peu d'histoire . . . . . . . . . . . . . . . . . . . . . . . . . 42<br />

7.2.2 Principe d'optimalité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42<br />

7.2.3 Plus court chemin dans un graphe . . . . . . . . . . . . . . . . . . . . . . . . . . . 43<br />

7.2.4 Coût <strong>de</strong> l'algorithme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

7.2.5 A propos <strong>de</strong>s graphes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

7.2.6 Programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47<br />

8 Voyageur <strong>de</strong> commerce, carte <strong>de</strong> Kohon<strong>en</strong> 53<br />

8.1 Enoncé E8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

8.1.1 Cartes <strong>de</strong> Kohon<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54<br />

8.1.2 Arbre <strong>de</strong> poids minimal et circuit hamiltoni<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . 56<br />

8.1.3 Arbre <strong>de</strong> poids minimum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />

8.1.4 Circuit euléri<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

8.1.5 Circuit hamiltoni<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60<br />

8.1.6 Simplication du circuit hamiltoni<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . 61<br />

8.2 Programme, explications <strong>de</strong> l'exemple E8 . . . . . . . . . . . . . . . . . . . . . . . . . . . 64<br />

8.2.1 Carte <strong>de</strong> Kohon<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64<br />

8.2.2 Construction à partir d'un arbre <strong>de</strong> poids minimum . . . . . . . . . . . . . . . . . 71<br />

9 Filtrage d'un signal sonore 89<br />

9.1 Enoncé E9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

9.1.1 Transformée <strong>de</strong> Fourier discrète . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

9.1.2 Transformée <strong>de</strong> Fourier rapi<strong>de</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

9.1.3 Filtrage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

9.1.4 Horizons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

9.2 Programme, explications <strong>de</strong> l'exemple E9 . . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

10 Images <strong>de</strong> synthèse, lancer <strong>de</strong> rayon 96<br />

10.1 Enoncé E10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96<br />

10.1.1 La scène . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96<br />

10.1.2 Modèle d'illumination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97<br />

10.1.3 Géométrie et sphère . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98<br />

10.1.4 Géométrie et facettes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100<br />

2


10.1.5 Lancer <strong>de</strong> rayon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102<br />

10.1.6 Antialiasing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104<br />

10.1.7 Modèle d'illumination <strong>de</strong> Phong . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

10.1.8 Interpolation <strong>de</strong> Gouraud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

10.1.9 Horizons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106<br />

10.2 Programme, explications <strong>de</strong> l'exemple E10 . . . . . . . . . . . . . . . . . . . . . . . . . . . 106<br />

10.2.1 Fichier : image_synthese_base.py . . . . . . . . . . . . . . . . . . . . . . . . . . 106<br />

10.2.2 Fichier : image_synthese_sphere.py . . . . . . . . . . . . . . . . . . . . . . . . . 113<br />

10.2.3 Fichier : image_synthese_sc<strong>en</strong>e.py . . . . . . . . . . . . . . . . . . . . . . . . . . 114<br />

10.2.4 Fichier : image_synthese_phong.py . . . . . . . . . . . . . . . . . . . . . . . . . . 118<br />

10.2.5 Fichier : image_synthese_facette.py . . . . . . . . . . . . . . . . . . . . . . . . 120<br />

11 Cor<strong>de</strong> susp<strong>en</strong>due 125<br />

11.1 Enoncé E11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125<br />

11.1.1 Equation théorique <strong>de</strong> la cor<strong>de</strong> <strong>en</strong> équilibre . . . . . . . . . . . . . . . . . . . . . . 125<br />

11.1.2 Solution dans un cas particulier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />

11.1.3 Simulation informatique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />

11.2 Programme, explications <strong>de</strong> l'exemple E11 . . . . . . . . . . . . . . . . . . . . . . . . . . . 129<br />

12 Classication à l'ai<strong>de</strong> <strong>de</strong>s plus proches voisins 136<br />

12.1 Enoncé E12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

12.1.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

12.1.2 B+ tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137<br />

12.1.3 R-tree ou Rectangular Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

12.1.4 LAESA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140<br />

12.1.5 Résultats théoriques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

12.2 Programme, explications <strong>de</strong> l'exemple E12 . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

12.2.1 Programmes annexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

12.2.2 Programme <strong>de</strong> classication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150<br />

12.2.3 Optimisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156<br />

13 Optimisation <strong>de</strong> Newton, métho<strong>de</strong>s du gradi<strong>en</strong>t 161<br />

13.1 Enoncé E13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />

13.1.1 Algorithme et converg<strong>en</strong>ce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />

13.1.2 Métho<strong>de</strong> du second ordre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164<br />

13.1.3 Minimisation avec gradi<strong>en</strong>t stochastique . . . . . . . . . . . . . . . . . . . . . . . . 166<br />

13.1.4 Premier exemple : optimisation d'une fonction quadratique . . . . . . . . . . . . . 167<br />

13.1.5 Court rappel sur l'estimateur du maximum <strong>de</strong> vraisemblance . . . . . . . . . . . . 167<br />

3


13.1.6 Second exemple : proportion <strong>de</strong> mâles chez les poissons . . . . . . . . . . . . . . . . 171<br />

13.2 Programme, explications <strong>de</strong> l'exemple E13 . . . . . . . . . . . . . . . . . . . . . . . . . . . 173<br />

13.2.1 Premier exemple, fonction quadratique . . . . . . . . . . . . . . . . . . . . . . . . . 173<br />

13.2.2 Second exemple, proportion <strong>de</strong> mâles et <strong>de</strong> femelles chez les poissons . . . . . . . . 177<br />

13.2.3 Simulation d'une série (X i ) i<br />

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177<br />

13.2.4 Estimation <strong>de</strong>s paramètres avec une métho<strong>de</strong> du premier <strong>de</strong>gré . . . . . . . . . . . 178<br />

13.2.5 Estimation <strong>de</strong>s paramètres avec une métho<strong>de</strong> du second <strong>de</strong>gré . . . . . . . . . . . . 182<br />

4


1 Calcul d'une intégrale avec la métho<strong>de</strong> <strong>de</strong>s rectangles<br />

1.1 Enoncé E1<br />

On cherche à calculer une intégrale <strong>en</strong> utilisant la métho<strong>de</strong>s <strong>de</strong>s rectangles (voir gure 1.1).<br />

Fig. 1.1 Illustration du calcul approché d'une intégrale à l'ai<strong>de</strong> <strong>de</strong> la métho<strong>de</strong> <strong>de</strong>s rectangles.<br />

L'intervalle <strong>de</strong> l'intégrale est noté [a, b] et la fonction à intégrer f. On divise cet intervalle <strong>en</strong> n petits<br />

segm<strong>en</strong>ts et on fait la somme <strong>de</strong>s aires <strong>de</strong>s petits rectangles délimités par l'axe <strong>de</strong>s abscisses et la courbe<br />

<strong>de</strong> la fonction f.<br />

∫ b<br />

a<br />

f(x)dx ≈ b − a<br />

n<br />

n∑<br />

f<br />

i=1<br />

(<br />

a + i b − a )<br />

n<br />

On pourra pr<strong>en</strong>dre par exemple :<br />

a = −2<br />

b = 3<br />

f(x) = xcos(x)<br />

n = 20<br />

1.2 Programme, explications <strong>de</strong> l'exemple E1<br />

1 # -*- coding: cp1252 -*-<br />

2 import math<br />

5


3<br />

4 n = 2000<br />

5 a = -2.0<br />

6 b = 3.0<br />

7 h = (b-a)/n<br />

8<br />

9 sum = 0<br />

10 for i in xrange (1,n):<br />

11 x = a + h * i<br />

12 sum += h * x * math.cos (x)<br />

13<br />

14 print "intégrale : ", sum<br />

15<br />

16 import scipy.integrate as integral # on importe le module d'intégration<br />

17 <strong>de</strong>f fff(x): return x * math.cos (x) # on définit la fonction à intégrer<br />

18 sum = integral.quad (fff, a,b, args=()) [0] # on appelle la fonction d'intégration<br />

19 print "intégrale avec scipy : ", sum # on affiche le résultat<br />

Le module SciPy 1 propose <strong>de</strong> nombreux algorithmes mathématiques telles que le calcul d'intégrales. Le<br />

programme précé<strong>de</strong>nt se résume alors à quelques lignes.<br />

import scipy.integrate as integral # on importe le module d'intégration<br />

<strong>de</strong>f fff(x): return x * math.cos (x) # on définit la fonction à intégrer<br />

sum = integral.quad (fff, a,b, args=()) [0] # on appelle la fonction d'intégration<br />

print "intégrale avec scipy : ", sum # on affiche le résultat<br />

Le programme ache les lignes suivantes :<br />

intégrale : -1.96640795695<br />

intégrale avec scipy : -1.96908048953<br />

n correction exemple E1<br />

⊓⊔<br />

1 Le module SciPy propose <strong>de</strong> nombreux algorithmes, algorithmes <strong>de</strong> classication, intégration, optimisation, génération<br />

<strong>de</strong> nombres aléatoires, traitem<strong>en</strong>t du signal, interpolation, algorithmes génétiques, algèbre linéaire, transformée <strong>de</strong> Fourier,<br />

<strong>de</strong>ssin <strong>de</strong> courbe 2D, 3D, adresse : http ://www.scipy.org/<br />

6


2 Tri d'une liste<br />

2.1 Enoncé E2<br />

Pour trier une liste par ordre croissant, il sut d'utiliser la métho<strong>de</strong> sort associée aux listes.<br />

x = [1,3,2,5,8,4,9,0,7,6]<br />

x.sort ()<br />

print x<br />

Cep<strong>en</strong>dant, le tri est très courant <strong>en</strong> informatique et il est parfois utile <strong>de</strong> s'inspirer <strong>de</strong> telles métho<strong>de</strong>s pour<br />

résoudre <strong>de</strong>s problèmes similaires. On distingue principalem<strong>en</strong>t <strong>de</strong>ux familles d'algorithmes qui permett<strong>en</strong>t<br />

<strong>de</strong> trier un <strong>en</strong>semble. La première, la plus simple, inclus notemm<strong>en</strong>t le tri par sélection. Les algorithmes<br />

<strong>de</strong> cette classe ont un coût <strong>en</strong> O(n 2 ) où n est le nombre d'élém<strong>en</strong>ts à trier. La secon<strong>de</strong> classe d'algorithmes<br />

inclut le tri rapi<strong>de</strong> ou quicksort qui est détaillé dans le TD 4. Elle inclut égalem<strong>en</strong>t le tri par fusion décrit<br />

au paragraphe 2.1.2 ou le tri par insertion dichotomique décrit au paragraphe 2.1.3. Le point commun <strong>de</strong><br />

ces algorithmes est leur coût qui est <strong>en</strong> O(n ln n).<br />

2.1.1 Tri par sélection<br />

Bi<strong>en</strong> que moins performant que d'autres algorithmes <strong>de</strong> tri, le tri par sélection est pourtant souv<strong>en</strong>t utilisé<br />

sur <strong>de</strong> petits <strong>en</strong>sembles parce qu'il est très simple et très court à programmer.<br />

Algorithme 2.1 : tri par sélection<br />

On suppose qu'on possè<strong>de</strong> une suite nie quelconque <strong>de</strong> n élém<strong>en</strong>ts (u 1 , . . . , u n ). On pose l ←− n.<br />

Etape A : recherche du maximum<br />

On détermine l'indice i ∗<br />

<strong>de</strong> l'élém<strong>en</strong>t le plus grand inclus dans la suite<br />

(u 1 , . . . , u l ) : i ∗ = arg max {u i | i ∈ {1, . . . , l}}.<br />

Etape B : échange<br />

On échange les positions <strong>de</strong>s élém<strong>en</strong>ts u l et u i ∗. Puis on décrém<strong>en</strong>te l ←− l −1.<br />

Si l > 1, on retourne à l'étape A, dans le cas contraire, le tri est terminé.<br />

Ce tri s'appelle tri par sélection car, à chaque étape B, le plus grand élém<strong>en</strong>t remonte vers le haut <strong>de</strong> la<br />

liste, vers la surface. On peut se <strong>de</strong>man<strong>de</strong>r si, après l'application <strong>de</strong> cet algorithme, la liste est eectivem<strong>en</strong>t<br />

triée et comm<strong>en</strong>t cet algorithme serait implém<strong>en</strong>té <strong>en</strong> langage Python. Le coût <strong>de</strong> cet algorithme est <strong>en</strong><br />

O(n 2 ).<br />

2.1.2 Tri par fusion<br />

Le tri par fusion applique le principe diviser pour régner. Etant données <strong>de</strong>ux suites d'élém<strong>en</strong>ts triés, <strong>de</strong><br />

longueurs respectives l 1 et l 2 , il est très facile d'obt<strong>en</strong>ir une troisième suite d'élém<strong>en</strong>ts triés <strong>de</strong> longueur l 1<br />

7


+ l 2 , par interclassem<strong>en</strong>t ou fusion <strong>de</strong>s <strong>de</strong>ux précé<strong>de</strong>ntes suites.<br />

Algorithme 2.2 : fusion <strong>de</strong> <strong>de</strong>ux listes triées<br />

On suppose que (a 1 , . . . , a l1 ) et (b 1 , . . . , b l2 ) sont <strong>de</strong>ux listes triées dans l'ordre croissant. L'objectif<br />

<strong>de</strong> l'algorithme est <strong>de</strong> construire la liste (c 1 , . . . , c l1 +l 2<br />

) représ<strong>en</strong>te la fusion <strong>de</strong>s <strong>de</strong>ux listes<br />

précé<strong>de</strong>ntes <strong>de</strong> sorte que cette liste soit triée dans l'ordre croissant.<br />

i ←− 1<br />

j ←− 1<br />

k ←− 1<br />

tant que (k l 1 + l 2 ) faire<br />

si j > l 2 ou (i l 1 et a i b j )<br />

c k ←− a i<br />

i ←− i + 1<br />

k ←− k + 1<br />

sinon<br />

ck ←− b j<br />

j ←− j + 1<br />

k ←− k + 1<br />

n si<br />

n tant que<br />

Pour trier un <strong>en</strong>semble E <strong>en</strong> utilisant la métho<strong>de</strong> du tri par fusion, on trie d'abord toutes les paires<br />

d'élém<strong>en</strong>ts <strong>de</strong> E. On fusionne <strong>en</strong>semble ces paires d'élém<strong>en</strong>ts <strong>de</strong>ux par <strong>de</strong>ux pour obt<strong>en</strong>ir <strong>de</strong>s <strong>en</strong>sembles<br />

<strong>de</strong> quatre élém<strong>en</strong>ts. On fusionne à nouveau <strong>de</strong>s groupes <strong>de</strong> quatre élém<strong>en</strong>ts <strong>de</strong>ux par <strong>de</strong>ux pour obt<strong>en</strong>ir<br />

<strong>de</strong>s groupes <strong>de</strong> huit élém<strong>en</strong>ts. On réitère le processus jusqu'à ce que l'<strong>en</strong>semble E soit trié, ce qui mène à<br />

l'algorithme suivant.<br />

Algorithme 2.3 : tri par fusion<br />

On suppose que (a 1 , . . . , a l ) est une liste qu'il faut trier par ordre croissant.<br />

On suppose que l > 1.<br />

n ←− 1<br />

tant que (n < l) faire<br />

i ←− 1<br />

tant que (i l) faire<br />

m ←− min (l, i + n)<br />

b ←− min (l, m + n)<br />

si m < b<br />

On utilise l'algorithme 2.2 <strong>de</strong> fusion <strong>de</strong> listes sur les <strong>de</strong>ux sous-listes<br />

(a i , . . . , a m ) et (a m+1 , . . . , a b ). La liste fusionnée est la liste (t i , . . . , t b ).<br />

pour j = i à b faire<br />

a j ←− t j<br />

n pour<br />

n si<br />

i ←− b + 1<br />

n tant que<br />

n ←− n ∗ 2<br />

n tant que<br />

Le coût <strong>de</strong> cet algorithme est <strong>en</strong> O(n ln n). Cep<strong>en</strong>dant, l'algorithme 2.2 implique <strong>de</strong> conserver <strong>en</strong> mémoire<br />

un tableau d'indices le temps <strong>de</strong> la fusion et ce tableau temporaire est <strong>de</strong> même taille que la liste fusionnée.<br />

Cette contraire n'existe pas avec le tri par sélection ou le tri rapi<strong>de</strong> mais le coût <strong>de</strong> ces algorithmes est<br />

8


supérieur à celui du tri par fusion.<br />

2.1.3 Tri par insertion<br />

Le tri par insertion nécessite lui aussi l'utilisation d'un tableau intermédiaire. On pr<strong>en</strong>d un par un les<br />

élém<strong>en</strong>ts <strong>de</strong> la liste <strong>de</strong> départ pour les insérer dans une autre liste, initialem<strong>en</strong>t vi<strong>de</strong>, <strong>de</strong> sorte que celle-ci<br />

reste constamm<strong>en</strong>t triée. On vi<strong>de</strong> la première liste pour emplir la secon<strong>de</strong>. A chaque insertion d'un nouvel<br />

élém<strong>en</strong>t dans la liste triée, il faut chercher la position adéquate pour qu'elle reste triée. Cette étape utilise<br />

une recherche dichotomique.<br />

Algorithme 2.4 : recherche dichotomique<br />

Soit l = (l 1 , . . . , l n ) une liste supposée triée par ordre croissant. On cherche à déterminer la<br />

position d'insertion d'un nouvel élém<strong>en</strong>t x <strong>de</strong> sorte que la liste l reste triée.<br />

a ←− 1<br />

b ←− n<br />

tant que (a b) faire<br />

m = ⌊ ⌋<br />

a+b<br />

2<br />

si x = l m<br />

a ←− m<br />

b ←− m<br />

sinon si x < lm<br />

b ←− m − 1<br />

sinon<br />

a ←− m + 1<br />

n si<br />

n si<br />

n tant que<br />

x doit être inséré <strong>en</strong>tre les élém<strong>en</strong>ts l a−1 et l a . La position cherchée est a.<br />

On utilise l'algorithme suivant pour trier une liste par insertion dichotomique.<br />

Algorithme 2.5 : tri par insertion dichotomique<br />

On suppose que (l 1 , . . . , l n ) est une liste qu'il faut trier par ordre croissant. On suppose que<br />

n > 1. Le résultat cherchée est la liste c.<br />

c ←− (l 1 )<br />

pour i = 2 à n faire<br />

m est la position déterminée par l'algorithme 2.4 <strong>de</strong> recherche dichotomique<br />

appliquée à l'élém<strong>en</strong>t l i et la liste c. On insère l'élém<strong>en</strong>t l m dans la liste c <strong>en</strong>tre<br />

les élém<strong>en</strong>ts d'indice m − 1 et m.<br />

n pour<br />

Le coût d'une recherche dichotomique est <strong>en</strong> O(ln n) où n est le cardinal <strong>de</strong> l'<strong>en</strong>semble <strong>de</strong> recherche. Le<br />

coût d'un algorithme par insertion dichotomique est <strong>en</strong> O(n ln n).<br />

9


2.2 Programme, explications <strong>de</strong> l'exemple E2<br />

2.2.1 Démonstration<br />

La démonstration que l'algorithme 2.1 trie la suite (u 1 , . . . , u n ) s'eectue par récurr<strong>en</strong>ce. Cet algorithme<br />

permet bi<strong>en</strong> <strong>de</strong> classer un <strong>en</strong>semble composé d'un seul élém<strong>en</strong>t puisque tout <strong>en</strong>semble d'un élém<strong>en</strong>t est trié.<br />

On suppose maint<strong>en</strong>ant que l'algorithme peut trier une suite <strong>de</strong> n − 1 élém<strong>en</strong>ts et on cherche à démontrer<br />

que l'algorithme peut trier une suite <strong>de</strong> n élém<strong>en</strong>ts.<br />

Le premier passage par les étapes A et B permet <strong>de</strong> décomposer la suite (u 1 , . . . , u n ) <strong>en</strong> <strong>de</strong>ux sous-suites.<br />

La première suite (v k ) 1kn−1 est composée <strong>de</strong> n − 1 élém<strong>en</strong>ts, la secon<strong>de</strong> suite (w k ) k=1 conti<strong>en</strong>t un seul<br />

élém<strong>en</strong>t, supérieur à tous les autres, w 1 = arg max {u n | n ∈ {1, . . . , n}}.<br />

Les n − 1 passages suivant par les étapes A et B correspon<strong>de</strong>nt à l'application <strong>de</strong> l'algorithme 2.1 sur une<br />

suite <strong>de</strong> n − 1 élém<strong>en</strong>ts, ici (v k ) 1kn−1 . D'après l'hypothèse <strong>de</strong> récurr<strong>en</strong>ce, l'algorithme trie correctem<strong>en</strong>t<br />

cette suite (v k ) 1kn−1 pour donner la suite ordonnée (v ∗ k ) 1kn−1 vériant v ∗ 1 v∗ 2 ... v∗ n−1 . Etant<br />

donné que ∀k, w 1 v ∗ k , la suite ( v ∗ 1 , v∗ 2 , ..., v∗ n−1 , w 1)<br />

est aussi une suite ordonnée dont les élém<strong>en</strong>ts sont<br />

ceux <strong>de</strong> la suite (u 1 , . . . , u n ).<br />

2.2.2 Programme du tri par sélection<br />

1 # -*- coding: cp1252 -*-<br />

2 import random<br />

3<br />

4 <strong>de</strong>f construit_suite(n):<br />

5 """construit une liste <strong>de</strong> n nombres <strong>en</strong>tiers compris <strong>en</strong>tre 0 et 99"""<br />

6 l = []<br />

7 for i in range(0,n):<br />

8 l.app<strong>en</strong>d (random.randint(0,100))<br />

9 return l<br />

10<br />

11 <strong>de</strong>f tri_selection(l):<br />

12 """ tri une liste l, tri par sélection"""<br />

13 # première boucle, répétition <strong>de</strong>s étapes A et B<br />

14 for i in range(0,l<strong>en</strong>(l)):<br />

15<br />

16 # recherche du maximum, on suppose pour comm<strong>en</strong>cer<br />

17 # qu'il est à la position 0<br />

18 pos = 0<br />

19 # boucle <strong>de</strong> l'étape A<br />

20 for j in range(1,l<strong>en</strong>(l)-i):<br />

21 if l [pos] < l [j]: pos = j<br />

22<br />

23 # échange <strong>de</strong> l'étape B<br />

24 # la position du maximum est conservé dans la variable pos<br />

25 # on fait l'échange avec l'élém<strong>en</strong>t à la position l<strong>en</strong>(l)-i-1<br />

26 k = l<strong>en</strong>(l)-i-1<br />

27 ech = l [k]<br />

28 l [k] = l [pos]<br />

29 l [pos] = ech<br />

10


30<br />

31 l = construit_suite(8) # création d'une suite aléatoirem<strong>en</strong>t<br />

32 print "liste non triée :\t",l # affichage<br />

33 tri_selection (l) # tri<br />

34 print "liste triée :\t",l # affichage<br />

2.2.3 Programme du tri par fusion<br />

1 # -*- coding: cp1252 -*-<br />

2 import random<br />

3<br />

4 <strong>de</strong>f construit_suite(n):<br />

5 """construit une liste <strong>de</strong> n nombres <strong>en</strong>tiers compris <strong>en</strong>tre 0 et 99"""<br />

6 l = []<br />

7 for i in range(0,n):<br />

8 l.app<strong>en</strong>d (random.randint(0,100))<br />

9 return l<br />

10<br />

11 <strong>de</strong>f fusion_liste (l1,l2):<br />

12 """fusionne <strong>de</strong>ux listes l1 et l2 triées par ordre croissant<br />

13 <strong>de</strong> sorte que la liste résultante soit égalem<strong>en</strong>t triée"""<br />

14 i,j,k = 0,0,0<br />

15 fin = l<strong>en</strong> (l1) + l<strong>en</strong> (l2)<br />

16 l = []<br />

17 while k < fin :<br />

18 if j >= l<strong>en</strong> (l2) or (i < l<strong>en</strong> (l1) and l1 [i] < l2 [j]) :<br />

19 l.app<strong>en</strong>d (l1 [i])<br />

20 k += 1<br />

21 i += 1<br />

22 else :<br />

23 l.app<strong>en</strong>d (l2 [j])<br />

24 k += 1<br />

25 j += 1<br />

26 return l<br />

27<br />

28 <strong>de</strong>f tri_fusion(l):<br />

29 """ tri une liste l par fusion"""<br />

30 if l<strong>en</strong> (l)


43 print l [0:3]<br />

44 print "liste non triée :\t",l # affichage<br />

45 tri_fusion (l) # tri<br />

46 print "liste triée :\t",l # affichage<br />

2.2.4 Programme du tri par insertion dichotomique<br />

1 # -*- coding: cp1252 -*-<br />

2 import random<br />

3<br />

4 <strong>de</strong>f construit_suite(n):<br />

5 """construit une liste <strong>de</strong> n nombres <strong>en</strong>tiers compris <strong>en</strong>tre 0 et 99"""<br />

6 l = []<br />

7 for i in range(0,n):<br />

8 l.app<strong>en</strong>d (random.randint(0,100))<br />

9 return l<br />

10<br />

11 <strong>de</strong>f recherche_dichotomique (l,x):<br />

12 """cherche l'élém<strong>en</strong>t x dans la liste l, la liste l est<br />

13 supposée être triée par ordre croissant"""<br />

14 a = 0<br />

15 b = l<strong>en</strong>(l)-1<br />

16 while a


3 Partie <strong>de</strong> dames, tableaux à <strong>de</strong>ux dim<strong>en</strong>sions<br />

3.1 Enoncé E3<br />

Une partie <strong>de</strong> dames met <strong>en</strong> jeu quarante pions, vingt noirs, vingt blancs, chacun sur <strong>de</strong>s cases diér<strong>en</strong>tes<br />

ou i<strong>de</strong>ntiques si <strong>de</strong>ux d'<strong>en</strong>tre eux form<strong>en</strong>t une dame. L'objectif est <strong>de</strong> savoir si un pion est <strong>en</strong> mesure d'<strong>en</strong><br />

pr<strong>en</strong>dre un autre. Chaque pion est déni par :<br />

<strong>de</strong>ux coordonnées <strong>en</strong>tières, chacune comprise <strong>en</strong>tre 1 et 10<br />

une couleur, noir ou blanc<br />

1) On propose <strong>de</strong>ux représ<strong>en</strong>tations <strong>de</strong> l'<strong>en</strong>semble <strong>de</strong> pions :<br />

1. Un tableau <strong>de</strong> 40 pions, chaque pion étant déni par :<br />

<strong>de</strong>ux coordonnées comprise <strong>en</strong>tre 1 et 10, ou (0,0) si le pion n'est plus sur le damier<br />

un <strong>en</strong>tier qui vaut 1 pour blanc, 2 pour noir<br />

2. Un tableau d'<strong>en</strong>tiers à <strong>de</strong>ux dim<strong>en</strong>sions, chaque case conti<strong>en</strong>t :<br />

soit 0 s'il n'y a pas <strong>de</strong> pions<br />

soit 1 si la case conti<strong>en</strong>t un pion blanc<br />

soit 2 si la case conti<strong>en</strong>t un pion noir<br />

Y a-t-il d'autres représ<strong>en</strong>tations <strong>de</strong> ces informations ? Si on considère que l'ecacité d'une métho<strong>de</strong> est<br />

reliée à sa vitesse - autrem<strong>en</strong>t dit aux coûts <strong>de</strong>s algorithmes qu'elles utilis<strong>en</strong>t -, parmi ces <strong>de</strong>ux représ<strong>en</strong>tations,<br />

quelle est celle qui semble la plus ecace pour savoir si un pion donné du damier est <strong>en</strong> mesure<br />

d'<strong>en</strong> pr<strong>en</strong>dre un autre ? La réponse dép<strong>en</strong>d-elle du langage d'implém<strong>en</strong>tation ?<br />

2) Comm<strong>en</strong>t représ<strong>en</strong>ter un tableau d'<strong>en</strong>tiers à <strong>de</strong>ux dim<strong>en</strong>sions <strong>en</strong> langage Python à l'ai<strong>de</strong> <strong>de</strong>s types<br />

standards qu'il propose, à savoir t-uple, liste ou dictionnaire ?<br />

3) On cherche à écrire l'algorithme qui permet <strong>de</strong> savoir si un pion donné est un mesure <strong>de</strong> pr<strong>en</strong>dre un<br />

pion. Quelles les paramètres d'<strong>en</strong>trées et les résultats <strong>de</strong> cet algorithme ? Il ne reste plus qu'à écrire cet<br />

algorithme.<br />

3.2 Programme, explications <strong>de</strong> l'exemple E3<br />

1) La secon<strong>de</strong> représ<strong>en</strong>tation sous forme <strong>de</strong> tableau à <strong>de</strong>ux dim<strong>en</strong>sions est plus pratique pour eectuer les<br />

tests <strong>de</strong> voisinages. Avec la première représ<strong>en</strong>tation - le tableau <strong>de</strong>s pions - pour savoir s'il existe un pion<br />

dans une case donnée, il faut rechercher dans ce tableau s'il existe un pion ayant pour coordonnées celles<br />

<strong>de</strong> cette case. Avec la secon<strong>de</strong> représ<strong>en</strong>tation - le tableau à <strong>de</strong>ux dim<strong>en</strong>sions - on accè<strong>de</strong> directem<strong>en</strong>t à<br />

cette information sans avoir à la rechercher.<br />

2) Pour représ<strong>en</strong>ter le tableau <strong>en</strong> <strong>de</strong>ux dim<strong>en</strong>sions, il existe trois solutions :<br />

1. Une seule liste, il sut <strong>de</strong> numéroter les cases du damier <strong>de</strong> 0 à 99, <strong>en</strong> utilisant comme indice pour la<br />

case (i, j) : k = 10 ∗ i + j. Réciproquem<strong>en</strong>t, la case d'indice k aura pour coordonnées (k/10, k%10).<br />

2. Une liste <strong>de</strong> listes, chaque ligne est représ<strong>en</strong>tée par une liste. Toutes ces listes sont elles-mêmes<br />

assemblées dans une liste globale.<br />

3. Un dictionnaire dont la clé est un couple d'<strong>en</strong>tiers.<br />

13


3) On désire savoir si le pion <strong>de</strong> la case (i, j) peut <strong>en</strong> pr<strong>en</strong>dre un autre. On suppose que le tableau à <strong>de</strong>ux<br />

dim<strong>en</strong>sions est une liste <strong>de</strong> dix listes appelées damier. damier[i][j] est donc la couleur du pion <strong>de</strong> la case<br />

(i, j), à savoir 0 si la case est vi<strong>de</strong>, 1 si le pion est blanc, 2 si le pion est noir. Pour ces <strong>de</strong>ux <strong>de</strong>rniers cas,<br />

la couleur <strong>de</strong>s pions <strong>de</strong> l'adversaire sera donc 3 − damier[i][j].<br />

1 # -*- coding: cp1252 -*-<br />

2 <strong>de</strong>f pion_pr<strong>en</strong>dre(i,j,damier):<br />

3 c = damier [i][j]<br />

4 if c == 0: return False # case vi<strong>de</strong>, impossible <strong>de</strong> pr<strong>en</strong>dre<br />

5 c = 3 - c # couleur <strong>de</strong> l'adversaire<br />

6<br />

7 if damier [i-1][j-1] == c : # s'il y un pion adverse <strong>en</strong> haut à gauche<br />

8 if damier [i-2][j-2] == 0 : # si la case d'après <strong>en</strong> diagonale est vi<strong>de</strong><br />

9 return True #on peut pr<strong>en</strong>dre<br />

10<br />

11 # on répète ce test pour les trois autres cases<br />

12 if (damier [i-1][j+1] == c) and (damier [i-2][j+2] == 0):<br />

13 return True<br />

14 if (damier [i+1][j-1] == c) and (damier [i+2][j-2] == 0):<br />

15 return True<br />

16 if (damier [i+1][j+1] == c) and (damier [i+2][j+2] == 0):<br />

17 return True<br />

18<br />

19 # si tous les tests ont échoué, on ne peut pas pr<strong>en</strong>dre<br />

20 return False<br />

Voici une fonction équival<strong>en</strong>te lorsque le damier est un dictionnaire dont la clé est un couple d'<strong>en</strong>tiers.<br />

1 # -*- coding: cp1252 -*-<br />

2 <strong>de</strong>f pion_pr<strong>en</strong>dre(i,j,damier):<br />

3 c = damier [(i,j)]<br />

4 if c == 0: return False # case vi<strong>de</strong>, impossible <strong>de</strong> pr<strong>en</strong>dre<br />

5 c = 3 - c # couleur <strong>de</strong> l'adversaire<br />

6<br />

7 if damier [(i-1,j-1)] == c : # s'il y un pion adverse <strong>en</strong> haut à gauche<br />

8 if damier [(i-2,j-2)] == 0 : # si la case d'après <strong>en</strong> diagonale est vi<strong>de</strong><br />

9 return True #on peut pr<strong>en</strong>dre<br />

10<br />

11 # on répète ce test pour les trois autres cases<br />

12 if (damier [(i-1,j+1)] == c) and (damier [(i-2,j+2)] == 0):<br />

13 return True<br />

14 if (damier [(i+1,j-1)] == c) and (damier [(i+2,j-2)] == 0):<br />

15 return True<br />

16 if (damier [(i+1,j+1)] == c) and (damier [(i+2,j+2)] == 0):<br />

17 return True<br />

18<br />

19 # si tous les tests ont échoué, on ne peut pas pr<strong>en</strong>dre<br />

20 return False<br />

La même fonction lorsque le damier est représ<strong>en</strong>té par une seule liste.<br />

14


1 # -*- coding: cp1252 -*-<br />

2 <strong>de</strong>f pion_pr<strong>en</strong>dre(i,j,damier):<br />

3 c = damier [10*i+j]<br />

4 if c == 0: return False # case vi<strong>de</strong>, impossible <strong>de</strong> pr<strong>en</strong>dre<br />

5 c = 3 - c # couleur <strong>de</strong> l'adversaire<br />

6<br />

7 if damier [10*(i-1)+j-1] == c : # s'il y un pion adverse <strong>en</strong> haut à gauche<br />

8 if damier [10*(i-2)+j-2] == 0 : # si la case d'après <strong>en</strong> diagonale est vi<strong>de</strong><br />

9 return True #on peut pr<strong>en</strong>dre<br />

10<br />

11 # on répète ce test pour les trois autres cases<br />

12 if (damier [10*(i-1)+j+1] == c) and (damier [10*(i-2)+j+2] == 0):<br />

13 return True<br />

14 if (damier [10*(i+1)+j-1] == c) and (damier [10*(i+2)+j-2] == 0):<br />

15 return True<br />

16 if (damier [10*(i+1)+j+1] == c) and (damier [10*(i+2)+j+2] == 0):<br />

17 return True<br />

18<br />

19 # si tous les tests ont échoué, on ne peut pas pr<strong>en</strong>dre<br />

20 return False<br />

Pour ces trois cas, aucun eet <strong>de</strong> bord n'a été <strong>en</strong>visagé. Si la case est trop près d'un <strong>de</strong>s bords, un <strong>de</strong>s<br />

indices i, j, i − 1, j − 1, i + 1, j + 1, i − 2, j − 2, i + 2, j + 2 désignera une case hors du damier. Le co<strong>de</strong><br />

<strong>de</strong> la fonction pion_pr<strong>en</strong>dre <strong>de</strong>vra donc vérier que chaque case dont elle vérie le cont<strong>en</strong>u apparti<strong>en</strong>t<br />

au damier.<br />

Une autre option consiste à <strong>en</strong>tourer le damier d'un <strong>en</strong>semble <strong>de</strong> case dont le cont<strong>en</strong>u sera égal à une<br />

constante diér<strong>en</strong>te <strong>de</strong> 0, −1 par exemple. Dans ce cas, si le damier est représ<strong>en</strong>té par une liste <strong>de</strong> listes<br />

ou une seule liste, la première case du damier aura pour coordonnées (1, 1) au lieu <strong>de</strong> (0, 0) car les listes<br />

n'accept<strong>en</strong>t pas les indices négatifs. Ce n'est pas le cas lorsque le damier est représ<strong>en</strong>té par un dictionnaire<br />

car une case peut tout à fait avoir pour coordonnées (−1, −1).<br />

n correction exemple E3<br />

⊓⊔<br />

15


4 Tracer une ligne sans multiplication ni division<br />

4.1 Enoncé E4<br />

L'objectif <strong>de</strong> cet exercice est <strong>de</strong> <strong>de</strong>ssiner une ligne à l'écran <strong>en</strong> n'utilisant que <strong>de</strong>s additions et <strong>de</strong>s soustractions.<br />

4.1.1 L'algorithme <strong>de</strong> Bres<strong>en</strong>ham<br />

Ce problème cache <strong>de</strong>ux dicultés. La première consiste à déterminer un <strong>en</strong>semble <strong>de</strong> pixels voisins (voir<br />

les gures 4.1 et 4.2) uniformém<strong>en</strong>t répartis le long d'une droite. L'<strong>en</strong>semble <strong>de</strong>s pixels sélectionnés doit<br />

aussi être connexe car il doit être possible <strong>de</strong> passer d'une extremité à une autre, sautant d'un pixel vers<br />

un pixel voisin, sans emprunter un pixel ne faisant pas partie du segm<strong>en</strong>t. La secon<strong>de</strong> diculté est <strong>de</strong><br />

se passer <strong>de</strong>s multiplications et <strong>de</strong>s divisions, opérations plus onéreuses <strong>en</strong> temps <strong>de</strong> calcul qu'une simple<br />

addition ou soustraction. A l'époque où les ordinateurs n'étai<strong>en</strong>t pas aussi rapi<strong>de</strong>s qu'aujourd'hui, ce gain<br />

avait son importance.<br />

Fig. 4.1 Tracer une ligne à l'écran revi<strong>en</strong>t à déterminer un <strong>en</strong>semble <strong>de</strong> pixels voisins recouvrant la<br />

droite à <strong>de</strong>ssiner. La ligne <strong>de</strong> cette gure vérie la 4-connexité : <strong>de</strong>ux pixels sont considérés comme voisins<br />

s'ils ont une arête <strong>en</strong> commun.<br />

Fig. 4.2 Même ligne que pour la gure 4.1 mais cette ligne vérie la 8-connexité : <strong>de</strong>ux pixels<br />

sont considérés comme voisins s'ils ont un sommet <strong>en</strong> commun. Par rapport à la ligne <strong>de</strong> la -<br />

gure 4.1 <strong>de</strong>s pixels voisins ont disparu car ils ne sont pas nécessaires pour passer du pixel (0, 0) au<br />

pixel (7, 3) sans perte <strong>de</strong> 8-connexité. Le segm<strong>en</strong>t obt<strong>en</strong>u est décrit par l'<strong>en</strong>semble <strong>de</strong> pixels suivant :<br />

[(0, 0), (1, 0), (2, 1), (3, 1), (4, 2), (5, 2), (6, 3), (7, 3)].<br />

En regardant la gure 4.2, il paraît assez intuitif comme premirèe approche <strong>de</strong> calculer le vecteur directeur<br />

(1, m) <strong>de</strong> la droite puis d'<strong>en</strong> déduire le points <strong>de</strong> passage du segm<strong>en</strong>t pour chaque colonne verticale <strong>de</strong><br />

pixels : y = mx + p. Mais cette métho<strong>de</strong> utilise une multiplication pour le calcul <strong>de</strong>s ordonnées.<br />

L'algorithme <strong>de</strong> Bres<strong>en</strong>ham (voir [Bres<strong>en</strong>ham1965]) propose une solution s'appuyant uniquem<strong>en</strong>t sur <strong>de</strong>s<br />

additions et <strong>de</strong>s soustractions. La gure 4.2 montre un segm<strong>en</strong>t reliant <strong>de</strong>ux pixels obt<strong>en</strong>us par l'algorithme<br />

16


qui suit.<br />

Algorithme 4.1 : tracé d'une ligne (Bres<strong>en</strong>ham)<br />

On suppose qu'on doit tracer la ligne rejoignant les pixels <strong>de</strong> coordonnées (x 1 , y 1 ) à (x 2 , y 2 ). On<br />

suppose <strong>de</strong> plus que : x 2 > x 1 , y 2 y 1 et x 2 − x 1 y 2 − y 1 . Les autres cas sont obt<strong>en</strong>us par<br />

symétrie par rapport aux axes et aux premières bissectrices. On suppose que (x 1 , y 1 , x 2 , y 2 ) ∈ N 4 .<br />

Etape A : initialisation<br />

v x ←− x 2 − x 1<br />

v y ←− y 2 − y 1<br />

β ←− vx 2<br />

y ←− y 1<br />

initialisation<br />

Etape B : étape principale<br />

pour x = x 1 à x 2 faire<br />

<strong>de</strong>ssiner le point <strong>de</strong> coordonnées (x, y)<br />

β ←− β − v y<br />

si β < 0<br />

y ←− y + 1<br />

β ←− β + v x<br />

n si<br />

n pour<br />

On vérie qu'à tout mom<strong>en</strong>t : β = yv x − xv y + vx 2 et 0 β < v x. Ceci implique :<br />

− vx 2<br />

yv x − xv y < vx 2<br />

− 1 2<br />

y − x v y<br />

v x<br />

< 1 2<br />

Autrem<strong>en</strong>t dit, le pixel choisi pour représ<strong>en</strong>ter la droite n'est jamais éloigné <strong>de</strong> plus d'un <strong>de</strong>mi-pixel <strong>de</strong><br />

la vraie droite. L'objectif <strong>de</strong> cet exercice est d'implém<strong>en</strong>ter une fonction qui utilise l'algorithme 4.1 pour<br />

retourner la liste <strong>de</strong>s pixels par lesquels passe une droite reliant les pixels (x 1 , y 1 ) et (x 2 , y 2 ) <strong>en</strong> 8-connexité.<br />

Toutefois avant <strong>de</strong> passer à la partie programmation, il peut être utile <strong>de</strong> s'interroger sur la valeur <strong>de</strong> la<br />

constante choisie comme première valeur <strong>de</strong> la variable β. La gure 4.3 montre la même ligne que celle <strong>de</strong><br />

la gure 4.2 mais obt<strong>en</strong>ue pour <strong>de</strong>ux autres valeurs d'initialisation <strong>de</strong> la variable β. De manière imagée,<br />

lorsque la valeur d'initialisation choisie pour β est vx 2<br />

, le segm<strong>en</strong>t passe par le milieu <strong>de</strong>s pixels.<br />

β = 0<br />

β = v x<br />

Fig. 4.3 Ces <strong>de</strong>ux lignes sont obt<strong>en</strong>us pour <strong>de</strong>ux autres valeurs d'initialisation <strong>de</strong> la variable β. Pour<br />

la première image, β = 0 et l'<strong>en</strong>semble <strong>de</strong>s pixels est légèrem<strong>en</strong>t décalé vers le haut. Lorsque β = v x ,<br />

l'<strong>en</strong>semble <strong>de</strong>s pixels est légèrem<strong>en</strong>t décalé vers le bas. Pour la gure 4.2, β = vx 2 .<br />

17


4.1.2 Ext<strong>en</strong>sion aux ellipses<br />

Bres<strong>en</strong>ham a égalem<strong>en</strong>t décliné une version <strong>de</strong> cet algorithme pour le tracé <strong>de</strong>s arcs circulaires (voir<br />

[Bres<strong>en</strong>ham1977]). Ce paragraphe prés<strong>en</strong>te une version <strong>de</strong> cet algorithme qui permet <strong>de</strong> tracer une ellipse<br />

dont les axes sont confondues avec les axes <strong>de</strong>s abscisses et <strong>de</strong>s ordonnées. L'équation cartési<strong>en</strong>ne d'une<br />

ellipse <strong>de</strong> <strong>de</strong>mi-axe a et b est :<br />

x 2<br />

a 2 + y2<br />

b 2 = 1 (4.1)<br />

A partir <strong>de</strong> cette équation, on <strong>en</strong> déduit l'équation <strong>de</strong> la tang<strong>en</strong>te au point (x 0 , y 0 ) :<br />

x 2x 0<br />

a 2 + y 2y 0<br />

b 2 = 0 ⇐⇒ x x 0 b 2 + y y 0 a 2 = 0 (4.2)<br />

L'algorithme qui suit permet <strong>de</strong> tracer sans multiplication ni division une ellipse dont l'équation (4.1).<br />

Algorithme 4.2 : tracé d'une ellipse (Bres<strong>en</strong>ham)<br />

On suppose qu'on doit tracer l'ellipse d'équation x2<br />

auquel cas, les ellipses sont <strong>de</strong>s segm<strong>en</strong>ts. On suppose (a, b) ∈ N 2 .<br />

Etape A : initialisation<br />

A ←− a 2<br />

B ←− b 2<br />

x ←− a<br />

y ←− 0<br />

v x ←− aB<br />

v y ←− 0<br />

β ←− vx 2<br />

a 2<br />

+ y2<br />

b 2 = 1. On suppose que a ≠ 0 et b ≠ 0<br />

Le point (x, y) est la position du pixel qui fera partie <strong>de</strong> l'ellipse. Le vecteur (v x , v y ) représ<strong>en</strong>te<br />

le vecteur normal à l'ellipse au point (x, y). Ce vecteur est constamm<strong>en</strong>t égal à<br />

(v x , v y ) = (xB, yA) = ( xb 2 , ya 2) .<br />

18


Etape B : tracé du premier huitième<br />

tant que (v x v y ) faire<br />

<strong>de</strong>ssiner le point <strong>de</strong> coordonnées (x, y)<br />

y ←− y + 1<br />

v y ←− v y + A<br />

β ←−<br />

si β < 0<br />

β − v y<br />

x ←− x − 1<br />

v x ←− v x − B<br />

β ←− β + v x<br />

n si<br />

n tant que<br />

Etape C : tracé du second huitième<br />

tant que (x 0) faire<br />

<strong>de</strong>ssiner le point <strong>de</strong> coordonnées (x, y)<br />

x ←− x − 1<br />

v x ←− v x − B<br />

β ←−<br />

si β > 0<br />

β + v x<br />

y ←− y + 1<br />

v y ←− v y + A<br />

β ←− β − v y<br />

n si<br />

n tant que<br />

Etape D : autres huitièmes<br />

Le reste <strong>de</strong> l'ellipse est obt<strong>en</strong>u par symétrie par rapport aux <strong>de</strong>ux axes du<br />

répère orthonormé du plan.<br />

Avant <strong>de</strong> démontrer pourquoi le tracé <strong>de</strong> l'ellipse est bi<strong>en</strong> obt<strong>en</strong>u, on vérie que dès que x ou y sont<br />

modiés, v x et v y sont égalem<strong>en</strong>t modiés <strong>de</strong> sorte que (v x , v y ) = ( xb 2 , ya 2) . La gure 4.4 explique<br />

pourquoi l'algorithme conti<strong>en</strong>t <strong>de</strong>ux étapes <strong>de</strong> tracé. L'étape B incrém<strong>en</strong>te y à chaque itération. Comme<br />

x ne peut décroître plus vite que y ne croît, cette étape ne peut tracer <strong>de</strong>s courbes dont les tang<strong>en</strong>tes sont<br />

plus verticales qu'horizontales (voir gure 4.5). C'est pourquoi on ajoute une secon<strong>de</strong> étape C symétrique<br />

<strong>de</strong> la première.<br />

Fig. 4.4 Algorithme 4.2, les <strong>de</strong>ux premiers huitièmes sont scindés <strong>en</strong> <strong>de</strong>ux parties. La partie 1 pour<br />

laquelle v x > v y , c'est-à-dire qu'on se déplace plus souv<strong>en</strong>t vers le haut que vers la gauche. y croît à chaque<br />

itération <strong>de</strong> l'étape B tandis que x ne décroît qu'occasionnellem<strong>en</strong>t. C'est l'inverse pour la partie 2, v x < v y<br />

et x décroît à chaque itération <strong>de</strong> l'étape C tandis que y ne croît qu'occasionnellem<strong>en</strong>t.<br />

La première partie <strong>de</strong> l'ellipse (zone 1 <strong>de</strong> la gure 4.4), la normale à l'ellipse dirigée par le vecteur (v x , v y )<br />

19


Fig. 4.5 Algorithme 4.2, si le premier quart était tracé par l'étape B seule, le tracé suivrait bi<strong>en</strong> l'ellipse<br />

pour comm<strong>en</strong>cer puis suivrait <strong>en</strong>suite la droite bleue <strong>de</strong> vecteur directeur (−1, 1) et tang<strong>en</strong>te à l'ellipse.<br />

vérie v x > v y . Ce cas est traité par l'étape B <strong>de</strong> l'algorithme 4.2. On réécrit cette étape <strong>en</strong> fonction d'un<br />

indice t égal au nombre d'itérations :<br />

y(t + 1) = y(t) + 1 et y(0) = 0 (4.3)<br />

v y (t + 1) = v y (t) + a 2 et v y (0) = 0 (4.4)<br />

{ x(t) si β(t) − vy (t + 1) 0<br />

x(t + 1) =<br />

(4.5)<br />

x(t) − 1 sinon<br />

{<br />

vx (t) si β(t) − v<br />

v x (t + 1) =<br />

y (t + 1) 0<br />

v x (t) − b 2<br />

et v<br />

sinon<br />

x (0) = a b 2 (4.6)<br />

{ β(t) − vy (t + 1) si β(t) − v<br />

β(t + 1) =<br />

y (t + 1) 0<br />

et β(0) = v x(0)<br />

(4.7)<br />

β(t) − v y (t + 1) + v x (t + 1) sinon<br />

2<br />

Tout d'abord, les équations (4.4) et (4.6) nous permett<strong>en</strong>t <strong>de</strong> vérier que :<br />

De plus, on sait qu'à tout instant :<br />

∀t, v x (t) = b 2 x(t) et v y (t) = y(t) a 2<br />

0 β(t) v x (t + 1)<br />

=⇒ 0 β(t) b 2 x(t) (4.8)<br />

Tout repose sur la variable β. L'objectif <strong>de</strong>s lignes qui suiv<strong>en</strong>t est <strong>de</strong> compr<strong>en</strong>dre ce que β représ<strong>en</strong>te. Les<br />

équations (4.3) à (4.7) permett<strong>en</strong>t d'écrire que :<br />

β(t + 1) = β(t) − v y (t + 1) [y(t + 1) − y(t)] − v x (t + 1) [x(t + 1) − x(t)]<br />

⇐⇒ β(t + 1) − β(t) = −y(t + 1)a 2 [y(t + 1) − y(t)] − x(t + 1)b 2 [x(t + 1) − x(t)]<br />

( ) ( )<br />

x(t + 1)b<br />

2 x(t + 1) − x(t)<br />

⇐⇒ β(t + 1) − β(t) =<br />

y(t + 1)a 2 ·<br />

y(t + 1) − y(t)<br />

(4.9)<br />

L'équation (4.9) permet d'armer que :<br />

20


∑t+1<br />

( x(i + 1)b<br />

2<br />

β(t + 1) − β(0) =<br />

i=1<br />

y(i + 1)a 2 )<br />

·<br />

( x(i + 1) − x(i)<br />

y(i + 1) − y(i)<br />

)<br />

(4.10)<br />

On note le point M(t) <strong>de</strong> coordonnées M(t) = (x(t), y(t)) et −−−−−−−→ dM(t + 1) le vecteur −−−−−−−→ dM(t + 1) =<br />

−−−−−−−→<br />

OM(t + 1) − −−−−→ OM(t) = (x(t + 1) − x(t), y(t + 1) − y(t)). L'équation (4.10) peut s'écrire sous la forme :<br />

On dénit maint<strong>en</strong>ant la fonction f par :<br />

∑t+1<br />

( )<br />

x(i + 1)b<br />

2<br />

β(t + 1) − β(0) =<br />

y(i + 1)a 2 · −−−−−−−→ dM(t + 1) (4.11)<br />

i=1<br />

{<br />

L'ellipse correspond à l'<strong>en</strong>semble<br />

f(x, y) = b2 x 2 + a 2 y 2<br />

2<br />

(x, y) ∈ R 2 | f(x, y) = a2 b 2<br />

2<br />

}<br />

. On remarque égalem<strong>en</strong>t que :<br />

(4.12)<br />

( ∂f<br />

∂x , ∂f )<br />

= ( b 2 x, a 2 y )<br />

∂y<br />

(<br />

−−−−−→ ∂f<br />

N(t + 1) =<br />

∂x , ∂f )<br />

(x(t), y(t)) = ( b 2 x(t + 1), a 2 y(t + 1) ) (4.13)<br />

∂y<br />

( )<br />

∂f<br />

∂x , ∂f<br />

∂y<br />

est un vecteur orthonogonal à la courbe <strong>de</strong> niveau <strong>de</strong> f passant par le point (x, y). En regroupant<br />

les équations (4.11) et (4.13), on peut écrire que :<br />

∑t+1<br />

−−−−−→<br />

β(t + 1) − β(0) = N(t + 1) · −−−−−−−→ dM(t + 1) (4.14)<br />

i=1<br />

La gure 4.6 représ<strong>en</strong>te les vecteurs −−−−−→ N(t + 1) et −−−−−−−→ dM(t + 1). β(t) est <strong>en</strong> quelques sorte la somme <strong>de</strong>s écarts<br />

à la normale. L'inéquation (4.8) <strong>de</strong>vi<strong>en</strong>t :<br />

(4.8) ⇐⇒ −β(0) β(t + 1) − β(0) b 2 x(t + 1) − β(0)<br />

⇐⇒ − b2 x(t + 1)<br />

2<br />

⇐⇒ − 1 2<br />

β(t + 1) − β(0) b2 x(t + 1)<br />

2<br />

<br />

β(t + 1) − β(0)<br />

b 2 x(t + 1)<br />

1 2<br />

(4.15)<br />

Dans le cas du tracé d'une droite (algorithme 4.1), le vecteur −−−−−→ N(t + 1) est constant et l'équation (4.14)<br />

peut être simpliée. Pour une ellipse, ce vecteur est diér<strong>en</strong>t à chaque nouvelle position M(t + 1).<br />

On sait que −−−−−→ N(t + 1) =<br />

( x(t + 1)b<br />

2<br />

y(t + 1)a 2 )<br />

. De plus pour la première partie <strong>de</strong> la courbe (zone 1 <strong>de</strong> la<br />

gure 4.4), on sait que x(t + 1)b 2 > y(t + 1)a 2 . On <strong>en</strong> déduit que :<br />

21


Fig. 4.6 La secon<strong>de</strong> image est un grossissem<strong>en</strong>t d'une partie <strong>de</strong> la première image. La troisième image<br />

est aussi un grossissem<strong>en</strong>t <strong>de</strong> la secon<strong>de</strong> où sont représ<strong>en</strong>tés le vecteur dM(t + 1) et le vecteur normal<br />

N(t + 1) orthognonal à la courbe <strong>de</strong> niveau <strong>de</strong> la fonction f passant par le point M(t + 1). β(t + 1) est la<br />

somme <strong>de</strong>s produits scalaires <strong>en</strong>tre la normale N(t + 1) et <strong>de</strong>s petits déplacem<strong>en</strong>ts dM(t + 1).<br />

−−−−−→ ∥ N(t + 1) ∥∥∥∥ ∥x(t + 1)b 2 1 (4.16)<br />

On dénit α(t+1) = −−−−−−−→ −−−−−→<br />

N(t + 1)<br />

−−−−−−−→<br />

dM(t + 1). . L'inégalité (4.16) signie que pour un déplacem<strong>en</strong>t dM(t + 1),<br />

x(t + 1)b2 le nombre α(t+1) sous-estime le nombre <strong>de</strong> pixels dont on se rapproche ou dont on s'éloigne <strong>de</strong> la courbe <strong>de</strong><br />

β(t + 1)<br />

l'ellipse. En additionnant ces petits déplacem<strong>en</strong>ts, on peut considérer que est une approximation<br />

x(t + 1)b2 <strong>de</strong> la distance <strong>en</strong> pixels <strong>en</strong>tre le points M(t + 1) et la courbe <strong>de</strong> l'ellipse. Et d'après l'inégalité (4.15), cette<br />

distance est toujours inférieure à un <strong>de</strong>mi-pixel.<br />

La conclusion <strong>de</strong> cette démonstration n'est pas rigoureuse mais elle explique l'étape B <strong>de</strong> l'algorithme 4.2<br />

et le rôle <strong>de</strong> la variable β(t + 1). Lors <strong>de</strong> l'étape C, les rôles <strong>de</strong> x et y sont inversées. Il est facile d'adapter<br />

la démonstration précé<strong>de</strong>nte pour démontrer que les c<strong>en</strong>tres <strong>de</strong>s pixels choisis pour représ<strong>en</strong>ter l'ellipse ne<br />

sont pas distants du plus d'un <strong>de</strong>mi-pixel <strong>de</strong> la courbe <strong>de</strong> l'ellipse lors <strong>de</strong> l'étape C<br />

Le terme β(0) est choisi selon le même raisonnem<strong>en</strong>t que celui développé dans le cas d'une droite (voir<br />

gure 4.3). La gure 4.7 repr<strong>en</strong>d cette expéri<strong>en</strong>ce dans le cas d'un cercle. Les diér<strong>en</strong>tes valeurs déplac<strong>en</strong>t<br />

dans un s<strong>en</strong>s ou dans l'autre les bornes <strong>de</strong> l'<strong>en</strong>cadrem<strong>en</strong>t (4.15) <strong>de</strong> β(t + 1) − β(0) selon que l'on souhaite<br />

que l'ellipse passe par le c<strong>en</strong>tre <strong>de</strong>s pixels ou un sommet (voir gure 4.8).<br />

4.1.3 Ext<strong>en</strong>sion aux coniques<br />

Le paragraphe précé<strong>de</strong>nt a montré comm<strong>en</strong>t l'algorithme 4.2 a été conçu et pourquoi le tracé <strong>de</strong> l'ellipse ne<br />

s'éloigne pas <strong>de</strong> plus d'un <strong>de</strong>mi-pixel <strong>de</strong> la courbe. Il serait possible <strong>de</strong> concevoir un algorithme similaire<br />

pour le tracé <strong>de</strong> toute courbe pour laquelle les coordonnées du vecteur normal dép<strong>en</strong><strong>de</strong>nt linéairem<strong>en</strong>t<br />

<strong>de</strong> (x, y). Il est donc possible <strong>de</strong> représ<strong>en</strong>ter n'importe quelle conique par un <strong>en</strong>semble <strong>de</strong> pixels sans<br />

multiplication ni division (voir [Bres<strong>en</strong>ham1977]).<br />

22


β(0) = 0 β(0) = vx 2<br />

β(0) = v x<br />

Fig. 4.7 Ces trois ellipses sont obt<strong>en</strong>us pour trois valeurs diér<strong>en</strong>tes d'initialisation <strong>de</strong> la variable β.<br />

Pour la première image, β = 0 et l'<strong>en</strong>semble <strong>de</strong>s pixels est légèrem<strong>en</strong>t décalé vers le c<strong>en</strong>tre du cercle.<br />

Lorsque β = v x , l'<strong>en</strong>semble <strong>de</strong>s pixels est légèrem<strong>en</strong>t décalé vers l'extérieur. Le cercle le plus vraisemblant<br />

est obt<strong>en</strong>u pour β = vx 2 .<br />

Fig. 4.8 Les pixels choisis pour représ<strong>en</strong>ter la courbe <strong>de</strong> l'ellipse sont ceux (couleur gris clair) dont le<br />

c<strong>en</strong>tre est le plus près <strong>de</strong> cette courbe et non ceux (gris foncé) dont le coin inférieur gauche est le plus près<br />

<strong>de</strong> cette courbe. C'est pour cela qu'on dénit β(0) = vx(0)<br />

2<br />

et non β(0) = 0.<br />

4.2 Programme, explications <strong>de</strong> l'exemple E4<br />

La fonction qui permet <strong>de</strong> tracer une ligne se décompose <strong>en</strong> <strong>de</strong>ux fonctions. La première partie<br />

trace_ligne_simple permet <strong>de</strong> tracer les lignes pour lesquels x 1 < x 2 et y 1 < y 2 . La secon<strong>de</strong> fonction<br />

trace_ligne trace les lignes pour tous les cas. Dans un premier temps, cette fonction échange les<br />

couples (x 1 , x 2 ) et/ou (y 1 , y 2 ) pour se ram<strong>en</strong>er à un cas accepté par la fonction trace_ligne_simple.<br />

Dans un second temps, à partir du résultat obt<strong>en</strong>u à cette première étape, elle <strong>en</strong> déduit la ligne pour le<br />

cas initial.<br />

1 # -*- coding: cp1252 -*-<br />

2 """ce module conti<strong>en</strong>t la fonction trace_ligne qui retourne l'<strong>en</strong>semble <strong>de</strong>s pixels<br />

3 concernés par le tracé d'une ligne <strong>en</strong> 8-connexité <strong>en</strong>tre <strong>de</strong>ux pixels"""<br />

4 import pygame # pour les affichages<br />

5 import random<br />

6<br />

7 <strong>de</strong>f trace_ligne_simple (x1,y1,x2,y2):<br />

23


8 """trace une ligne <strong>en</strong>tre les points <strong>de</strong> coordonnées (x1,y1) et (x2,y2),<br />

9 on suppose que x2 > x1, y2 >= y1,<br />

10 retourne la ligne sous la forme d'un <strong>en</strong>semble <strong>de</strong> pixels (x,y)"""<br />

11<br />

12 if y2 - y1


58 if x1 < x2 :<br />

59 if y1 < y2 :<br />

60 return trace_ligne_simple (x1,y1,x2,y2)<br />

61 else :<br />

62 ligne = trace_ligne_simple (x1,y2,x2,y1)<br />

63 return [ (x,y1 + y2 - y) for (x,y) in ligne ]<br />

64<br />

65 if x2 < x1 :<br />

66 if y1 < y2 :<br />

67 ligne = trace_ligne_simple (x2,y1,x1,y2)<br />

68 return [ (x1 + x2 - x, y) for (x,y) in ligne ]<br />

69 else :<br />

70 ligne = trace_ligne_simple (x2,y2,x1,y1)<br />

71 return [ (x1 + x2 - x, y1 + y2 - y) for (x,y) in ligne ]<br />

72<br />

73 <strong>de</strong>f display_ligne (ligne, scre<strong>en</strong>):<br />

74 """affiche une ligne à l'écran"""<br />

75 color = 0,0,0<br />

76 for p in ligne:<br />

77 pygame.draw.line (scre<strong>en</strong>, color, p,p)<br />

78 pygame.display.flip ()<br />

79<br />

80<br />

81 <strong>de</strong>f att<strong>en</strong>dre_clic (scre<strong>en</strong>):<br />

82 """att<strong>en</strong>d la pression d'un clic <strong>de</strong> souris pour continuer"""<br />

83 reste = True<br />

84 while reste:<br />

85 for ev<strong>en</strong>t in pygame.ev<strong>en</strong>t.get():<br />

86 if ev<strong>en</strong>t.type == pygame.MOUSEBUTTONUP :<br />

87 reste = False<br />

88 break<br />

89<br />

90 if __name__ == "__main__" :<br />

91 pygame.init ()<br />

92<br />

93 size = width, height = x,y = 200, 200<br />

94 black = 0, 0, 0<br />

95 white = 255,255,255<br />

96 scre<strong>en</strong> = pygame.display.set_mo<strong>de</strong>(size)<br />

97 scre<strong>en</strong>.fill (white)<br />

98<br />

99 print trace_ligne (0,0, 7,3)<br />

100 # affiche [(0, 0), (1, 0), (2, 1), (3, 1), (4, 2), (5, 2), (6, 3), (7, 3)]<br />

101<br />

102 for n in xrange (0,10):<br />

103 x1 = random.randint (0,x-1)<br />

104 y1 = random.randint (0,y-1)<br />

105 x2 = random.randint (0,x-1)<br />

106 y2 = random.randint (0,y-1)<br />

107 ligne = trace_ligne (x1,y1,x2,y2)<br />

25


108 display_ligne (ligne, scre<strong>en</strong>)<br />

109<br />

110 att<strong>en</strong>dre_clic (scre<strong>en</strong>)<br />

L'algorithme 4.2 décrivant le tracé d'une ellipse est le sujet du TD 5.<br />

n correction exemple E4<br />

⊓⊔<br />

26


5 Gênes porteurs <strong>de</strong> maladie, simulation <strong>de</strong> la consanguinité<br />

5.1 Enoncé E5<br />

L'article qui suit est extrait du Courrier International daté du Jeudi 16 septembre 2004 qui repr<strong>en</strong>d un<br />

article du journal The In<strong>de</strong>p<strong>en</strong><strong>de</strong>nt. Il concerne une province nlandaise dont la population est <strong>en</strong> gran<strong>de</strong><br />

partie issue d'un noyau <strong>de</strong> 40 à 60 familles qui vivai<strong>en</strong>t là au XVI e siècle. Quelques siècles plus tard, la<br />

<strong>fr</strong>équ<strong>en</strong>ce <strong>de</strong> certaines maladies est anormalem<strong>en</strong>t élevée.<br />

"La population <strong>de</strong> l'est <strong>de</strong> la Finlan<strong>de</strong> est bi<strong>en</strong> connue pour être mala<strong>de</strong>", note The<br />

In<strong>de</strong>p<strong>en</strong><strong>de</strong>nt. Dans cette région isolée, on constate non seulem<strong>en</strong>t l'un <strong>de</strong>s plus forts<br />

taux <strong>de</strong> maladies cardio-vasculaires au mon<strong>de</strong>, mais aussi <strong>de</strong>s taux anormalem<strong>en</strong>t<br />

élevés <strong>de</strong> diabète, <strong>de</strong> schizophrénie et <strong>de</strong> dépression, énumère le quotidi<strong>en</strong> britannique.<br />

Alors que la mortalité infantile y est l'une <strong>de</strong>s plus faibles au mon<strong>de</strong>, dès que<br />

les g<strong>en</strong>s atteign<strong>en</strong>t la quarantaine, "leur espérance <strong>de</strong> vie est bi<strong>en</strong> inférieure à celle<br />

<strong>de</strong>s autres Europé<strong>en</strong>s".<br />

Cette spécicité régionale intrigue la communauté sci<strong>en</strong>tique <strong>de</strong>puis plus <strong>de</strong> vingt<br />

ans. Certains croyai<strong>en</strong>t t<strong>en</strong>ir l'explication <strong>en</strong> pointant les mauvaises habitu<strong>de</strong>s alim<strong>en</strong>taires<br />

<strong>de</strong>s Finlandais, ainsi que leur consommation d'alcool. En eet, "alors<br />

qu'<strong>en</strong> moy<strong>en</strong>ne ils boiv<strong>en</strong>t moins que les Français ou les Itali<strong>en</strong>s, par exemple, ils<br />

consomm<strong>en</strong>t <strong>de</strong> gran<strong>de</strong>s quantités d'alcool <strong>en</strong> peu <strong>de</strong> temps, et ce dans un seul but :<br />

se soûler".<br />

Mais la véritable explication <strong>de</strong> la mauvaise santé <strong>de</strong> cette population serait d'ordre<br />

génétique. Desc<strong>en</strong>dant d'un tout petit groupe <strong>de</strong> colons installés dans la région au<br />

XVIe siècle par le roi <strong>de</strong> Suè<strong>de</strong> - "les histori<strong>en</strong>s estim<strong>en</strong>t qu'il s'agissait <strong>de</strong> 40 à 60 familles",<br />

précise The In<strong>de</strong>p<strong>en</strong><strong>de</strong>nt -, les habitants <strong>de</strong> la région ont <strong>en</strong>suite pratiqué <strong>de</strong>s<br />

mariages consanguins p<strong>en</strong>dant plusieurs c<strong>en</strong>taines d'années. "Cela veut dire que les<br />

Finlandais <strong>de</strong> l'Est possè<strong>de</strong>nt aujourd'hui <strong>de</strong>s traits génétiques extraordinairem<strong>en</strong>t<br />

homogènes."<br />

Cette homogénéité "s'est maint<strong>en</strong>ue p<strong>en</strong>dant <strong>de</strong>s siècles parce que très peu <strong>de</strong> g<strong>en</strong>s<br />

ont immigré dans la région". Autrem<strong>en</strong>t dit, "l'isolem<strong>en</strong>t géographique, religieux et<br />

linguistique <strong>de</strong>s Finlandais <strong>de</strong> l'Est a abouti à un héritage génétique commun" qui<br />

<strong>en</strong> fait "un matériau parfait pour la sci<strong>en</strong>ce", explique le quotidi<strong>en</strong>. Et, <strong>de</strong> fait, les<br />

chercheurs ont déjà i<strong>de</strong>ntié 31 gènes diér<strong>en</strong>ts qui serai<strong>en</strong>t impliqués dans <strong>de</strong>s pathologies<br />

caractéristiques <strong>de</strong> la région. Selon une sci<strong>en</strong>tique <strong>de</strong> l'université d'Helsinki,<br />

chaque habitant est porteur d'au moins un <strong>de</strong> ces gènes.<br />

Comm<strong>en</strong>t pourrait-on reproduire <strong>en</strong> Python cette micro-population ?<br />

L'<strong>en</strong>semble <strong>de</strong>s chromosomes d'une personne est réduit à un gêne récessif porteur d'une maladie. Chaque<br />

personne est dénie par un couple <strong>de</strong> boolé<strong>en</strong> (b 1 , b 2 ). Si b 1 = b 2 = true alors la personne porteuse <strong>de</strong> ce<br />

couple tombera mala<strong>de</strong>. Si b 1 ≠ b 2 , la personne sera dit porteuse. Dans le <strong>de</strong>rnier cas, la personne sera<br />

considérée comme saine. Pour simplier, on suppose que la population <strong>de</strong> départ conti<strong>en</strong>t 40 couples qui<br />

ont à chaque génération un garçon et une lle. Chaque <strong>en</strong>fant pr<strong>en</strong>dra au hasard un gêne <strong>de</strong> sa mère et<br />

un gêne <strong>de</strong> son père. A la génération suivante, les <strong>en</strong>fants form<strong>en</strong>t <strong>de</strong>s couples au hasard et ont à leur tour<br />

<strong>de</strong>s <strong>en</strong>fants tandis que leurs par<strong>en</strong>ts ont à nouveau <strong>de</strong>s <strong>en</strong>fants. Chaque couple ne peut avoir d'<strong>en</strong>fants que<br />

p<strong>en</strong>dant <strong>de</strong>ux générations. La population évolue presque (sans t<strong>en</strong>ir compte <strong>de</strong>s mala<strong>de</strong>s) comme la suite<br />

<strong>de</strong> Fibonacci 2 :<br />

2 La suite <strong>de</strong> Fibonacci (ou Léonard <strong>de</strong> Pise, son vrai nom) répond au problème <strong>de</strong>s lapins : "Possédant au départ un couple<br />

<strong>de</strong> lapins, combi<strong>en</strong> <strong>de</strong> couples <strong>de</strong> lapins obti<strong>en</strong>t-on <strong>en</strong> douze mois si chaque couple <strong>en</strong>g<strong>en</strong>dre tous les mois un nouveau couple<br />

27


u 0 = 40<br />

u 1 = 80<br />

u 2 = 120 = u 1 + u 0<br />

...<br />

u n = u n−1 + u n−2<br />

Si on suppose qu'il existait à la première génération, un porteur du gêne récessif, comm<strong>en</strong>t évolue par la<br />

suite la proportion <strong>de</strong> mala<strong>de</strong>s, <strong>de</strong> porteurs du gêne récessif et <strong>de</strong> personnes saines ?<br />

5.2 Programme, explications <strong>de</strong> l'exemple E5<br />

La gure 5.1 prés<strong>en</strong>te les résultats d'une simulation. Au début, une seule personne parmi 80 est porteuse<br />

du gêne <strong>de</strong> la maladie. On suppose que les mariages sont consanguins pour la plupart. Selon l'article,<br />

ces maladies ne survi<strong>en</strong>n<strong>en</strong>t qu'à un âge avancé. Il est fort probable qu'elles soi<strong>en</strong>t longtemps passées<br />

inaperçues. Il n'est pas absur<strong>de</strong> que <strong>de</strong>s personnes mala<strong>de</strong>s ai<strong>en</strong>t eu <strong>de</strong>s <strong>en</strong>fants tout autant que les<br />

personnes saines.<br />

Les résultats obt<strong>en</strong>us peuv<strong>en</strong>t être assez variables d'une simulation à l'autre. Ceci est dû au fait que la<br />

première génération ne conti<strong>en</strong>t qu'un seul porteur du gêne. Ce gêne peut tout-à-fait disparaître après<br />

quelques générations ou se répandre très rapi<strong>de</strong>m<strong>en</strong>t. Mais après quelques génération, la progression se<br />

statibilise. Il serait néanmoins nécessaire d'agréger les résultats <strong>de</strong> plusieurs simulations pour obt<strong>en</strong>ir <strong>de</strong>s<br />

estimateurs ables <strong>de</strong> la population nale. Pour cette simulation, à peu près 1,6 % <strong>de</strong> la population est<br />

porteuse <strong>de</strong> gêne déci<strong>en</strong>t. Cela paraît peu <strong>en</strong> comparaison <strong>de</strong>s conclusions <strong>de</strong> l'article mais il faudrait<br />

étoer cette maquette informatique avec 31 gêne et non plus un seul.<br />

n correction exemple E5<br />

⊓⊔<br />

à compter du second mois <strong>de</strong> son exist<strong>en</strong>ce ?". La réponse débouche sur la suite dite <strong>de</strong> Fibonacci dénie par récurr<strong>en</strong>ce :<br />

u n = u n−1 + u n−2. Au mois n, la population est égale à la population du mois précé<strong>de</strong>nt n − 1 et les <strong>en</strong>fants du mois<br />

n − 2. Ce mathématici<strong>en</strong> a réuni dans son livre Liber Abacci, écrit <strong>en</strong> 1202, l'<strong>en</strong>semble <strong>de</strong>s connaissances mathématiques<br />

<strong>de</strong> l'époque, notamm<strong>en</strong>t le signe zéro v<strong>en</strong>u d'In<strong>de</strong> à travers l'Ori<strong>en</strong>t, secoué par les Croisa<strong>de</strong>s. P<strong>en</strong>dant cette pério<strong>de</strong>, les<br />

marchands pisans, génois, véniti<strong>en</strong>, suivir<strong>en</strong>t les soldats et ét<strong>en</strong>dir<strong>en</strong>t leur zone d'inu<strong>en</strong>ce aux ports <strong>de</strong> la Méditerrannée et<br />

<strong>de</strong> la mer Noire. "Alors qu'il était jeune garçon, son père, qui dirigeait pour le compte <strong>de</strong> l'Ordre <strong>de</strong>s Marchands <strong>de</strong> Pise, le<br />

bureau <strong>de</strong>s douanes <strong>de</strong> Bougie <strong>en</strong> Algérie, le rappela près <strong>de</strong> lui et lui t suivre les meilleurs cours sur les métho<strong>de</strong>s <strong>de</strong> calcul<br />

indo-arabes. C'est ainsi qu'il s'initia aux mathématiques ; lors <strong>de</strong> ses <strong>fr</strong>équ<strong>en</strong>ts voyages professionnels pour le compte <strong>de</strong>s<br />

marchands pisans, il r<strong>en</strong>contrait <strong>de</strong>s mathématici<strong>en</strong>s <strong>en</strong> Egypte, <strong>en</strong> Syrie, <strong>en</strong> Prov<strong>en</strong>ce, <strong>en</strong> Grèce, et <strong>en</strong> Sicile. Il <strong>en</strong> relevait<br />

au cours <strong>de</strong> disputes, <strong>de</strong>s dés mathématiques et étudiait <strong>en</strong> profon<strong>de</strong>ur les Elém<strong>en</strong>ts d'Eucli<strong>de</strong> qu'il a toujours considérés<br />

comme un modèle <strong>de</strong> style et <strong>de</strong> rigueur logique.", extrait <strong>de</strong> Les mathématici<strong>en</strong>s, collection Bibliothèque sci<strong>en</strong>tique, Belin<br />

pour la sci<strong>en</strong>ce. Son livre servit <strong>de</strong> référ<strong>en</strong>ce à l'<strong>en</strong>seignem<strong>en</strong>t <strong>de</strong>s mathématiques p<strong>en</strong>dant près <strong>de</strong> trois siècles.<br />

28


(a) (b) (c)<br />

Fig. 5.1 Simulation <strong>de</strong> la progression du gêne mala<strong>de</strong>. L'image (a) montre l'évolution <strong>de</strong> la population et<br />

<strong>de</strong>s personnes non porteuses du gênes. L'image (b) montre l'évolution <strong>de</strong>s porteurs et <strong>de</strong>s mala<strong>de</strong>s. L'image<br />

(c) montre l'évolution <strong>de</strong> ces mêmes populations mais rapportées à la population globale. On suppose que<br />

les mariages sont consanguins à 60%, et que les mala<strong>de</strong>s ont <strong>de</strong>s <strong>en</strong>fants p<strong>en</strong>dant <strong>de</strong>ux générations. La<br />

première génération conti<strong>en</strong>t 80 personnes dont une porteuse du gêne. La population nale est composée<br />

<strong>de</strong> six millions <strong>de</strong> personnes dont 78710 porteurs du gêne et 18903 mala<strong>de</strong>s, soit respectivem<strong>en</strong>t 1,3% et<br />

0,3% <strong>de</strong> la population. Ces <strong>de</strong>ux chires sembl<strong>en</strong>t se stabiliser.<br />

6 Psychokinèse, les ampoules grill<strong>en</strong>t à distance, les d'att<strong>en</strong>te<br />

6.1 Enoncé E6<br />

Cet énoncé s'inspire du livre Dev<strong>en</strong>ez sorciers, <strong>de</strong>v<strong>en</strong>ez savants <strong>de</strong> Georges Charpak et H<strong>en</strong>ri Broch dont<br />

est tiré l'extrait suivant.<br />

Le prés<strong>en</strong>tateur se tourne vers la caméra principale, et d'un air très sérieux et <strong>en</strong>jôleur,<br />

regar<strong>de</strong> le téléspectateur droit dans les yeux <strong>en</strong> déclarant : "Allez-y ! Allumez<br />

cinq ou six lampes à côté <strong>de</strong> vous :" Puis il se tourne vers le médium et <strong>de</strong>man<strong>de</strong> :<br />

"Vous p<strong>en</strong>sez réellem<strong>en</strong>t pouvoir le faire ?" Après quelques mom<strong>en</strong>ts d'hésitation, le<br />

mage se prononce : "J'espère avoir susamm<strong>en</strong>t <strong>de</strong> conc<strong>en</strong>tration ce soir, mais je ne<br />

suis pas dans les conditions vraim<strong>en</strong>t idéales ; pour produire ce g<strong>en</strong>re <strong>de</strong> phénomène<br />

à distance, d'habitu<strong>de</strong>, je me retire p<strong>en</strong>dant plusieurs jours dans une solitu<strong>de</strong> totale<br />

et une profon<strong>de</strong> obscurité, après un jeûne strict."<br />

Si jamais il échoue, le public le mettra au compte <strong>de</strong>s circonstances et non pas <strong>de</strong> ces<br />

compét<strong>en</strong>ces. Et, pourtant, le médium n'échoue pas. Des ampoules grill<strong>en</strong>t chez les<br />

téléspectateurs qui regar<strong>de</strong>nt cette émission. Ils font part au standard téléphonique<br />

<strong>de</strong> la chaîne qui diuse <strong>en</strong> direct cet extraordinaire mom<strong>en</strong>t <strong>de</strong> culture. Le médium<br />

a donc bi<strong>en</strong> réussi - comme il le prét<strong>en</strong>dait -, <strong>en</strong> conc<strong>en</strong>trant sa puissance spirituelle<br />

sur la matière, à griller <strong>de</strong>s ampoules électriques à distance.<br />

29


Supposons que cette émission soit suivie par <strong>en</strong>viron un million <strong>de</strong> téléspectateurs et<br />

qu'elle dure une heure ou plus. Cela signie qu'<strong>en</strong>viron cinq à six millions d'ampoules<br />

ont été allumées p<strong>en</strong>dant une heure ou plus. Supposons que ce nombre soit <strong>de</strong> <strong>de</strong>ux<br />

millions.<br />

La durée <strong>de</strong> vie moy<strong>en</strong>ne d'une ampoule à incan<strong>de</strong>sc<strong>en</strong>ce est <strong>de</strong> mille heures. Ce qui<br />

signie que, p<strong>en</strong>dant la durée <strong>de</strong> l'émission, il y aura <strong>en</strong>viron <strong>de</strong>ux milles lampes<br />

grillées.<br />

Comm<strong>en</strong>t pourrait-on réaliser <strong>en</strong> Python une simulation numérique <strong>de</strong> cette expéri<strong>en</strong>ce ?<br />

Il est préférable <strong>de</strong> comm<strong>en</strong>cer les simulations avec <strong>de</strong> petits valeurs, 10000 ampoules pour une durée <strong>de</strong><br />

vie <strong>de</strong> 100 heures et une expéri<strong>en</strong>ce qui s'étale sur 500 heures.<br />

6.2 Programme, explications <strong>de</strong> l'exemple E6<br />

Ce problème ressemble à un problème <strong>de</strong> les d'att<strong>en</strong>te. Ce <strong>de</strong>rnier consiste à déterminer un nombre<br />

adéquat <strong>de</strong> guichets <strong>en</strong> fonction <strong>de</strong> la vitesse <strong>de</strong> remplissage d'une le d'att<strong>en</strong>te an <strong>de</strong> limiter le temps<br />

d'att<strong>en</strong>te. On désigne souv<strong>en</strong>t cette problématique par un sigle du type M/M/S. Le premier M signie<br />

qu'on suppose que la probabilité que n personnes arriv<strong>en</strong>t p<strong>en</strong>dant une durée t suit une loi <strong>de</strong> Poisson<br />

<strong>de</strong> paramètre λt. Le second M signie qu'on suppose que le temps <strong>de</strong> traitem<strong>en</strong>t d'une personne par<br />

un guichetier suit une loi expon<strong>en</strong>tielle <strong>de</strong> paramètre µ. S désigne le nombre <strong>de</strong> guichets. Pour <strong>de</strong> tels<br />

problèmes, on cherche à déterminer la probabilité que le système (le d'att<strong>en</strong>te + guichets) conti<strong>en</strong>ne n<br />

personnes. De cette probabilité, on peut déduire par exemple le temps d'att<strong>en</strong>te moy<strong>en</strong> pour une personne<br />

qui <strong>en</strong>tre dans la le d'att<strong>en</strong>te. On suppose que le système est stationnaire, les probabilités ne dép<strong>en</strong><strong>de</strong>nt<br />

pas du temps.<br />

Rappel 6.1 : loi <strong>de</strong> Poisson et loi expon<strong>en</strong>tielle<br />

Si une variable X suit une loi <strong>de</strong> Poisson <strong>de</strong> paramète λt, elle a pour <strong>de</strong>nsité :<br />

P (X = n) = (λt)n<br />

n!<br />

e −λt (6.1)<br />

Si une variable X suit une loi expon<strong>en</strong>tielle <strong>de</strong> paramètre µ, elle a pour <strong>de</strong>nsité :<br />

f(t) = µ e −µt et P (X t) =<br />

∫ t<br />

0<br />

µ e −µx dx = 1 − e −µt (6.2)<br />

6.2.1 File d'att<strong>en</strong>te, cas M/M/1<br />

On s'intéresse d'abord à un système M/M/1. Il n'y a donc qu'un seul guichet. λ est le nombre moy<strong>en</strong><br />

d'arrivée par unité <strong>de</strong> temps tandis que µ est le nombre moy<strong>en</strong> <strong>de</strong> cli<strong>en</strong>ts traités par unité <strong>de</strong> temps. On<br />

suppose égalem<strong>en</strong>t que λ < µ. Dans le cas contraire, la le d'att<strong>en</strong>te <strong>de</strong>vi<strong>en</strong>t innie. On désigne par p n (t)<br />

la probabilité que la le d'att<strong>en</strong>te conti<strong>en</strong>n<strong>en</strong>t n personne. Que vaut cette probabilité à l'instant t + dt ?<br />

On considère que p<strong>en</strong>dant la durée t + dt, au plus une personne peut s'ajouter à la le d'att<strong>en</strong>te et au<br />

plus une personne peut être traitée par un guichetier. Les autres cas sont négligeables. On désigne par<br />

30


B(x, t, dt) le fait qu'une personne quitte un guichet p<strong>en</strong>dant les instants t et t + dt sachant qu'elle est<br />

arrivée au guichet à l'instant x. On cherche à déterminer la probabilité P (B(x, t, dt)). On peut dire aussi<br />

que P (B(x, t, dt) est la probabilité que le temps <strong>de</strong> traitem<strong>en</strong>t d'une personne est inférieur à t + dt − x<br />

sachant qu'il est supérieur à t − x. Si D est une variable <strong>de</strong> durée suivant une loi expon<strong>en</strong>tielle, alors :<br />

P (B(x, t, dt)) = P (D t + dt − x | D > t − x) =<br />

=<br />

∫ t+dt−x<br />

t−x<br />

µe −µu du<br />

∫ ∞<br />

t−x µe−µu du<br />

= 1 − e −µdt<br />

= e−µ(t−x) − e −µ(t−x+dt)<br />

e −µ(t−x)<br />

P (t + dt − x D > t − x)<br />

P (D > t − x)<br />

P (B(x, t, dt)) = −µdt + o(dt) (6.3)<br />

Cette probabilité ne dép<strong>en</strong>d ni <strong>de</strong> x, ni <strong>de</strong> t. En ce qui concerne les arrivées, la probabilité qu'une personne<br />

arrive p<strong>en</strong>dant les instants t et t + dt ne dép<strong>en</strong>d pas du passé et suit une loi <strong>de</strong> Poisson <strong>de</strong> paramètre λ.<br />

Cette constation et l'équation (6.3) nous permett<strong>en</strong>t d'écrire que :<br />

P (une personne arrive p<strong>en</strong>dant dt) = λdt e −λdt ∼ λdt + o(dt) (6.4)<br />

P (une personne part p<strong>en</strong>dant dt) = 1 − e −µdt ∼ µdt + o(dt) (6.5)<br />

De plus, p<strong>en</strong>dant la durée dt, quatre cas sont possibles :<br />

1. Une personne peut arriver sans qu'aucune ne parte d'un guichet.<br />

2. Une personne peut partir d'un guichet sans qu'aucune autre n'arrive.<br />

3. Une personne peut arriver et une autre partir d'un guichet.<br />

4. Aucune personne n'arrive et aucune ne part d'un guichet.<br />

Ces quatre cas permett<strong>en</strong>t d'écrire la relation suivante :<br />

p n (t + dt) = p n−1 (t) λdt +<br />

p n+1 (t) µdt +<br />

p n (t) (µdtλdt) +<br />

p n (t) (1 − µdt) (1 − λdt) (6.6)<br />

On néglige les termes du second <strong>de</strong>gré <strong>en</strong> (dt) 2 :<br />

p n (t + dt) = p n−1 (t) λdt + p n+1 (t) µdt + p n (t) (1 − µdt − λdt) (6.7)<br />

⇐⇒ p n(t + dt) − p n (t)<br />

= λp n−1 (t) + µp n+1 (t) − (µ + λ) p n (t)<br />

dt<br />

(6.8)<br />

Cette relation n'est vraie que lorsque n > 0, lorsque n = 0, aucune personne déjà prés<strong>en</strong>te ne peut être<br />

traitée par un guichetier, on a donc :<br />

p 0 (t + dt) − p 0 (t)<br />

dt<br />

= µp 1 (t) − λp 0 (t) (6.9)<br />

31


Comme le système est stationnaire, toutes les dérivées sont nulles. Les probabilités ne dép<strong>en</strong><strong>de</strong>nt pas du<br />

temps. Ceci donne les relations suivantes :<br />

{<br />

λpn−1 + µp n+1 − (µ + λ) p n = 0<br />

µp 1 − λp 0 = 0<br />

(6.10)<br />

⇐⇒<br />

{<br />

µpn+1 = (µ + λ) p n − λ p n−1<br />

µp 1 = λp 0<br />

(6.11)<br />

On vérie par récurr<strong>en</strong>ce que :<br />

( ) λ n<br />

p n = p 0 (6.12)<br />

µ<br />

Il reste à déterminer p 0 . Or, comme : ∑ ∞<br />

0 p n = 1 = p 0<br />

∑ ∞<br />

0<br />

(<br />

λ<br />

µ<br />

) n,<br />

on obti<strong>en</strong>t que :<br />

p 0<br />

1 − λ µ<br />

= 1 ⇐⇒ p 0 = 1 − λ µ<br />

(6.13)<br />

Exemple :<br />

On suppose qu'un mé<strong>de</strong>cin traite <strong>en</strong> moy<strong>en</strong>ne quatre pati<strong>en</strong>ts par heure tandis qu'il accepte trois r<strong>en</strong><strong>de</strong>zvous<br />

par heure. Donc λ = 3 et µ = 4. Le nombre moy<strong>en</strong> n <strong>de</strong> pati<strong>en</strong>ts dans sa salle d'att<strong>en</strong>te est donné<br />

par :<br />

n =<br />

∞∑<br />

np n =<br />

0<br />

(<br />

1 − λ )<br />

∑ ∞ ( ) λ n<br />

n =<br />

µ µ<br />

0<br />

λ<br />

µ<br />

1 − λ µ<br />

= λ<br />

µ − λ<br />

(6.14)<br />

Il y a donc <strong>en</strong> moy<strong>en</strong>ne trois personnes dans la salle d'att<strong>en</strong>te <strong>de</strong> ce mé<strong>de</strong>cin. Comme le temps moy<strong>en</strong> <strong>de</strong><br />

traitem<strong>en</strong>t <strong>de</strong> chacun est 1 µ<br />

, le temps moy<strong>en</strong> d'att<strong>en</strong>te d'un pati<strong>en</strong>t arrivant dans la salle d'att<strong>en</strong>te est :<br />

λµ<br />

µ−λ<br />

. Ce temps est égal à trois quarts d'heure pour cet exemple.<br />

6.2.2 File d'att<strong>en</strong>te, cas M/M/S<br />

Le système conti<strong>en</strong>t dorénavant S guichets, on suppose que la vitesse µ <strong>de</strong> traitem<strong>en</strong>t <strong>de</strong>s cli<strong>en</strong>ts est<br />

commune à tous les guichets. On cherche tout d'abord la probabilité qu'une personne s'<strong>en</strong> aille parmi<br />

les k qui occup<strong>en</strong>t un guichet. On désigne par (D 1 , . . . , D k ) k variables indép<strong>en</strong>dantes suivant une loi<br />

expon<strong>en</strong>tielle <strong>de</strong> paramètre µ, p<strong>en</strong>dant un temps dt, la probabilité qu'une personne parmi k quitte un<br />

guichet est :<br />

32


P (min {D 1 , . . . , D k } dt) = 1 − P (min {D 1 , . . . , D k } > dt)<br />

[ k∏<br />

]<br />

= 1 − P (D n > dt)<br />

n=1<br />

[ k∏<br />

]<br />

= 1 − 1 − P (D n dt)<br />

n=1<br />

[ k∏<br />

]<br />

n=1e −µdt<br />

= 1 −<br />

= 1 − e −kµdt ∼ kµdt + o(dt) (6.15)<br />

Pour déterminer les probabilités (p n ) n<br />

, on applique le même raisonnem<strong>en</strong>t que pour un système M/M/1<br />

<strong>en</strong> distinguant les cas n S et n > S. On adapte la récurr<strong>en</strong>ce donnée par le système d'équations (6.10)<br />

au cas M/M/S :<br />

⎧<br />

⎨<br />

⎩<br />

µp 1 − λp 0 = 0<br />

λp n−1 + (n + 1) µp n+1 − (nµ + λ) p n = 0 si 1 n < S<br />

λp n−1 + Sµp n+1 − (Sµ + λ) p n = 0 si n S<br />

(6.16)<br />

On suppose que λ<br />

Sµ<br />

< 1 an que la le d'att<strong>en</strong>te ne <strong>de</strong>vi<strong>en</strong>ne innie. Comme pour un système M/M/1,<br />

ces formules <strong>de</strong> récurr<strong>en</strong>ces et le fait que ∑ ∞<br />

0 p n = 1 permet <strong>de</strong> déduire que :<br />

⎧<br />

⎪⎨<br />

p 0 =<br />

p n = 1 n!<br />

1<br />

( ) S λ<br />

µ<br />

( ) + S−1<br />

( ) n<br />

∑ λ<br />

µ<br />

S! 1− λ<br />

n!<br />

Sµ k=1<br />

( λ<br />

µ<br />

) n<br />

p 0 si n < S<br />

(6.17)<br />

⎪⎩<br />

p n =<br />

1<br />

S n−S S!<br />

( λ<br />

µ<br />

) n<br />

p 0 si n S<br />

Ces calculs sont utilisés pour optimiser le nombre <strong>de</strong> guichets. Chaque guichetier a un coût qui doit être<br />

comparé avec le coût associé au temps d'att<strong>en</strong>te <strong>de</strong>s cli<strong>en</strong>ts. Ces résultats sont extraits du livre [Faure2000].<br />

"La théorie <strong>de</strong>s les d'att<strong>en</strong>te remonte aux premiers travaux <strong>de</strong> K. Erlang (1917), sur<br />

le calcul du nombre d'organes <strong>de</strong> chaque type à installer dans un c<strong>en</strong>tral téléphonique<br />

automatique. Développée aussi par Engset (1918), cette théorie s'est ampliée sous<br />

l'impulsion <strong>de</strong> nombreux chercheurs (E. Borel, D. K<strong>en</strong>dall, A. Kolmogorov, Khintchine,<br />

LC. Palm, F. Pollaczek, L. Feller, ...). Les informatici<strong>en</strong>s l'utilis<strong>en</strong>t notamm<strong>en</strong>t<br />

pour l'évaluation <strong>de</strong> performances, à titre prévisionnel, <strong>de</strong>s systèmes ou <strong>de</strong>s réseaux<br />

informatiques.<br />

6.2.3 Retour aux ampoules<br />

La durée <strong>de</strong> traitem<strong>en</strong>t d'un cli<strong>en</strong>t fait p<strong>en</strong>ser à la durée <strong>de</strong> vie d'une ampoule. Les lampes font oce <strong>de</strong><br />

guichets tandis que le rôle <strong>de</strong>s cli<strong>en</strong>ts est joué par <strong>de</strong>s lumières. Toutefois, ce n'est pas le temps d'att<strong>en</strong>te<br />

33


moy<strong>en</strong> ni la longueur moy<strong>en</strong>ne <strong>de</strong> la le d'att<strong>en</strong>te qui nous intéresse mais, <strong>en</strong> quelque sorte, le nombre <strong>de</strong><br />

cli<strong>en</strong>ts qui sont traités p<strong>en</strong>dant une durée T . En fait, plus exactem<strong>en</strong>t, c'est le nombre <strong>de</strong> guichets qui<br />

auront traités au moins un cli<strong>en</strong>t p<strong>en</strong>dant une durée T qui nous intéresse. Il correspond exactem<strong>en</strong>t au<br />

nombre d'ampoules qui vont griller p<strong>en</strong>dant cette même pério<strong>de</strong> T . Il reste à dénir ce qu'est une le<br />

d'att<strong>en</strong>te d'ampoules et surtout son paramètre λ.<br />

Lorsqu'une ampoule grille, elle est a priori changée dans les plus brefs délais, comme si la le d'att<strong>en</strong>te<br />

<strong>de</strong>s ampoules était innie. Ceci signie que λ >> µ, conguration qui sort du champ d'application <strong>de</strong>s<br />

les d'att<strong>en</strong>te M/M/S. Le paramètre λ n'a a priori aucun rôle à jouer. On peut néanmoins s'inspirer <strong>de</strong><br />

la métho<strong>de</strong> développée dans les paragraphes précédants pour abor<strong>de</strong>r le problème <strong>de</strong>s ampoules.<br />

On suppose que la durée <strong>de</strong> vie d'une ampoule suit toujours une loi expon<strong>en</strong>tielle <strong>de</strong> paramètre µ et qu'il y<br />

<strong>en</strong> a exactem<strong>en</strong>t S qui brill<strong>en</strong>t <strong>en</strong> même temps. On note p n (t) la probabilité que n ampoules ai<strong>en</strong>t grillées<br />

à l'instant t. Si N(t) est le nombre d'ampoules grillées à l'instant t : p n (t) = P (N(t) = n). Cette fonction<br />

n'est plus stationnaire et décroît avec le temps à partir d'un certain mom<strong>en</strong>t 3 . On utilise un raisonnem<strong>en</strong>t<br />

similaire à celui qui a permis d'écrire les équations (6.4), (6.5), (6.6) pour obt<strong>en</strong>ir :<br />

p n (t + dt) = (1 − Sµdt) p n (t) + Sµp n−1 (t)dt<br />

⇐⇒ p n(t + dt) − p n−1 (t)<br />

= −Sµp n (t) + Sµp n−1 (t)<br />

dt<br />

⇐⇒ p ′ n(t) = −Sµp n (t) + Sµp n−1 (t) (6.18)<br />

On connaît la fonction p 0 (t) puisqu'elle correspond à la probabilité qu'aucune <strong>de</strong>s S ampoules n'ait grillé<br />

<strong>de</strong>puis l'instant 0 jusqu'à l'instant t, par conséqu<strong>en</strong>t :<br />

p 0 (t) = P ( durée <strong>de</strong> vie <strong>de</strong>s S ampoules soi<strong>en</strong>t toutes supérieures à t )<br />

[∫ ∞<br />

] S<br />

=⇒ p 0 (t) = µe −µu du<br />

t<br />

=⇒ p 0 (t) = e −Sµt (6.19)<br />

L'équation (6.18) permet <strong>de</strong> dénir une suite d'équations diér<strong>en</strong>tielles du premier <strong>de</strong>gré :<br />

p 0 (t) = e −Sµt<br />

p ′ 1(t) = −Sµp 1 (t) + Sµe −Sµt<br />

p ′ 2(t) = −Sµp 2 (t) + p 1 (t)<br />

...<br />

p ′ n(t) = −Sµp n (t) + Sµp n−1 (t)<br />

On peut donc calculer par récurr<strong>en</strong>ce la suite <strong>de</strong> fonction (p n (t)) n<br />

. La solution <strong>de</strong> l'équation diér<strong>en</strong>tielle<br />

homogène est e −Sµt . On utilise la métho<strong>de</strong> <strong>de</strong> la variation <strong>de</strong> la constante <strong>en</strong> posant p n (t) = C n (t)e −Sµt .<br />

On aboutit à l'équation :<br />

p ′ n(t) = −Sµp n (t) + Sµp n−1 (t) = −Sµp n (t) + C ′ n(t)e −Sµt<br />

=⇒ C ′ n(t)e −Sµt = Sµp n−1 (t)<br />

=⇒ C ′ n(t) = Sµp n−1 (t)e Sµt<br />

3 Plus on avance dans le temps, plus le nombre d'ampoules grillées augm<strong>en</strong>te. Par conséqu<strong>en</strong>t, la probabilité qu'il y ait n<br />

ampoules grillées augm<strong>en</strong>te tout d'abord puis, à partir d'un mom<strong>en</strong>t t, elle diminue.<br />

34


Pour n = 1, on obti<strong>en</strong>t<br />

∑<br />

C 1 ′ (t) = Sµ =⇒ C′ 1 (t) = Sµt + A 1. Par conséqu<strong>en</strong>t, p 1 (t) = (Sµt + A 1 ) e −Sµt .<br />

On sait que ∀t, ∞<br />

0<br />

p n(t) = 1 mais cela ne permet pas <strong>de</strong> déterminer la constante A 1 . Néanmoins, <strong>en</strong><br />

faisant t<strong>en</strong>dre t −→ 0, nécessairem<strong>en</strong>t p 1 (t) −→ 0. Par conséqu<strong>en</strong>t : A 1 = 0 et p 1 (t) = Sµt e −Sµt . On pose<br />

maint<strong>en</strong>ant p 2 (t) = C 2 (t)e −Sµt . La résolution <strong>de</strong> l'équation diér<strong>en</strong>tielle (6.20) pour n = 2 aboutit à :<br />

C ′ 2(t) = Sµp 1 (t)e Sµt = (Sµt) 2<br />

=⇒ C 2 (t) = 1 2 S2 µ 2 t 2 + A 2<br />

( )<br />

1<br />

=⇒ p 2 (t) =<br />

2 S2 µ 2 t 2 + A 2 e −Sµt<br />

De même, <strong>en</strong> faisant t<strong>en</strong>dre t −→ 0, on démontre que A 2 = 0. En poursuivant ce raisonnem<strong>en</strong>t, par<br />

récurr<strong>en</strong>ce, on démontre que :<br />

p n (t) = (Sµt)n<br />

n!<br />

e −Sµt (6.20)<br />

p n (t) = P (N(t) = n) et d'après l'équation (6.20), la variable aléatoire N(t) suit une loi <strong>de</strong> Poisson <strong>de</strong><br />

paramètre Sµt. N est aussi appelé processus <strong>de</strong> Poisson. L'espérance <strong>de</strong> N est égale à : E (N(t)) = Sµt.<br />

P<strong>en</strong>dant une durée T , le nombre moy<strong>en</strong> d'ampoules grillées est :<br />

Ce nombre est indép<strong>en</strong>dant du temps t.<br />

Application numérique :<br />

E (N(t) − N(t − T )) = E (N(T )) − E (N(t − T )) = SµT (6.21)<br />

Pour <strong>de</strong>s ampoules d'une durée <strong>de</strong> 1000 heures, le paramètre µ = 1<br />

1000<br />

, T = 1. S'il y a <strong>de</strong>ux millions<br />

d'ampoules, le nombre moy<strong>en</strong> d'ampoules grillées par heure est : SµT = 2000. On retrouve le résultat<br />

énoncé.<br />

6.2.4 Programme informatique<br />

La durée <strong>de</strong> vie <strong>de</strong> chaque ampoule suit une loi expon<strong>en</strong>tielle <strong>de</strong> paramètre µ. Il faut donc simuler une<br />

telle variable dont la fonction <strong>de</strong> répartition est : F µ (x) = 1 − e µx . On utilise pour cela une propriété qui<br />

découle <strong>de</strong> la fonction <strong>de</strong> répartition. On note Fµ −1 (x) = − ln(1−x)<br />

µ<br />

. Cette fonction vérie Fµ −1 (F µ (x)) = 1.<br />

Or si U est une variable aléatoire uniforme sur [0, 1], alors la variable V = Fµ −1 (U) suit la loi expon<strong>en</strong>tielle<br />

avec µ pour paramètre 4 . La première fonction simule une variable expon<strong>en</strong>tielle <strong>de</strong> paramètre µ :<br />

import math<br />

import random<br />

<strong>de</strong>f g<strong>en</strong>erate_expo (mu):<br />

x = 0<br />

while x == 0: x = random.random()<br />

y = - math.log(1-x) / mu<br />

return y<br />

4 Eectivem<strong>en</strong>t : P (V t) = P ( F −1<br />

µ (U) t ) = P (U F µ(t)) = F µ(x). La fonction <strong>de</strong> répartition <strong>de</strong> la variable V est<br />

F µ, V est donc une loi expon<strong>en</strong>tielle <strong>de</strong> paramètre µ.<br />

35


Le module random propose aussi une fonction qui simule automatiquem<strong>en</strong>t une variable expon<strong>en</strong>tielle :<br />

import random<br />

<strong>de</strong>f g<strong>en</strong>erate_expo (mu):<br />

return random.expovariate(mu)<br />

Pour réaliser cette simulation, on suppose qu'on a un tableau <strong>de</strong> S ampoules. Chaque case <strong>de</strong> ce tableau<br />

conti<strong>en</strong>t la durée <strong>de</strong> vie restante d'une ampoule, simulée selon une loi expon<strong>en</strong>tielle. Au départ, toutes les<br />

durées <strong>de</strong> vie sont nulles. On considère qu'à chaque itération, une heure passe. Lors <strong>de</strong> chaque itération,<br />

pour chaque ampoule, on vérie sa durée <strong>de</strong> vie restante. Si celle-ci est nulle, on la remplace par une autre<br />

dont on choisit aléatoirem<strong>en</strong>t la durée <strong>de</strong> vie (arrondie à l'<strong>en</strong>tier le plus proche). Si la durée <strong>de</strong> vie n'est<br />

pas nulle, on la diminue d'une heure. A chaque itération, on compte le nombre d'ampoules grillées pour<br />

<strong>en</strong> faire la moy<strong>en</strong>ne au bout <strong>de</strong> n itérations. Pour eectuer cette simulation, les valeurs choisies sont :<br />

S = 10000 µ = 1<br />

100<br />

n = 500<br />

Le programme suivant réalise cette simulation. On calcule la moy<strong>en</strong>ne du nombre d'ampoules grillées par<br />

heure sur les 500 itérations excepté la première pour laquelle toutes les ampoules sont grillées - conguration<br />

aberrante ou tout du moins très peu probable -. La valeur obt<strong>en</strong>ue est proche <strong>de</strong> Sµ = 100.<br />

1 # -*- coding: cp1252 -*-<br />

2<br />

3 import math<br />

4 import random<br />

5<br />

6 <strong>de</strong>f g<strong>en</strong>erate_expo (mu):<br />

7 return random.expovariate(mu)<br />

8<br />

9 S = 10000<br />

10 iteration = 500<br />

11 mu = 1.0 / 100<br />

12<br />

13 # création d'un tableau <strong>de</strong> S ampoule qui conti<strong>en</strong>t la durée <strong>de</strong><br />

14 # vi<strong>de</strong> restante d'une ampoule<br />

15 ampoule = [0 for a in range(0,S)]<br />

16 moy<strong>en</strong>ne_grille = 0<br />

17 for i in range(0,iteration):<br />

18 grille = 0<br />

19 mean = 0<br />

20 for n in range(0,S):<br />

21 mean += ampoule [n]<br />

22 if ampoule[n] == 0:<br />

23 # remplacem<strong>en</strong>t d'une ampoule grillée<br />

24 grille += 1<br />

25 # on détermine la durée <strong>de</strong> vie <strong>de</strong> cette ampoule<br />

26 # on arrondit à l'<strong>en</strong>tier le plus proche<br />

27 ampoule [n] = int (g<strong>en</strong>erate_expo(mu))<br />

28 else :<br />

29 # on <strong>en</strong>lève une heure à la durée <strong>de</strong> vie <strong>de</strong> l'ampoule<br />

36


30 ampoule [n] -= 1<br />

31 mean /= S<br />

32 if i > 0: moy<strong>en</strong>ne_grille += grille<br />

33 print "itération : ", i, " moy<strong>en</strong>ne durée : ", mean, " grillées :", grille<br />

34<br />

35 moy<strong>en</strong>ne_grille = float (moy<strong>en</strong>ne_grille) / float (iteration - 1)<br />

36 print "nombre moy<strong>en</strong> d'ampoules grillées :", moy<strong>en</strong>ne_grille<br />

n correction exemple E6<br />

⊓⊔<br />

37


7 Plus court chemin dans un graphe<br />

7.1 Enoncé E7<br />

La gure 7.1 représ<strong>en</strong>te un mini réseau routier. Chaque n÷ud désigne une ville tandis que chaque arc<br />

dissimule une route reliant <strong>de</strong>ux villes ainsi que sa longueur. Comm<strong>en</strong>t dénir une métho<strong>de</strong> qui permette<br />

dans tous les cas <strong>de</strong> déterminer le chemin <strong>en</strong>tre <strong>de</strong>ux n÷uds d'un graphe ?<br />

Fig. 7.1 Chaque n÷ud <strong>de</strong> ce graphe symbolise une ville tandis que chaque arc symbolise une route et sa<br />

distance. On cherche à savoir quelle est le chemin <strong>de</strong> plus court pour relier les villes numéro 0 et numéro 4.<br />

On dénit un graphe G par le couple G = (X, E) où X est l'<strong>en</strong>semble <strong>de</strong>s sommets du graphe, et E celui<br />

<strong>de</strong> ses arêtes. Appliqué au graphe <strong>de</strong> la gure 7.1, cela donne :<br />

X = {0, 1, ..., 14}<br />

E = {(0, 1), (0, 2), (0, 8), (1, 2), (1, 3), (2, 3), (3, 7), (3, 8), (3, 10), (4, 5), (4, 12), (4, 13)<br />

(5, 14), (6, 10), (6, 11), (6, 13), (6, 14), (7, 8), (7, 10), (7, 11), (8, 9)<br />

(9, 12), (11, 12), (11, 14)} (7.1)<br />

Les arcs sont souv<strong>en</strong>t pondérés comme ici puisque à chaque arc est associée la distance séparant <strong>de</strong>ux<br />

n÷uds ou plus généralem<strong>en</strong>t la valeur d'une fonction f : E −→ R. Il s'agit <strong>de</strong> déterminer le chemin le<br />

plus court ou celui <strong>de</strong> poids minimum, c'est-à-dire que la somme <strong>de</strong>s valeurs <strong>de</strong> la fonction f associées à<br />

chaque arc du chemin doit être minimum.<br />

Le graphe <strong>de</strong> la gure 7.1 est supposé non-ori<strong>en</strong>té puisque chaque route qui relie <strong>de</strong>ux villes peut être<br />

parcourue dans un s<strong>en</strong>s ou dans l'autre. Un graphe ori<strong>en</strong>té n'autorise le parcours d'un arc que dans un<br />

s<strong>en</strong>s, comme les rues <strong>en</strong> s<strong>en</strong>s interdit dans une ville. Les arcs ori<strong>en</strong>tés sont représ<strong>en</strong>tés graphiquem<strong>en</strong>t par<br />

<strong>de</strong>s èches, qui sont abs<strong>en</strong>tes du graphe <strong>de</strong> la gure 7.1 puisque le graphe est non-ori<strong>en</strong>té.<br />

L'<strong>en</strong>semble E (7.1) est décrit par une suite <strong>de</strong> couples <strong>de</strong> sommets : E ⊂ X 2 . Il existe toutefois d'autres<br />

manières <strong>de</strong> représ<strong>en</strong>ter l'<strong>en</strong>semble <strong>de</strong>s arcs. La première consiste à associer à chaque sommet <strong>de</strong> X<br />

l'<strong>en</strong>semble <strong>de</strong> ses successeurs :<br />

38


S : x ∈ X −→ S(x) = {y ∈ X | (x, y) ∈ E}<br />

Par exemple, pour le sommet 7 <strong>de</strong> la gure 7.1, S(7) = 3, 8, 10, 11. Cette représ<strong>en</strong>tation est utilisée dans<br />

le TD 4 et l'est généralem<strong>en</strong>t lorsque les graphes à manipuler conti<strong>en</strong>n<strong>en</strong>t peu d'arcs. Une autre façon <strong>de</strong><br />

représ<strong>en</strong>ter l'<strong>en</strong>semble E est une matrice appelée matrice d'adjac<strong>en</strong>ce notée A. Il sut pour cela d'in<strong>de</strong>xer<br />

les sommets <strong>de</strong> X par <strong>de</strong>s <strong>en</strong>tiers :<br />

A = (a ij ) ij<br />

{ 1 si (i, j) ∈ E<br />

avec ∀i, j, a ij =<br />

0 sinon<br />

Cette matrice est souv<strong>en</strong>t utilisée pour <strong>de</strong> petits graphes tels que ceux employés dans cet exercice. Elle<br />

permet égalem<strong>en</strong>t d'associer facilem<strong>en</strong>t un poids à chaque arc :<br />

a ij =<br />

{ poids <strong>de</strong> l'arc i −→ j si (i, j) ∈ E<br />

0 ou ∞ sinon<br />

La valeur associée à un arc inexistant (/∈ E) dép<strong>en</strong>d du problème à résoudre. Dans le cas <strong>de</strong> la recherche<br />

du plus court chemin, on pr<strong>en</strong>dra comme valeur ∞. Tout chemin passant par un arc inexistant est <strong>de</strong><br />

longueur innie. Dans le cas <strong>de</strong> la recherche du plus long chemin <strong>en</strong>tre <strong>de</strong>ux n÷uds, les arcs inexistant<br />

auront un poids nul. La matrice d'adjac<strong>en</strong>ce possè<strong>de</strong> quelques propriétés intéressantes :<br />

1. Si elle est symétrique, A = A ′ , alors le graphe est non-ori<strong>en</strong>té.<br />

2. Si les arcs inexistants sont pondérés par la valeur 0 et les arcs existants par <strong>de</strong>s valeurs strictem<strong>en</strong>t<br />

positives, si (A n ) ij > 0, alors il existe un chemin <strong>de</strong> longueur n <strong>en</strong>tre les n÷uds i et j.<br />

Cette secon<strong>de</strong> propriété se démontre par récurr<strong>en</strong>ce. Pour n = 2, on a (A 2 ) ij = ∑ k a ika kj . Par conséqu<strong>en</strong>t,<br />

si (A 2 ) ij > 0 alors il existe un sommet k tel que les arêtes (i, k) et (k, j) exist<strong>en</strong>t. Il est facile <strong>de</strong> continuer<br />

la démonstration pour n > 2.<br />

Les matrices d'adjac<strong>en</strong>ce sont peu adaptées pour <strong>de</strong>s graphes ayant un grand nombre <strong>de</strong> sommets et un<br />

petit nombre d'arêtes. D'un point <strong>de</strong> vue informatique, beaucoup <strong>de</strong> mémoire est utilisée pour stocker une<br />

information inutile puisque la matrice est presque uniforme sauf pour un petit nombre d'arêtes. Presque<br />

tous ces élém<strong>en</strong>ts sont nuls ou égaux à ∞ selon la conv<strong>en</strong>tion choisie. D'un point <strong>de</strong> vue informatique,<br />

la matrice d'adjac<strong>en</strong>ce est rarem<strong>en</strong>t une représ<strong>en</strong>tation optimale mais c'est pour sa simplicité qu'elle sera<br />

utilisée dans ce programme d'exemple chargé <strong>de</strong> déterminer le plus court chemin dans un graphe.<br />

An <strong>de</strong> se conc<strong>en</strong>trer sur l'algorithme <strong>de</strong> recherche du plus court chemin, une partie du programme est déjà<br />

développée. Elle conti<strong>en</strong>t diér<strong>en</strong>tes fonctions permettant <strong>de</strong> constuire aléatoirem<strong>en</strong>t un certain nombre<br />

<strong>de</strong> villes et <strong>de</strong> routes puis d'acher le résultat dans une image. Il ne reste plus qu'à lui ajouter la fonction<br />

qui détermine le plus court chemin <strong>en</strong>tre <strong>de</strong>ux villes. Ce programme utilise le type Matrix fourni avec les<br />

sous-modules Matrix et UserArray du module Numeric. Mis à part les <strong>de</strong>ux lignes qui serv<strong>en</strong>t à créer une<br />

matrice d'adjac<strong>en</strong>ce carrée et nulle <strong>de</strong> taille nb, pour compr<strong>en</strong>dre le programme, il sut <strong>de</strong> savoir qu'un<br />

élém<strong>en</strong>t d'indice (i, j) <strong>de</strong> la matrice d'adjac<strong>en</strong>ce m est désigné par m[i, j].<br />

# -*- coding: cp1252 -*-<br />

import random<br />

# pour tirer aléatoirem<strong>en</strong>t <strong>de</strong>s nombres<br />

39


import Matrix as mat<br />

import UserArray as ua<br />

import math<br />

import PIL.Image as Im<br />

import PIL.ImageDraw as Id<br />

# pour les matrices<br />

# pour les matrices<br />

# fonction sqrt<br />

# pour les images<br />

# pour <strong>de</strong>ssiner<br />

<strong>de</strong>f construit_ville(n, x =500, y = 500):<br />

"""tire aléatoirem<strong>en</strong>t n villes dans un carrée x * y"""<br />

l = []<br />

for i in range(0,n):<br />

xx = x * random.random ()<br />

yy = y * random.random ()<br />

l.app<strong>en</strong>d ((xx,yy))<br />

return l<br />

<strong>de</strong>f distance_ville (l,i,j):<br />

"""calcule la distance <strong>en</strong>tre <strong>de</strong>ux villes i et j <strong>de</strong> la liste l"""<br />

x = l [i][0] - l [j][0]<br />

y = l [i][1] - l [j][1]<br />

return math.sqrt (float (x*x+y*y))<br />

<strong>de</strong>f construit_arete (l,part = 0.15):<br />

"""tire aléatoirem<strong>en</strong>t part * l<strong>en</strong> (l) arêtes et construit la matrice<br />

d'adjac<strong>en</strong>ce"""<br />

nb = l<strong>en</strong> (l)<br />

m = mat.Matrix ( [ 0 for i in range(0,nb) ]) # crée un vecteur <strong>de</strong> nb zéros<br />

m = ua.transpose (m) * m # effectue une multiplication du vecteur<br />

# précé<strong>de</strong>nt avec son vecteur transposé<br />

# pour obt<strong>en</strong>ir une matrice carrée nulle<br />

are = int (part * nb * nb)<br />

while are > 0:<br />

i = random.randint (0,nb-1)<br />

j = random.randint (0,nb-1)<br />

if m [i,j] > 0: continue<br />

# si l'arête existe déjà, on recomm<strong>en</strong>ce<br />

m [i,j] = int (distance_ville (l,i,j)) # on affecte comme poids à l'arête<br />

# la distance <strong>en</strong>tre les <strong>de</strong>ux villes<br />

are -= 1<br />

return m<br />

<strong>de</strong>f <strong>de</strong>ssin_ville_arete (l,m,chemin):<br />

"""<strong>de</strong>ssine la ville et les routes dans une image"""<br />

mx, my = 0,0<br />

for i in l:<br />

mx = max (mx, i [0])<br />

my = max (my, i [1])<br />

mx += 25<br />

my += 25<br />

mx, my = int (mx), int (my)<br />

im = Im.new ("RGB", (mx, my), (255,255,255)) # création d'une image blanche<br />

draw = Id.Draw(im)<br />

40


# <strong>de</strong>ssin <strong>de</strong>s villes<br />

for i in l:<br />

j = (int (i [0]), int (i[1]))<br />

j2 = (j [0] + 10, j [1] + 10)<br />

draw.ellipse ((j,j2), fill = (0,0,0))<br />

# <strong>de</strong>ssin <strong>de</strong>s arêtes<br />

for i in range (0,l<strong>en</strong>(l)):<br />

for j in range (0,l<strong>en</strong>(l)):<br />

if m [i,j] > 0:<br />

a = (int (l [i][0]+5), int (l [i][1]+5))<br />

b = (int (l [j][0]+5), int (l [j][1]+5))<br />

draw.line ((a,b),fill=(255,0,0))<br />

# <strong>de</strong>ssin <strong>de</strong>s villes <strong>de</strong> départ et d'arrivée<br />

v1 = chemin [0]<br />

v2 = chemin [ l<strong>en</strong> (chemin)-1]<br />

a = (int (l [v1][0]), int (l [v1][1]))<br />

b = (int (l [v1][0]+10), int (l [v1][1]+10))<br />

draw.ellipse ((a,b), fill = (0,255,0))<br />

a = (int (l [v2][0]), int (l [v2][1]))<br />

b = (int (l [v2][0]+10), int (l [v2][1]+10))<br />

draw.ellipse ((a,b), fill = (0,255,0))<br />

# <strong>de</strong>ssin du chemin<br />

for c in range (0,l<strong>en</strong>(chemin)-1):<br />

i = chemin [c]<br />

j = chemin [c+1]<br />

print i,j<br />

if m [i,j] > 0:<br />

a = (int (l [i][0]+5), int (l [i][1]+5))<br />

b = (int (l [j][0]+5), int (l [j][1]+5))<br />

draw.line ((a,b),fill=(0,0,255))<br />

else:<br />

a = (int (l [i][0]+5), int (l [i][1]+5))<br />

b = (int (l [j][0]+5), int (l [j][1]+5))<br />

draw.line ((a,b),fill=(0,0,50))<br />

# on retourne l'image<br />

return im<br />

# programme principal<br />

# construction <strong>de</strong>s villes<br />

l = construit_ville (10)<br />

print l<br />

# construction <strong>de</strong>s arêtes<br />

m = construit_arete (l)<br />

print m<br />

41


# choix <strong>de</strong> la ville <strong>de</strong> départ <strong>de</strong> d'arrivée<br />

a,b = 0,0<br />

while a == b:<br />

a = random.randint (0,l<strong>en</strong>(l)-1)<br />

b = random.randint (0,l<strong>en</strong>(l)-1)<br />

print "ville <strong>de</strong> départ et d'arrivée : ",a,b<br />

# construction <strong>de</strong> l'image du résultat<br />

im = <strong>de</strong>ssin_ville_arete(l,m,[a,b])<br />

im.show () # on affiche l'image<br />

7.2 Programme, explications <strong>de</strong> l'exemple E7<br />

7.2.1 Théorie <strong>de</strong>s graphes, un peu d'histoire<br />

Cette brève histoire <strong>de</strong> la théorie <strong>de</strong>s graphes est extraite du site http ://www.sci<strong>en</strong>ces.ch/.<br />

L'histoire <strong>de</strong> la théorie <strong>de</strong>s graphes (ou <strong>de</strong>s "complexes cellulaires") débute peut-être avec les travaux<br />

d'Euler au 18e siècle et trouve son origine dans l'étu<strong>de</strong> <strong>de</strong> certains problèmes, tels que celui <strong>de</strong>s ponts<br />

<strong>de</strong> Königsberg - les habitants <strong>de</strong> Königsberg se <strong>de</strong>mandai<strong>en</strong>t s'il était possible, <strong>en</strong> partant d'un quartier<br />

quelconque <strong>de</strong> la ville, <strong>de</strong> traverser tous les ponts sans passer <strong>de</strong>ux fois par le même et <strong>de</strong> rev<strong>en</strong>ir à leur<br />

point <strong>de</strong> départ, voir gures 7.2 et 7.3 -, la marche du cavalier sur l'échiquier ou le problème <strong>de</strong> coloriage<br />

<strong>de</strong> cartes.<br />

La théorie <strong>de</strong>s graphes s'est alors développée dans diverses disciplines telles que la chimie (isomères), la biologie,<br />

les sci<strong>en</strong>ces sociales (réseaux <strong>de</strong> transports), gestion <strong>de</strong> projets (C.P.M.), informatique (topologie <strong>de</strong>s<br />

réseaux), etc.. Depuis le début du 20e siècle, elle constitue une branche à part <strong>en</strong>tière <strong>de</strong>s mathématiques,<br />

grâce aux travaux <strong>de</strong> König, M<strong>en</strong>ger, Cayley puis <strong>de</strong> Berge et d'Erdös.<br />

De manière générale, un graphe permet <strong>de</strong> représ<strong>en</strong>ter la structure, les connexions d'un <strong>en</strong>semble complexe<br />

<strong>en</strong> exprimant les relations <strong>en</strong>tre ses élém<strong>en</strong>ts : réseau <strong>de</strong> communication, réseaux routiers, interaction <strong>de</strong><br />

diverses espèces animales, circuits électriques, ... Les graphes constitu<strong>en</strong>t donc une métho<strong>de</strong> <strong>de</strong> p<strong>en</strong>sée qui<br />

permet <strong>de</strong> modéliser une gran<strong>de</strong> variété <strong>de</strong> problèmes <strong>en</strong> se ram<strong>en</strong>ant à l'étu<strong>de</strong> <strong>de</strong> sommets et d'arcs.<br />

Les <strong>de</strong>rniers travaux <strong>en</strong> théorie <strong>de</strong>s graphes sont souv<strong>en</strong>t eectués par <strong>de</strong>s informatici<strong>en</strong>s, du fait <strong>de</strong><br />

l'importance qu'y revêt l'aspect algorithmique. La théorie <strong>de</strong>s graphes connaît un assez grand <strong>en</strong>gouem<strong>en</strong>t<br />

ces tr<strong>en</strong>te <strong>de</strong>rnières, peut-être est-ce parce qu'elle ne nécessite pas dans ses concepts élém<strong>en</strong>taires <strong>de</strong> bagage<br />

mathématique considérable.<br />

7.2.2 Principe d'optimalité<br />

Déterminer le plus court chemin dans un graphe fait partie d'une classe <strong>de</strong> problème résolus par ce qu'on<br />

appelle la programmation dynamique qui découle elle-même du principe d'optimalité déni dans l'÷uvre du<br />

mathématici<strong>en</strong> Pierre <strong>de</strong> Fermat. Le principe <strong>de</strong> Fermat conti<strong>en</strong>t les postulats fondam<strong>en</strong>taux <strong>de</strong> l'optique<br />

géométrique et permet <strong>de</strong> retrouver le principe <strong>de</strong> propagation rectiligne, le principe <strong>de</strong> retour inverse et<br />

les lois <strong>de</strong> Snell-Descartes sur la ré<strong>fr</strong>action.<br />

42


Fig. 7.2 Les sept ponts <strong>de</strong> Könisberg, existe-t-il un chemin qui permett<strong>en</strong>t rev<strong>en</strong>ir à son point <strong>de</strong> départ<br />

<strong>en</strong> ne parcourant chacun <strong>de</strong>s sept ponts qu'une seule fois ?<br />

Fig. 7.3 Les sept ponts <strong>de</strong> Könisberg <strong>de</strong> la gure 7.2 et un graphe qui les représ<strong>en</strong>te. Chaque arc<br />

correspond à un pont. Chaque n÷ud correspond à un quartier. Il est impossible <strong>de</strong> rev<strong>en</strong>ir au n÷ud <strong>de</strong><br />

départ <strong>en</strong> ne parcourant toutes les arêtes qu'une seule fois. Pour que cela fût possible, il eut fallu que ce<br />

graphe ne comportât au plus que <strong>de</strong>ux n÷uds reliés à un nombre impair d'arcs.<br />

Le trajet réellem<strong>en</strong>t suivi par la lumière pour se r<strong>en</strong>dre d'un<br />

point A à un point B, correspond à un temps <strong>de</strong> parcours<br />

minimum ou, si l'on ti<strong>en</strong>t compte <strong>de</strong> la nature <strong>de</strong>s diér<strong>en</strong>ts<br />

milieux traversés, à un chemin optique minimum.<br />

En d'autres termes, appliqué à la recherche du plus court chemin dans un graphe comme celui <strong>de</strong> la<br />

gure 7.4, ce principe peut être interprété selon une récurr<strong>en</strong>ce <strong>de</strong> la forme :<br />

Toute partie d'un chemin optimal est elle-même optimale :<br />

si le trajet le plus court pour se r<strong>en</strong>dre d'un point A à un<br />

point C passe par le point B, alors ce trajet est aussi le plus<br />

court pour aller <strong>de</strong> A à B et <strong>de</strong> B à C.<br />

7.2.3 Plus court chemin dans un graphe<br />

On désigne par G = (X, S) un graphe dont l'<strong>en</strong>semble <strong>de</strong>s sommets est X et l'<strong>en</strong>semble <strong>de</strong>s arêtes est S.<br />

On note égalem<strong>en</strong>t d(i, j) le poids associé à l'arc (i, j) ∈ S. Pour le graphe <strong>de</strong> la gure 7.4, d(i, j) est la<br />

43


Fig. 7.4 Ce graphe est i<strong>de</strong>ntique à celui <strong>de</strong> la gure 7.1. Le chemin bleu est le chemin le plus court qui<br />

permette <strong>de</strong> relier les n÷uds numéro 0 et numéro 4.<br />

longueur <strong>de</strong> la route reliant les villes i et j ou plus simplem<strong>en</strong>t la distance à vol d'oiseau. La fonction d<br />

est symétrique si seulem<strong>en</strong>t le graphe est non-ori<strong>en</strong>té, comme celui <strong>de</strong> la gure 7.4.<br />

A partir du principe d'optimalité, il est possible <strong>de</strong> construire un algorithme fonctionnant par récurr<strong>en</strong>ce<br />

permettant <strong>de</strong> trouver le plus court chemin <strong>en</strong>tre <strong>de</strong>ux n÷uds. On suppose que v ∗ (i, j) représ<strong>en</strong>te la<br />

distance minimale <strong>en</strong>tre <strong>de</strong>ux n÷uds. Le principe précé<strong>de</strong>nt signie que :<br />

v ∗ (i, j) = min {v ∗ (i, k) + d(k, j) | (k → j) ∈ S}<br />

Autrem<strong>en</strong>t dit, le plus court chemin <strong>en</strong>tre les n÷uds i et j correspond au meilleur <strong>de</strong>s plus courts chemins<br />

<strong>en</strong>tre le n÷ud i et ses voisins k auxquels est ajoutée la distance d(k, j). Plusieurs algorithmes équival<strong>en</strong>ts<br />

exist<strong>en</strong>t pour calculer la fonction v ∗ (algorithmes <strong>de</strong> Bellman, Dijkstra ou Ford). L'algorithme <strong>de</strong> Ford<br />

est le moins restrictif puisqu'il suppose seulem<strong>en</strong>t que le graphe n'inclut pas <strong>de</strong> cycle <strong>de</strong> poids négatif,<br />

condition égalem<strong>en</strong>t nécessaire aux autres algorithmes. L'algorithme <strong>de</strong> Dijkstra suppose quant à lui que<br />

les poids sont strictem<strong>en</strong>t positifs et utilise cette hypothèse supplém<strong>en</strong>taire pour trouver ce meilleur chemin<br />

44


plus rapi<strong>de</strong>m<strong>en</strong>t.<br />

Dénition 7.1 : cycle dans un graphe<br />

Un cycle est un chemin qui part d'un n÷ud i et qui y revi<strong>en</strong>t. Le poids d'un chemin est la somme<br />

<strong>de</strong>s poids <strong>de</strong>s arêtes qu'il emprunte.<br />

Algorithme 7.2 : algorithme du plus court chemin (Ford)<br />

On suppose qu'un graphe est déni par un <strong>en</strong>semble <strong>de</strong> N sommets indicés <strong>de</strong> 1 à N. X =<br />

{1, . . . , N}. Ces arêtes et leurs poids sont dénis par une matrice d'adjac<strong>en</strong>ce A = (a ij ) (i,j)∈X 2<br />

<strong>de</strong> telle sorte que a ij soit égal au poids <strong>de</strong> l'arête si elle existe, égal à l'inni si l'arête n'existe<br />

pas. On suppose que le graphe ne conti<strong>en</strong>t pas <strong>de</strong> cycle <strong>de</strong> poids négatifs. Dans le cas contraire,<br />

l'algorithme est sans n. On cherche le chemin le plus court du n÷ud i 0 au n÷ud i 1 .<br />

Etape A : initialisation<br />

{ 0 si i = i0<br />

∀i ∈ X, v(i 0 , i) =<br />

∞ sinon<br />

∀i ∈ X, p(i 0 , i) = ∅<br />

Etape B : mise à jour<br />

m ←− 0<br />

pour i = 1 à N faire<br />

pour j = 1 à N faire<br />

t ←− v(i 0 , i) + d(i, j)<br />

si t < v(i 0 , j)<br />

v(i 0 , j) ←− t<br />

p(i 0 , j) ←− {i}<br />

m ←− m + 1<br />

n si<br />

n pour<br />

n pour<br />

Etape C : récurr<strong>en</strong>ce<br />

Si m > 0, cela signie qu'une meilleure solution a été trouvée. On retourne à<br />

l'étape B.<br />

Etape D : meilleur chemin<br />

La longueur du meilleur chemin est donnée par v(i 0 , i 1 ). Le meilleur chemin utilise<br />

les valeurs (p(i 0 , i)) i<br />

qui mémoris<strong>en</strong>t les prédécesseurs, ceux qui ont permis<br />

d'obt<strong>en</strong>ir le meilleur chemin. p(i 0 , i 1 ) est le prédécesseur <strong>de</strong> i 1 , p (i 0 , p(i 0 , i 1 ))<br />

est le prédécesseur p(i 0 , i 1 ). Le plus court chemin à l'<strong>en</strong>vers correspond donc à<br />

la suite dénie par récurr<strong>en</strong>ce u 0 = i 1 et u n = p (i 0 , u n−1 ). La construction <strong>de</strong><br />

cette suite s'arrête lorsque u n = ∅.<br />

Lorsqu'un graphe conti<strong>en</strong>t un cycle <strong>de</strong> poids négatif, il est toujours possible <strong>de</strong> trouver <strong>de</strong>s valeurs meilleures<br />

pour la fonction v. La <strong>de</strong>rnière étape D n'arrive jamais.<br />

45


7.2.4 Coût <strong>de</strong> l'algorithme<br />

Dénition 7.3 : coût d'un algorithme<br />

Le coût d'un algorithme est le nombre d'opérations élém<strong>en</strong>taires (opération numérique, test,<br />

aectation, ...) nécessaire à sa complète réalisation. Ce coût est généralem<strong>en</strong>t exprimé comme<br />

un multiple constant d'une fonction t<strong>en</strong>ant compte <strong>de</strong>s dim<strong>en</strong>sions <strong>de</strong>s données manipulées par<br />

l'algorithme.<br />

Par exemple, l'étape A <strong>de</strong> l'algorithme 7.2 eectue pour chaque sommet du graphe un test et <strong>de</strong>ux aectations.<br />

Le coût <strong>de</strong> cette étape est donc un multiple <strong>de</strong> N. On dit que son coût est O(N).<br />

L'étape B eectue d'abord une aectation, puis se lance dans <strong>de</strong>ux boucles imbriquées qui visit<strong>en</strong>t les N<br />

sommets du graphe. A chaque itération, l'algorithme eectue une additation, un test et selon la valeur <strong>de</strong> ce<br />

test, une ou quatre aectations. Le coût <strong>de</strong> cette étape est un multiple <strong>de</strong> N 2 auquel il faut ajouter 1 pour<br />

la première aectation. Cette aectation étant négligeable <strong>de</strong>vant les N 2 autres opérations élém<strong>en</strong>taires,<br />

on dit que le coût <strong>de</strong> cette étape est O(N 2 ).<br />

Le coût <strong>de</strong> la <strong>de</strong>rnière étape est fonction <strong>de</strong> la longueur du meilleur chemin qui ne peut inclure plus d'arêtes<br />

que n'<strong>en</strong> peut cont<strong>en</strong>ir le graphe, soit N 2 . Le coût <strong>de</strong> l'étape D est au plus O(N 2 ).<br />

Si le graphe ne conti<strong>en</strong>t pas <strong>de</strong> poids négatifs, il est alors possible d'armer que le plus court chemin inclut<br />

au plus N arêtes où N est le nombre <strong>de</strong> sommets du graphe. L'étape B est visitée au plus N fois.<br />

Finalem<strong>en</strong>t, le coût <strong>de</strong> l'algorithme est au plus : C = O(N) + NO(N 2 ) + O(N 2 ). On élimine <strong>de</strong> cette<br />

expression les termes négligeables pour obt<strong>en</strong>ir un coût au plus égal à C = O(N 3 ). Dans ce cas, le coût<br />

est fonction d'une puissance <strong>de</strong> N, on dit que le coût est polynômial.<br />

Remarque 7.4: coût dép<strong>en</strong>dant du cont<strong>en</strong>u <strong>de</strong>s données<br />

Il n'est pas toujours possible d'obt<strong>en</strong>ir un coût dép<strong>en</strong>dant seulem<strong>en</strong>t <strong>de</strong> la dim<strong>en</strong>sion <strong>de</strong>s données manipulées.<br />

L'exemple le plus courant est l'algorithme <strong>de</strong> tri rapi<strong>de</strong> (ou tri quicksort) dont le coût dép<strong>en</strong>d <strong>de</strong><br />

l'ordre du tableau à trier. Le coût <strong>de</strong> l'algorithme est alors déni comme un coût moy<strong>en</strong> sur l'<strong>en</strong>semble<br />

<strong>de</strong>s congurations possibles, ou <strong>de</strong>s ordres possibles <strong>en</strong> ce qui concerne le tri rapi<strong>de</strong> (N!).<br />

7.2.5 A propos <strong>de</strong>s graphes<br />

Les graphes sont égalem<strong>en</strong>t utilisés pour résoudre les problèmes d'ordonnancem<strong>en</strong>t par l'intermédiaire<br />

<strong>de</strong> la métho<strong>de</strong> <strong>de</strong>s pot<strong>en</strong>tiels (ou MPM). Cette métho<strong>de</strong> fait interv<strong>en</strong>ir les graphes pour modéliser les<br />

dép<strong>en</strong>dances <strong>en</strong>tre <strong>de</strong>s tâches et dénir la date à laquelle chacun d'elles peut être comm<strong>en</strong>cée. Cette<br />

métho<strong>de</strong> combin<strong>en</strong>t <strong>de</strong>s chemins les plus courts et <strong>de</strong>s chemins les plus longs an <strong>de</strong> déterminer une plage<br />

temporelle p<strong>en</strong>dant laquelle une tâche doit être exécutée.<br />

Les graphes sont égalem<strong>en</strong>t utilisés pour optimiser les débits dans un réseau par l'intermédiaire <strong>de</strong> l'algorithme<br />

<strong>de</strong> Ford-Fulkerson. Ce problème est souv<strong>en</strong>t désigné par "problème <strong>de</strong> ot maximal".<br />

La théorie <strong>de</strong>s graphes a permis aussi <strong>de</strong> résoudre le problème <strong>de</strong> coloriage <strong>de</strong>s cartes : combi<strong>en</strong> faut-il <strong>de</strong><br />

couleurs au minimum pour colorier une carte sans que <strong>de</strong>ux régions voisines soi<strong>en</strong>t <strong>de</strong> la même couleur ?<br />

Les graphes apparaiss<strong>en</strong>t <strong>en</strong>core comme une possible représ<strong>en</strong>tation <strong>de</strong> modèles probabilistes tels que<br />

les chaînes <strong>de</strong> Markov. La matrice d'adjac<strong>en</strong>ce équivaut pour ces modèles à la matrice <strong>de</strong> transition qui<br />

conti<strong>en</strong>t <strong>de</strong>s probabilités <strong>de</strong> transitions d'un état à un autre.<br />

46


7.2.6 Programme<br />

Le programme suivant implém<strong>en</strong>te la recherche du meilleur chemin dans un graphe. La fonction<br />

meilleur_chemin est la trascription <strong>de</strong> l'algorithme 7.2 <strong>en</strong> langage Python. Les autres fonctions ne serv<strong>en</strong>t<br />

qu'à générer un graphe décrivant un réseau routier aléatoire. Les villes sont placées <strong>de</strong> telle sorte qu'elles<br />

soi<strong>en</strong>t susamm<strong>en</strong>t espacées pour r<strong>en</strong>dre l'achage graphique lisible. Pour la même raison, le graphe ne<br />

conti<strong>en</strong>t pas trop d'arêtes. La gure 7.5 montre un exemple <strong>de</strong> l'image construite par le programme.<br />

Fig. 7.5 Exemple d'image construite par le programme cherchant le plus court chemin dans un graphe.<br />

Les villes vertes sont les points <strong>de</strong> départ et d'arrivée. Les arêtes bleues et épaisses sont les arêtes faisant<br />

partie du meilleur chemin.<br />

Après avoir généré les villes et les arêtes <strong>de</strong> manière aléatoire grâce aux fonctions construit_ville<br />

et construit_arete, la fonction choix_villes_<strong>de</strong>part_arrive détermine <strong>de</strong>ux villes <strong>de</strong> départ et<br />

d'arrivée. Cette fonction utilise la fonction meilleur_chemin an d'éviter les chemins trop courts (moins<br />

<strong>de</strong> quatre villes). Enn, la fonction <strong>de</strong>ssin_ville_arete termine le programme par la construction d'une<br />

image cont<strong>en</strong>ant le graphe et le meilleur chemin trouvé. Toutes ces fonctions ne serv<strong>en</strong>t qu'à planter un<br />

décor pour la fonction meilleur_chemin et ainsi vérier visuellem<strong>en</strong>t que la solution trouvée est bi<strong>en</strong><br />

la bonne. Ceci explique la longueur du programme alors que la fonction meilleur_chemin n'<strong>en</strong> forme<br />

qu'une petite partie.<br />

Ce g<strong>en</strong>re <strong>de</strong> problème facile - appelé problème jouet ou toy problem - est souv<strong>en</strong>t prés<strong>en</strong>t dans les programme<br />

<strong>en</strong> tant que fonction <strong>de</strong> test. Il permet <strong>de</strong> vérier qu'un algorithme a été implém<strong>en</strong>té correctem<strong>en</strong>t et<br />

fonctionne pour une conguration dans laquelle le programmeur peut rapi<strong>de</strong>m<strong>en</strong>t évaluer la validité d'un<br />

résultat. Il peut ainsi vali<strong>de</strong>r ou invali<strong>de</strong>r une portion <strong>de</strong> son co<strong>de</strong>.<br />

1 # -*- coding: cp1252 -*-<br />

2 import random # pour tirer aléatoirem<strong>en</strong>t <strong>de</strong>s nombres<br />

3 import Matrix as mat # pour les matrices<br />

4 import UserArray as ua # pour les matrices<br />

5 import math # fonction sqrt<br />

6 import PIL.Image as Im # pour les images<br />

7 import PIL.ImageDraw as Id # pour <strong>de</strong>ssiner<br />

47


8<br />

10<br />

9 infini = 10000000 # l'infini est égal à dix millions, c'est une variable globale<br />

11 <strong>de</strong>f construit_ville(n, x =1000, y = 800):<br />

12 """tire aléatoirem<strong>en</strong>t n villes dans un carrée x * y, on choisit<br />

13 ces villes <strong>de</strong> sort<strong>en</strong>t qu'elles ne soi<strong>en</strong>t pas trop proches"""<br />

14 # <strong>de</strong>ux villes ne pourront pas être plus proches que mind<br />

15 mind = math.sqrt (x*x+y*y) / (n * 0.75)<br />

16 # liste vi<strong>de</strong><br />

17 l = []<br />

18 while n > 0:<br />

19 # on tire aléatoirem<strong>en</strong>t les coordonnées d'une ville<br />

20 xx = x * random.random ()<br />

21 yy = y * random.random ()<br />

22 # on vérifie qu'elle n'est pas trop proche d'aucune autre ville<br />

23 ajout = True<br />

24 for t in l :<br />

25 d1 = t [0] - xx<br />

26 d2 = t [1] - yy<br />

27 d = math.sqrt (d1*d1+d2*d2)<br />

28 if d < mind :<br />

29 ajout = False # ville trop proche<br />

30 # si la ville n'est pas trop proche <strong>de</strong>s autres, on l'ajoute à la liste<br />

31 if ajout:<br />

32 l.app<strong>en</strong>d ((xx,yy))<br />

33 n -= 1 # une ville <strong>en</strong> moins à choisir<br />

34 return l<br />

35<br />

36 <strong>de</strong>f distance_ville (l,i,j):<br />

37 """calcule la distance <strong>en</strong>tre <strong>de</strong>ux villes i et j <strong>de</strong> la liste l"""<br />

38 x = l [i][0] - l [j][0]<br />

39 y = l [i][1] - l [j][1]<br />

40 return math.sqrt (float (x*x+y*y))<br />

41<br />

42 <strong>de</strong>f construit_arete (l,part = 0.15):<br />

43 """tire aléatoirem<strong>en</strong>t part * l<strong>en</strong> (l) arêtes et construit la matrice<br />

44 d'adjac<strong>en</strong>ce"""<br />

45 global infini<br />

46 nb = l<strong>en</strong> (l)<br />

47 m = mat.Matrix ( [ 0 for i in range(0,nb) ]) # crée un vecteur <strong>de</strong> nb zéros<br />

48 m = ua.transpose (m) * m # effectue une multiplication du vecteur<br />

49 # précé<strong>de</strong>nt avec son vecteur transposé<br />

50 # pour obt<strong>en</strong>ir une matrice carrée nulle<br />

51<br />

52 are = int (part * nb * nb)<br />

53 while are > 0:<br />

54 i = random.randint (0,nb-1) # première ville<br />

55 j = random.randint (0,nb-1) # secon<strong>de</strong> ville<br />

56 if i == j : continue # pas besoin d'arête <strong>en</strong>tre i et i<br />

57 if m [i,j] > 0: continue # si l'arête existe déjà, on recomm<strong>en</strong>ce<br />

48


58 m [i,j] = int (distance_ville (l,i,j)) # on affecte comme poids à l'arête<br />

59 # la distance <strong>en</strong>tre les <strong>de</strong>ux villes<br />

60 m [j,i] = m [i,j] # symétrie <strong>de</strong> la matrice car le graphe est non ori<strong>en</strong>té<br />

61 are -= 2 # <strong>de</strong>ux cases <strong>de</strong> la matrice ne sont plus nulles<br />

62<br />

63 # on associe à toutes les arêtes nulles <strong>de</strong> poids nul, donc inexistantes,<br />

64 # une valeur égale à l'infini pour signifier qu'elles ne sont pas reliées<br />

65 global infini<br />

66 for i in range (0, nb):<br />

67 for j in range (0, nb):<br />

68 if m [i,j] == 0:<br />

69 m [i,j] = infini<br />

70<br />

71 return m<br />

72<br />

73 <strong>de</strong>f <strong>de</strong>ssin_ville_arete (l,m,chemin):<br />

74 """<strong>de</strong>ssine la ville et les routes dans une image"""<br />

75<br />

76 # on pr<strong>en</strong>d les coordonnées maximales<br />

77 mx, my = 0,0<br />

78 for i in l:<br />

79 mx = max (mx, i [0])<br />

80 my = max (my, i [1])<br />

81 mx += 25<br />

82 my += 25<br />

83 mx, my = int (mx), int (my)<br />

84 im = Im.new ("RGB", (mx, my), (255,255,255)) # création d'une image blanche<br />

85 draw = Id.Draw(im)<br />

86<br />

87 # <strong>de</strong>ssin <strong>de</strong>s villes<br />

88 for i in l:<br />

89 j = (int (i [0]), int (i[1]))<br />

90 j2 = (j [0] + 10, j [1] + 10)<br />

91 draw.ellipse ((j,j2), fill = (0,0,0))<br />

92<br />

93 # <strong>de</strong>ssin <strong>de</strong>s arêtes<br />

94 global infini<br />

95 for i in range (0,l<strong>en</strong>(l)):<br />

96 for j in range (0,l<strong>en</strong>(l)):<br />

97 if m [i,j] > 0 and m [i,j] < infini :<br />

98 a = (int (l [i][0]+5), int (l [i][1]+5))<br />

99 b = (int (l [j][0]+5), int (l [j][1]+5))<br />

100 draw.line ((a,b),fill=(255,0,0))<br />

101<br />

102 # <strong>de</strong>ssin <strong>de</strong>s villes <strong>de</strong> départ et d'arrivée <strong>en</strong> vert<br />

103 v1 = chemin [0]<br />

104 v2 = chemin [ l<strong>en</strong> (chemin)-1]<br />

105 a = (int (l [v1][0]), int (l [v1][1]))<br />

106 b = (int (l [v1][0]+10), int (l [v1][1]+10))<br />

107 draw.ellipse ((a,b), fill = (0,255,0))<br />

49


108 a = (int (l [v2][0]), int (l [v2][1]))<br />

109 b = (int (l [v2][0]+10), int (l [v2][1]+10))<br />

110 draw.ellipse ((a,b), fill = (0,255,0))<br />

111<br />

112 # <strong>de</strong>ssin du chemin, arêtes <strong>en</strong> bleu<br />

113 for c in range (0,l<strong>en</strong>(chemin)-1):<br />

114 i = chemin [c]<br />

115 j = chemin [c+1]<br />

116 if m [i,j] > 0 and m [i,j] < infini :<br />

117 a = (int (l [i][0]+5), int (l [i][1]+5))<br />

118 b = (int (l [j][0]+5), int (l [j][1]+5))<br />

119 draw.line ((a,b),fill=(0,0,255))<br />

120 else:<br />

121 a = (int (l [i][0]+5), int (l [i][1]+5))<br />

122 b = (int (l [j][0]+5), int (l [j][1]+5))<br />

123 draw.line ((a,b),fill=(0,0,50))<br />

124<br />

125 # on retourne l'image<br />

126 return im<br />

127<br />

128 <strong>de</strong>f meilleur_chemin (n,m,a,b):<br />

129 """détermine le meilleur chemin,<br />

130 n est le nombre <strong>de</strong> villes,<br />

131 m est la matrice d'adjac<strong>en</strong>ce,<br />

132 a est la ville <strong>de</strong> départ,<br />

133 b la ville d'arrivée"""<br />

134<br />

135 # création d'un tableau, d [i] conti<strong>en</strong>t la meilleure distance minimale actuelle<br />

136 # séparant la ville i <strong>de</strong> la ville a<br />

137 d = [ 10000000 for i in range(0,n) ]<br />

138<br />

139 # p [i] conti<strong>en</strong>t la ville prédécesseur qui permet d'atteindre la ville i<br />

140 # avec la distance d [i]<br />

141 p = [ -1 for i in range(0,n) ]<br />

142<br />

143 # au départ, seul la distance d[a] est nulle<br />

144 d [a] = 0<br />

145<br />

146 # cette boucle s'exécute tant qu'on effectue <strong>de</strong>s mises à jour<br />

147 # dans le tableau d[i]<br />

148 modif = 1<br />

149 while modif > 0:<br />

150 modif = 0<br />

151 # on parcourt toutes les arêtes<br />

152 for i in range(0,n):<br />

153 for j in range (0,n):<br />

154 # nouvelle distance<br />

155 t = d [i] + m [i,j]<br />

156 if t < d [j] : # si on a trouvé une meilleure distance minimale<br />

157 d [j] = t # on met à jour<br />

50


158 p [j] = i<br />

159 modif += 1 # une mise à jour <strong>de</strong> plus<br />

160<br />

161 # on récupère le meilleur chemin<br />

162 l = []<br />

163 while b != -1:<br />

164 l.app<strong>en</strong>d (b)<br />

165 b = p [b]<br />

166 # on le retourne<br />

167 l.reverse ()<br />

168 return l<br />

169<br />

170 <strong>de</strong>f choix_villes_<strong>de</strong>part_arrive(nb,m):<br />

171 """cette fonction choisit <strong>de</strong>ux villes aléatoirem<strong>en</strong>t, départ et arrivée,<br />

172 elle évite que la ville et départ et d'arrivée soi<strong>en</strong>t les mêmes,<br />

173 elle évite que ces <strong>de</strong>ux villes soi<strong>en</strong>t reliés par un seul arc,<br />

174 elle choisit <strong>de</strong>ux villes pour lesquelles il existe un meilleur<br />

175 chemin"""<br />

176 global infini<br />

177 a,b = -1,-1<br />

178 tour = 0<br />

179 while True:<br />

180 a = random.randint (0,nb-1) # première ville au hasard<br />

181 b = random.randint (0,nb-1) # secon<strong>de</strong> ville au hasard<br />

182 if a == b: continue # villes i<strong>de</strong>ntiques, on recomm<strong>en</strong>ce<br />

183 if m [a,b] != infini : continue # villes reliées, on recomm<strong>en</strong>ce<br />

184 l = meilleur_chemin (nb,m,a,b)<br />

185 if l != None and l<strong>en</strong>(l) > 3 :<br />

186 return a,b # si le meilleur chemin existe,<br />

187 # et n'est pas trop court (4 villes minimum,<br />

188 # soit <strong>de</strong>ux étapes <strong>en</strong>tre a et b), alors<br />

189 # on retourne le résultat<br />

190 else:<br />

191 tour += 1<br />

192 if tour > 120 : return 0,0 # au bout <strong>de</strong> 120 essais, on s'arrête<br />

193<br />

194 ###############################################################################<br />

195 # programme principal<br />

196 # construction <strong>de</strong>s villes<br />

197 l = construit_ville (15)<br />

198<br />

199 # construction <strong>de</strong>s arêtes<br />

200 print "adjac<strong>en</strong>ce"<br />

201 m = construit_arete (l)<br />

202<br />

203 # choix <strong>de</strong> la ville <strong>de</strong> départ <strong>de</strong> d'arrivée<br />

204 print "départ"<br />

205 a,b = choix_villes_<strong>de</strong>part_arrive(l<strong>en</strong>(l),m)<br />

206<br />

207 print "recherche du meilleur chemin"<br />

51


208 chemin = meilleur_chemin (l<strong>en</strong>(l), m, a,b)<br />

209<br />

210 if chemin != None and l<strong>en</strong>(chemin) > 0:<br />

211 print "meilleur chemin ", a, " --> ", b, " : ", chemin<br />

212 # construction <strong>de</strong> l'image du résultat<br />

213 im = <strong>de</strong>ssin_ville_arete(l,m,chemin)<br />

214 im.show () # on affiche l'image<br />

215 else :<br />

216 print "il n'existe pas <strong>de</strong> meilleur chemin"<br />

217<br />

n correction exemple E7<br />

⊓⊔<br />

52


8 Voyageur <strong>de</strong> commerce, carte <strong>de</strong> Kohon<strong>en</strong><br />

8.1 Enoncé E8<br />

Le problème du voyageur <strong>de</strong> commerce est un problème classique, aussi connu sous le nom <strong>de</strong> travevelling<br />

salesman problem. Le voyageur <strong>de</strong> commerce doit eectuer la tournée <strong>de</strong> ses magasins le plus rapi<strong>de</strong>m<strong>en</strong>t<br />

possible. Il doit donc déterminer un chemin passant par toutes les villes qui soit le plus court possible. La<br />

gure 8.1 donne un exemple <strong>de</strong> chemin passant par six villes.<br />

Cette problématique se retrouve égalem<strong>en</strong>t dans d'autres domaines comme la plannication <strong>de</strong> tâches.<br />

Par exemple, une machine doit eectuer un certain nombre <strong>de</strong> travaux, l'ordre dans lequel elle les traite<br />

importe peu, on <strong>de</strong>man<strong>de</strong> simplem<strong>en</strong>t que le temps <strong>de</strong> traitem<strong>en</strong>t <strong>de</strong> l'<strong>en</strong>semble <strong>de</strong>s tâches soit le plus<br />

court possible. Cep<strong>en</strong>dant, avant toute tâche, cette machine doit être préparée et le temps <strong>de</strong> préparation<br />

dép<strong>en</strong>d <strong>de</strong>s tâche précé<strong>de</strong>nte et suivante. Ce problème ressemble à celui du voyageur <strong>de</strong> commerce, les<br />

temps <strong>de</strong> préparation <strong>en</strong>tre <strong>de</strong>ux tâches jou<strong>en</strong>t le rôle <strong>de</strong> distance.<br />

Fig. 8.1 Plus court chemin passant par les six villes.<br />

Il existe plusieurs algorithmes pour déterminer ce plus court chemin. Deux seront prés<strong>en</strong>tés. Le premier<br />

est celui <strong>de</strong>s cartes <strong>de</strong> Kohon<strong>en</strong>, il s'applique <strong>de</strong> préfér<strong>en</strong>ce à un nombre <strong>de</strong> villes inférieur à un ou <strong>de</strong>ux<br />

c<strong>en</strong>taines. Au <strong>de</strong>là, lorsque les villes sont trop proches <strong>de</strong>s unes <strong>de</strong>s autres, cet algorithme retourne une<br />

solution où les erreurs apparaiss<strong>en</strong>t <strong>de</strong> manière évi<strong>de</strong>nte (voir gure 8.2). La secon<strong>de</strong> métho<strong>de</strong> est plutôt<br />

utilisée pour la recherche du plus court chemin pour <strong>de</strong>s graphes cont<strong>en</strong>ant un grand nombre <strong>de</strong> villes.<br />

Pour ces grands graphes, les cartes <strong>de</strong> Kohon<strong>en</strong> converg<strong>en</strong>t l<strong>en</strong>tem<strong>en</strong>t. Le plus court chemin est <strong>en</strong> fait<br />

une permutation <strong>de</strong> l'<strong>en</strong>semble <strong>de</strong>s villes, les <strong>de</strong>ux métho<strong>de</strong>s proposées sont <strong>de</strong>ux manières diér<strong>en</strong>tes<br />

d'explorer l'<strong>en</strong>semble <strong>de</strong>s permutations possibles.<br />

Obt<strong>en</strong>ir le plus court chemin passant par tous les sommets d'un graphe est un problème d'optimisation<br />

avec contrainte. C'est un problème dit NP-complet (voir dénition 8.1). Le site [www-TSP] est dédié à ce<br />

problème. Il référ<strong>en</strong>ce <strong>de</strong>s articles, quelques solutions à <strong>de</strong>s problèmes géographiques comme le plus court<br />

chemin reliant toutes les villes <strong>de</strong> Suè<strong>de</strong>. Il référ<strong>en</strong>ce égalem<strong>en</strong>t une librairie écrite <strong>en</strong> C proposant divers<br />

algorithmes permettant <strong>de</strong> résoudre le problème du voyageur <strong>de</strong> commerce.<br />

Dénition 8.1 : problème NP-complet<br />

Un problème est dit NP-complet s'il n'existe aucun algorithme <strong>de</strong> coût polynômial permettant<br />

d'obt<strong>en</strong>ir une solution optimale.<br />

Cette complexité peut se traduire <strong>en</strong> nombre, le docum<strong>en</strong>t [Helsgaun2000] cite un problème d'optimisation<br />

<strong>de</strong> 7397 rec<strong>en</strong>sé par le site [www-TSP] pour lequel il y a plus <strong>de</strong> 10 25000 solutions possibles. Le problème du<br />

voyageur <strong>de</strong> commerce apparti<strong>en</strong>t à une classe <strong>de</strong> problème plus vaste appelée optimisation combinatoire.<br />

Pour ce type <strong>de</strong> problème, l'<strong>en</strong>semble <strong>de</strong>s solutions possibles est discret et non continu.<br />

53


Fig. 8.2 Plus court chemin passant par les 300 villes, solution retournée par l'algorithme <strong>de</strong> Kohon<strong>en</strong>.<br />

Certaines erreurs cerclées <strong>de</strong> noir sont évi<strong>de</strong>ntes comme <strong>de</strong>ux segm<strong>en</strong>ts du chemin qui se crois<strong>en</strong>t. Celles-ci<br />

sont néanmoins facile à corriger une fois que la solution a convergé.<br />

8.1.1 Cartes <strong>de</strong> Kohon<strong>en</strong><br />

Les cartes <strong>de</strong> Kohon<strong>en</strong> sont utilisées pour réaliser la projection d'un nuage <strong>de</strong> points sur un espace <strong>de</strong><br />

dim<strong>en</strong>sion moindre. Dans le cas du problème du voyageur <strong>de</strong> commerce, il s'agit <strong>de</strong> projeter un nuage<br />

<strong>de</strong> points inclus dans un espace à <strong>de</strong>ux dim<strong>en</strong>sions vers un espace à une dim<strong>en</strong>sion. Chaque ville recevra<br />

une coordonnée : son ordre dans le chemin le plus court. Les cartes <strong>de</strong> Kohon<strong>en</strong> sont plus <strong>fr</strong>équemm<strong>en</strong>t<br />

utilisées pour projeter un nuage <strong>de</strong> points inclus dans un espace à n > 2 dim<strong>en</strong>sions vers un espace à <strong>de</strong>ux<br />

dim<strong>en</strong>sions, soit un espace qui puiss<strong>en</strong>t être représ<strong>en</strong>té graphiquem<strong>en</strong>t (voir gure 8.3). Ceci explique leur<br />

désignation <strong>de</strong> cartes <strong>de</strong> Kohon<strong>en</strong> ou <strong>en</strong> anglais Self Organized Map (SOM). Le docum<strong>en</strong>t [Koivisto1999]<br />

(voir aussi [Vesanto2000]) regroupe plusieurs articles à propos <strong>de</strong> diverses techniques et diverses utilisations<br />

<strong>de</strong>s cartes <strong>de</strong> Kohon<strong>en</strong> dans <strong>de</strong>s problèmes tels que la reconnaissance <strong>de</strong> la parole, les modèles <strong>de</strong> Markov<br />

cachés, la recherche <strong>de</strong> mot-clé dans un texte, l'analyse <strong>en</strong> composantes principales (ACP).<br />

On suppose qu'un chemin est constitué <strong>de</strong> points ou neurones, diér<strong>en</strong>ts ou non <strong>de</strong>s villes, chacun étant<br />

reliés à ses <strong>de</strong>ux voisins. L'idée principale <strong>de</strong> l'algorithme consiste à faire <strong>en</strong> sorte que chaque villes tire<br />

vers elle un neurone et ses voisins. Au cours <strong>de</strong>s itérations successives, la longueur du chemin s'agrandit<br />

54


Fig. 8.3 Exemple <strong>de</strong> cartes <strong>de</strong> Kohon<strong>en</strong> après converg<strong>en</strong>ce. Il s'agit <strong>de</strong> projeter dans un plan un nuage <strong>de</strong><br />

points <strong>en</strong> trois dim<strong>en</strong>sions. Chaque neurone possè<strong>de</strong> six voisins. Plane au départ, la carte se déforme pour<br />

s'adapter au nuage <strong>de</strong> points qu'elle doit modéliser. La secon<strong>de</strong> carte est plus déformée que la première et<br />

prés<strong>en</strong>te quelques torsions qui ressembl<strong>en</strong>t à un papillon. Ces gures sont tirées <strong>de</strong> l'article [Vesanto2000].<br />

et il se rapproche peu à peu <strong>de</strong>s villes jusqu'à que chaque ville soit rejointe par un neurone.<br />

Algorithme 8.2 : carte <strong>de</strong> Kohon<strong>en</strong><br />

On note n villes par (v 1 , . . . , v n ) ∈ ( R k) n<br />

avec k 2. Un chemin <strong>de</strong> longueur l est noté<br />

(p 1 , . . . , p l ) ∈ ( R k) l , on suppose que les voisins du neurone pi sont les neurones d'indices (i − 1)<br />

mod l et (i + 1) mod l. La fonction d : R k × R k −→ R désigne une distance.<br />

Etape A : initialisation<br />

Le chemin est constitué <strong>de</strong> trois neurones placés autour du baryc<strong>en</strong>tre <strong>de</strong> l'<strong>en</strong>semble<br />

(v 1 , . . . , v n ).<br />

t ←− 0, t compte le nombre d'itérations.<br />

Etape B : itération<br />

On dénit le vecteur (c 1 , . . . , c l ) = 0.<br />

t ←− t + 1<br />

pour i = 1 à n faire<br />

On détermine l'indice j ∗ du neurone le plus proche <strong>de</strong> la ville i.<br />

c j ∗ ←− c j ∗ + 1<br />

p j ∗ ←− p j ∗ + ɛ (t, d (v i , p j ∗)) [v i − p j ∗]<br />

On met à jour chacun <strong>de</strong>s voisins q <strong>de</strong> p j ∗ <strong>de</strong> la façon suivante :<br />

q ←− q + η (t, d (v i , p j ∗) , d (q, p j ∗)) [v i − p j ∗]<br />

n pour<br />

Etape C : ajout, suppression <strong>de</strong> neurones<br />

Pour chaque neurone d'indice i du chemin, si c i = 0 alors il est supprimé. Ceci<br />

signie qu'aucune ville ne l'a choisi. Dans le cas contraire, si c i 2, plus d'une<br />

ville l'a choisi, le neurone est dupliqué. C'est-à-dire qu'un autre neurone est<br />

inséré dans le chemin et placé à proximité du neurone dupliqué.<br />

Etape D : terminaison<br />

Tant que les neurones boug<strong>en</strong>t <strong>en</strong>core, on retourne à l'étape A.<br />

55


An que l'algorithme converge, on choisit <strong>de</strong>s fonctions ɛ (t, x) et η (t, x) décroissantes par rapport à t. Il<br />

est conseillé <strong>de</strong> choisir <strong>de</strong>s fonctions qui véri<strong>en</strong>t :<br />

∞∑<br />

∞∑<br />

∀x ∈ R + , ɛ (t, x) = ∞ et ɛ 2 (t, x) < ∞ (8.1)<br />

t=1<br />

t=1<br />

∀t, x, y ∈ N × R + × R + , η (t, x, y) < ɛ (t, x) (8.2)<br />

On peut choisir par exemple :<br />

ɛ (t, x) = ɛ 0<br />

1 + t<br />

d 0<br />

d 0 + x<br />

(8.3)<br />

η (t, x) = η 0<br />

ɛ (t, x) (8.4)<br />

1 + t<br />

Il est souv<strong>en</strong>t utile d'eectuer plusieurs essais pour déterminer les valeurs ɛ 0 , η 0 , d 0 . Ces fonctions peuv<strong>en</strong>t<br />

égalem<strong>en</strong>t dép<strong>en</strong>dre d'autres paramètres tels que l'angle formé par un neurone et ses <strong>de</strong>ux voisins. Il est<br />

aussi possible qu'une ville attire un neurone, ses <strong>de</strong>ux neurones les plus proches puis <strong>en</strong>core d'autres voisins<br />

plus éloignés. Il n'existe pas <strong>de</strong> fonction optimale, elles dép<strong>en</strong><strong>de</strong>nt du problème à résoudre.<br />

Pour améliorer la converg<strong>en</strong>ce <strong>de</strong> l'algorithme, plutôt que <strong>de</strong> considérer à l'étape A les villes toujours dans<br />

le même ordre, elles sont toutes passées <strong>en</strong> revue dans un ordre aléatoire et diér<strong>en</strong>t à chaque itération.<br />

Cette modication permet <strong>de</strong> relancer plusieurs fois l'algorithme pour choisir au nal le chemin <strong>de</strong> plus<br />

courte longueur.<br />

La suppression et l'insertion <strong>de</strong> neurones est facile à concevoir lorsque la carte <strong>de</strong> Kohon<strong>en</strong> n'est qu'un<br />

chemin à une dim<strong>en</strong>sion. Lorsqu'il s'agit d'un maillage à <strong>de</strong>ux dim<strong>en</strong>sions, cette étape n'est pas incluse.<br />

L'initialisation consiste alors à placer un nombre <strong>de</strong> neurones xe puis à appliquer l'étape A. Le voisinage<br />

est simplem<strong>en</strong>t plus ét<strong>en</strong>du.<br />

Le coût <strong>de</strong> cet algorithme dép<strong>en</strong>d <strong>de</strong> la vitesse <strong>de</strong> converg<strong>en</strong>ce. Toutefois, l'étape B a un coût <strong>de</strong> n 2 où n<br />

est le nombre <strong>de</strong> villes, coût à multiplier par le nombre d'itérations. Les paragraphes qui suiv<strong>en</strong>t propos<strong>en</strong>t<br />

un autre algorithme permettant <strong>de</strong> construire une solution pour le problème du voyageur <strong>de</strong> commerce. Il<br />

est beaucoup plus rapi<strong>de</strong> mais il explore davantage l'<strong>en</strong>semble <strong>de</strong>s solutions.<br />

8.1.2 Arbre <strong>de</strong> poids minimal et circuit hamiltoni<strong>en</strong><br />

Ce paragraphe décrit un algorithme permettant <strong>de</strong> construire un chemin dont on espère qu'il sera le<br />

plus court. Ilpart d'un <strong>en</strong>semble <strong>de</strong> villes pour construire un circuit hamiltoni<strong>en</strong> (voir dénition 8.4). Cet<br />

algorithme utilise <strong>de</strong>ux chemins d'un type particulier, euléri<strong>en</strong> et hamiltoni<strong>en</strong>.<br />

Dénition 8.3 : circuit euléri<strong>en</strong><br />

Un chemin euléri<strong>en</strong> d'un graphe est un chemin passant par tous les arcs <strong>de</strong> ce graphe. Un circuit<br />

euléri<strong>en</strong> est un chemin euléri<strong>en</strong> dont les n÷uds <strong>de</strong> départ et d'arrivée sont i<strong>de</strong>ntiques.<br />

Dénition 8.4 : circuit hamiltoni<strong>en</strong><br />

Un chemin hamiltoni<strong>en</strong> d'un graphe est un chemin passant par tous les n÷uds <strong>de</strong> ce graphe.<br />

Un circuit hamiltoni<strong>en</strong> est un chemin hamiltoni<strong>en</strong> dont les n÷uds <strong>de</strong> départ et d'arrivée sont<br />

i<strong>de</strong>ntiques.<br />

56


La première étape <strong>de</strong> l'algorithme consiste à construire un arbre <strong>de</strong> poids minimum (voir dénition 8.7).<br />

Si le chemin doit parcourir n villes, cette première étape permet <strong>de</strong> réduire l'<strong>en</strong>semble <strong>de</strong>s arcs possibles<br />

( n(n−1)<br />

2<br />

) à un multiple <strong>de</strong> n.<br />

Algorithme 8.5 : plus court chemin à partir d'un arbre <strong>de</strong> poids minimum<br />

On suppose qu'il faut déterminer le plus court chemin passant par n villes. L'algorithme se<br />

décompose <strong>en</strong> quatre étapes :<br />

1. construction <strong>de</strong> l'arbre <strong>de</strong> poids minimum (algorithme 8.8)<br />

2. construction d'un circuit euléri<strong>en</strong> (algorithme 8.9)<br />

3. construction d'un circuit hamiltoni<strong>en</strong> (algorithme 8.10)<br />

4. simplication du circuit hamiltoni<strong>en</strong> (algorithme 8.11)<br />

Le circuit euléri<strong>en</strong> parcourt tous les arcs <strong>de</strong> poids minimal et passe donc plusieurs fois par le même<br />

sommet. En <strong>en</strong>levant les sommets redondants, on obti<strong>en</strong>t un circuit hamiltoni<strong>en</strong> qui est une solution pour<br />

le problème du voyageur <strong>de</strong> commerce. L'avantage <strong>de</strong> cet algorithme est son coût, multiple du nombre <strong>de</strong><br />

villes.<br />

8.1.3 Arbre <strong>de</strong> poids minimum<br />

Obt<strong>en</strong>ir l'arbre <strong>de</strong> poids minimum est <strong>en</strong>core un problème d'optimisation avec contrainte. Il s'agit <strong>de</strong><br />

réduire l'<strong>en</strong>semble <strong>de</strong>s arcs tout <strong>en</strong> conservant un graphe composé d'une seule composante connexe.<br />

Dénition 8.6 : composante connexe<br />

Une composante connexe C d'un graphe est un <strong>en</strong>semble <strong>de</strong> n÷uds tel que pour tout couple <strong>de</strong><br />

n÷uds (s, t) ∈ C 2 , il existe un chemin allant <strong>de</strong> s à t dont tous les n÷uds intermédiaires sont<br />

inclus dans C.<br />

Il est maint<strong>en</strong>ant possible d'exprimer la contrainte liée au problème <strong>de</strong> l'arbre <strong>de</strong> poids minimum.<br />

Dénition 8.7 : arbre <strong>de</strong> poids minimum<br />

Soit un graphe G = (X, E), un arbre <strong>de</strong> poids minimum G ∗ = (X ∗ , E ∗ ) est un sous-arbre <strong>de</strong> G<br />

qui vérie :<br />

1. X ∗ = X, le graphe <strong>de</strong> poids minimum partage les mêmes sommets que le graphe initial.<br />

2. E ∗ ⊂ E, les arcs graphe <strong>de</strong> poids minimum possè<strong>de</strong> forme un sous-<strong>en</strong>semble <strong>de</strong> l'<strong>en</strong>semble<br />

<strong>de</strong>s arcs du graphe initial.<br />

3. La somme <strong>de</strong>s poids <strong>de</strong>s arcs <strong>de</strong> E ∗ est minimale.<br />

4. Le graphe G ∗ conti<strong>en</strong>t autant <strong>de</strong> composantes connexes que le graphe initial.<br />

L'algorithme qui suit n'est pas le seul permettant d'obt<strong>en</strong>ir une solution approchée à ce problème d'optimisation.<br />

Il ne considère que le cas où le graphe ne conti<strong>en</strong>t qu'une seule composante connexe. Il consiste<br />

à partir d'un graphe débarrassé <strong>de</strong> tous ses arcs puis à ajouter <strong>de</strong>s arcs reliant <strong>de</strong>ux composantes connexes<br />

57


diér<strong>en</strong>tes jusqu'à ce qu'il n'<strong>en</strong> reste plus qu'une.<br />

Algorithme 8.8 : algorithme <strong>de</strong> Kruskal<br />

On suppose que G(X, E) est un graphe, X est l'<strong>en</strong>semble <strong>de</strong>s sommets, E est l'<strong>en</strong>semble <strong>de</strong>s<br />

arcs. Si e ∈ E, f(e) désigne son poids. Cet algorithme a pour but d'obt<strong>en</strong>ir un sous-<strong>en</strong>semble<br />

F ⊂ E <strong>de</strong> poids minimal tel que le graphe G(X, F ) ne forme qu'une seule composante connexe.<br />

Le nombre <strong>de</strong> sommets est N. L'arc e relie les sommets a(e) −→ b(e).<br />

Etape A : initialisation<br />

On trie les arcs par ordre croissant <strong>de</strong> poids, on obti<strong>en</strong>t la suite (e 1 , . . . , e n ). A<br />

chaque sommet x, on associe la composante connexe c(x). n ←− N, n désigne<br />

le nombre <strong>de</strong> composantes connexes. F ←− ∅<br />

Etape B : itération<br />

i ←− 1<br />

tant que (N > 1 ) faire<br />

si c(a(e i )) ≠ c(b(e i ))<br />

F ←− F ∪ {e i }<br />

pour chaque x ∈ X faire<br />

c(x) =<br />

n pour<br />

n si<br />

i ←− i + 1<br />

n tant que<br />

{ c(x) si c(x) ≠ c(b(ei ))<br />

c(a(e i )) si c(x) = c(b(e i ))<br />

Cet algorithme ne retourne pas la solution optimale mais une solution approchée. Son coût est proportionnel<br />

au cardinal <strong>de</strong> l'<strong>en</strong>semble E. A priori, pour le problème du voyageur <strong>de</strong> commerce, si le graphe<br />

conti<strong>en</strong>t n villes, le nombre d'arcs possibles est n(n−1)<br />

2<br />

. Dans cet <strong>en</strong>semble, la majeure partie <strong>de</strong>s arcs ne<br />

sera pas utilisée pour le meilleur chemin puisque les arcs <strong>en</strong>tre plus proches voisins leur seront préférés.<br />

An <strong>de</strong> réduire cet <strong>en</strong>semble, il est possible <strong>de</strong> quadriller le plan <strong>en</strong> zones et d'associer à chaque ville la<br />

zone à laquelle il apparti<strong>en</strong>t. On peut ainsi se cont<strong>en</strong>ter <strong>de</strong> construire l'arbre <strong>de</strong> poids minimal à l'ai<strong>de</strong><br />

<strong>de</strong>s arcs <strong>en</strong>tre villes <strong>de</strong> même zone ou <strong>de</strong> zones voisines (voir gure 8.4). La gure 8.5 montre un exemple<br />

d'arbre <strong>de</strong> poids optimal obt<strong>en</strong>u par l'algorithme <strong>de</strong> Kruskal.<br />

8.1.4 Circuit euléri<strong>en</strong><br />

Le graphe obt<strong>en</strong>u par l'algorithme 8.9 est dans notre cas non ori<strong>en</strong>té. Il est possible <strong>de</strong> passer d'une ville<br />

à une autre puis d'<strong>en</strong> rev<strong>en</strong>ir. Ceci signie donc que chaque sommet est connecté à un nombre pair d'arcs.<br />

Par conséqu<strong>en</strong>t, il est possible <strong>de</strong> construire un chemin qui passe une seule fois par tous les arcs du graphe<br />

(voir gure 7.3, page 43).<br />

La dénition d'un circuit euléri<strong>en</strong> ne fait pas interv<strong>en</strong>ir <strong>de</strong> considérations géométriques. Toutefois, pour<br />

déterminer un circuit euléri<strong>en</strong>, nous allons utiliser les coordonnées <strong>de</strong>s villes qui compos<strong>en</strong>t les sommets<br />

du graphe an <strong>de</strong> parcourir cet arbre selon le s<strong>en</strong>s trigonométrique inverse (voir gure 8.6).<br />

58


Fig. 8.4 30000 villes sont réparties dans ce carré. Evaluer chaque arc est alors beaucoup trop coûteux.<br />

Une solution simple consiste à diviser le plan <strong>en</strong> zone puis à ne considérer les arcs <strong>en</strong>tre <strong>de</strong>ux sommets <strong>de</strong><br />

la même zone ou <strong>de</strong> <strong>de</strong>ux zones voisines. L'<strong>en</strong>semble <strong>de</strong>s arcs pris <strong>en</strong> compte dans l'algorithme <strong>de</strong> Kruskal<br />

(8.8) n'est plus n(n−1)<br />

2<br />

mais un multiple <strong>de</strong> n d'autant plus grand que les zones sont gran<strong>de</strong>s. On peut<br />

par exemple déterminer le nombre <strong>de</strong> zones z <strong>en</strong> essayant <strong>de</strong> faire <strong>en</strong> sorte qu'elles conti<strong>en</strong>n<strong>en</strong>t un nombre<br />

constant α <strong>de</strong> villes. Le nombre d'arcs <strong>en</strong>visagés est majoré par : 9αn.<br />

Fig. 8.5 <strong>Exemples</strong> d'arbres obt<strong>en</strong>us par l'algorithme <strong>de</strong> Kruskal (8.8). Le premier pour une c<strong>en</strong>taine <strong>de</strong><br />

villes, le second pour <strong>en</strong>viron 3000 villes.<br />

59


Fig. 8.6 Parcours <strong>de</strong> l'arbre <strong>de</strong> poids minimal <strong>de</strong> façon à former un circuit euléri<strong>en</strong>. On part d'une<br />

extrémité puis on parcourt le graphe dans le s<strong>en</strong>s trigonométrique inverse jusqu'à rev<strong>en</strong>ir au point <strong>de</strong><br />

départ.<br />

Algorithme 8.9 : construction d'un circuit euléri<strong>en</strong><br />

On suppose que le graphe dont il faut obt<strong>en</strong>ir un circuit euléri<strong>en</strong> est un arbre non-ori<strong>en</strong>té <strong>de</strong><br />

poids minimal comme celui retourné par l'algorithme 8.8. On suppose égalem<strong>en</strong>t qu'à chaque<br />

sommet x sont associés <strong>de</strong>s coordonnées p(x) et que <strong>de</strong>ux sommets ne sont jamais confondus.<br />

L'arbre conti<strong>en</strong>t n sommets et 2n arcs.<br />

Etape A : initialisation<br />

On choisit un n÷ud x connecté<br />

{<br />

à un seul autre sommet. ch ←− (x) et t ←− 1.<br />

1 si l'arc a été parcouru<br />

Pour chaque arc e, u(e) =<br />

0 sinon<br />

Etape B : itération<br />

tant que (t < 2n) faire<br />

x désigne le <strong>de</strong>rnier sommet visité, x − désigne le sommet précé<strong>de</strong>nt dans le<br />

chemin ch. On choisit le sommet suivant x + <strong>de</strong> telle sorte que :<br />

1. L'arc e = (x −→ x + ) existe et vérie c(e) = 0.<br />

2. Parmi tous les arcs ( vériant la première condition, on choisit celui qui<br />

−−−−−−→<br />

maximise l'ange p(x − )p(x), p(x)p(x −−−−−−→ )<br />

+ ) .<br />

t ←− t + 1<br />

ch ←− ch ∪ (x + )<br />

c(x → x + ) ←− 1<br />

n tant que<br />

ch est le chemin euléri<strong>en</strong> cherché.<br />

Le coût <strong>de</strong> cet algorithme est <strong>en</strong> O(n).<br />

8.1.5 Circuit hamiltoni<strong>en</strong><br />

A partir d'un circuit euléri<strong>en</strong>, on construit un circuit hamiltoni<strong>en</strong> <strong>en</strong> évitant simplem<strong>en</strong>t les n÷uds déjà<br />

parcourus. Cette construction est possible puisque le graphe est <strong>en</strong>tièrem<strong>en</strong>t connecté. Il est donc possible<br />

<strong>de</strong> passer d'un n÷ud quelconque à un autre n÷ud quelconque lui aussi. Il est possible que cet arc<br />

60


n'apparti<strong>en</strong>ne pas au graphe.<br />

Algorithme 8.10 : construction d'un circuit hamiltoni<strong>en</strong><br />

On suppose que le graphe G = (X, E) est <strong>en</strong>tièrem<strong>en</strong>t connecté. ch désigne un chemin euléri<strong>en</strong>.<br />

Etape A : initialisation<br />

Pour les sommets x ∈ X, on pose c(x) = 0. H ←− ∅, H est le chemin hamiltoni<strong>en</strong><br />

cherché.<br />

Etape B : parcours<br />

On parcourt le chemin euléri<strong>en</strong> ch dans l'ordre. Pour chaque sommet x du<br />

chemin, si x n'a pas <strong>en</strong>core été visité (c(x) = 0) alors H ←− H ∪ (x) et<br />

c(x) = 1. On poursuit avec les sommets suivants.<br />

Fig. 8.7 Résultat obt<strong>en</strong>u pour un <strong>en</strong>semble <strong>de</strong> 300 villes, la solution retournée est obt<strong>en</strong>ue rapi<strong>de</strong>m<strong>en</strong>t<br />

mais prés<strong>en</strong>te <strong>de</strong>s erreurs évi<strong>de</strong>ntes qu'il est possible <strong>de</strong> corriger <strong>en</strong> échangeant la position <strong>de</strong> n÷uds dans<br />

le chemin (voir gure 8.8). Les arcs verts font partie <strong>de</strong> l'arbre <strong>de</strong> poids minimal, les arcs noirs font partie<br />

du circuit hamiltoni<strong>en</strong>.<br />

La gure 8.7 montre un chemin hamiltoni<strong>en</strong> obt<strong>en</strong>u pour 300 villes. Le coût <strong>de</strong> cet algorithme est <strong>en</strong> O(n).<br />

8.1.6 Simplication du circuit hamiltoni<strong>en</strong><br />

La gure 8.7 montre <strong>de</strong>s imperfections évi<strong>de</strong>ntes qui pourrai<strong>en</strong>t être corrigées simplem<strong>en</strong>t <strong>en</strong> échangeant<br />

la position <strong>de</strong> n÷uds dans le chemin hamiltoni<strong>en</strong> obt<strong>en</strong>u après l'exécution <strong>de</strong>s trois premières étapes<br />

<strong>de</strong> l'algorithme 8.5. Un échange <strong>de</strong> n÷uds est pertin<strong>en</strong>t s'il fait décroître la longueur du chemin. Une<br />

autre solution consiste à déplacer un morceau du chemin pour l'insérer <strong>en</strong>tre <strong>de</strong>ux villes. L'<strong>en</strong>semble <strong>de</strong>s<br />

possibilités <strong>en</strong>visagées sont inspirées <strong>de</strong> [Helsgaun2000] et sont illustrées par la gure 8.8.<br />

61


a<br />

b<br />

c<br />

d<br />

Fig. 8.8 Trois Schémas simplicateurs <strong>en</strong>visagés pour supprimer les croisem<strong>en</strong>ts <strong>en</strong>tre segm<strong>en</strong>ts d'un<br />

chemin. La première image représ<strong>en</strong>te un chemin. La secon<strong>de</strong> image représ<strong>en</strong>te le schéma d'un retournem<strong>en</strong>t<br />

d'une portion du chemin. Ceci permet d'éviter que le chemin décrive une gure <strong>en</strong> forme <strong>de</strong> ∞. La<br />

troisième gure prés<strong>en</strong>te le déplacem<strong>en</strong>t d'une portion du chemin <strong>en</strong>tre <strong>de</strong>ux autres n÷uds. La <strong>de</strong>rnière<br />

image prés<strong>en</strong>te le déplacem<strong>en</strong>t et le retournem<strong>en</strong>t d'une portion du chemin <strong>en</strong>tre <strong>de</strong>ux villes.<br />

L'algorithme qui suit repr<strong>en</strong>d le schéma développé par Lin-Kernighan (voir [Helsgaun2000]).<br />

Algorithme 8.11 : simplication du circuit hamiltoni<strong>en</strong><br />

Soit un circuit hamiltoni<strong>en</strong> v = (v 1 , . . . , v n ) passant par les n n÷uds - ou villes - d'un graphe.<br />

Pour tout i /∈ {1, . . . , n}, on dénit la ville v i par v i = v i≡n . Il est possible d'associer à ce chemin<br />

∑<br />

un coût égal à la somme <strong>de</strong>s poids associés aux arêtes c = n c (v i , v i+1 ). Cet algorithme consiste<br />

à opérer <strong>de</strong>s modications simples sur le chemin v tant que son coût c décroît. Les opérations<br />

proposées sont :<br />

1. Le retournem<strong>en</strong>t (image b, gure 8.8) : il consiste à retourner une sous-partie du chemin.<br />

Si on retourne le sous-chemin <strong>en</strong>tre les villes i et j, le chemin complet <strong>de</strong>vi<strong>en</strong>t<br />

(v 1 , ..., v i−1 , v j , v j−1 , ..., v i , v j+1 , ..., v n ). Le retournem<strong>en</strong>t dép<strong>en</strong>d <strong>de</strong> <strong>de</strong>ux paramètres.<br />

2. Le déplacem<strong>en</strong>t (image d, gure 8.8) : il consiste à déplacer une sous-partie du chemin.<br />

Si on déplace le sous-chemin <strong>en</strong>tre les villes i et j <strong>en</strong>tre les villes k et k + 1, le chemin<br />

complet <strong>de</strong>vi<strong>en</strong>t (v 1 , ..., v i−1 , v j+1 , ..., v k , v i , v i+1 , ..., v j , v k+1 , v n ). Le déplacem<strong>en</strong>t dép<strong>en</strong>d <strong>de</strong><br />

trois paramètres.<br />

3. Le déplacem<strong>en</strong>t retourné (image d, gure 8.8) : il allie les <strong>de</strong>ux procédés précé<strong>de</strong>nts. Si<br />

on déplace et on retourne le sous-chemin <strong>en</strong>tre les villes i et j <strong>en</strong>tre les villes k et k + 1,<br />

le chemin complet <strong>de</strong>vi<strong>en</strong>t (v 1 , ..., v i−1 , v j+1 , ..., v k , v j , v j−1 , ..., v i , v k+1 , v n ). Le déplacem<strong>en</strong>t<br />

retourné dép<strong>en</strong>d aussi <strong>de</strong> trois paramètres.<br />

i=1<br />

Ces <strong>de</strong>ux opérations (retournem<strong>en</strong>t, déplacem<strong>en</strong>t) dép<strong>en</strong><strong>de</strong>nt d'au plus trois paramètres. Le coût <strong>de</strong> cet<br />

62


algorithme est donc <strong>en</strong> O(n 3 ), ce qui est très coûteux lorsque le nombre <strong>de</strong> villes dépasse quelques milliers 5<br />

C'est pourquoi toutes les combinaisons possibles pour les <strong>de</strong>ux paramètres d'un retournem<strong>en</strong>t ou les trois<br />

paramètres d'un déplacem<strong>en</strong>t ne seront pas <strong>en</strong>visagées. Peu d'<strong>en</strong>tre elles sont susceptibles d'avoir un<br />

résultat positif et pour un grand nombre <strong>de</strong> villes, le temps d'exécution <strong>de</strong>vi<strong>en</strong>t très long. Les contraintes<br />

choisies sur les paramètres détermin<strong>en</strong>t la vitesse <strong>de</strong> converg<strong>en</strong>ce et aecte les performances.<br />

Concernant les contraintes, la première idée (voir [Helsgaun2000]) est <strong>de</strong> se resteindre au retournem<strong>en</strong>t<br />

ou au déplacem<strong>en</strong>t <strong>de</strong> sous-chemin d'au plus quelques villes - pas plus d'une dizaine -. La secon<strong>de</strong> idée<br />

consiste à se conc<strong>en</strong>trer sur <strong>de</strong>s zones où il paraît possible <strong>de</strong> diminuer la longueur du chemin. La gure 8.9<br />

montre quelques schémas récurr<strong>en</strong>ts que les retournem<strong>en</strong>ts ou déplacem<strong>en</strong>ts cherch<strong>en</strong>t à résorber ainsi que<br />

l'utilisation <strong>de</strong> zones pour repérer le lieu probable <strong>de</strong> ces schémas. L'inconvéni<strong>en</strong>t d'une telle métho<strong>de</strong> est<br />

qu'elle n'est applicable que si les n÷uds du graphe ont <strong>de</strong>s coordonnées.<br />

Fig. 8.9 Cette gure montre un exemple <strong>de</strong> chemin hamiltoni<strong>en</strong> <strong>de</strong> 500 villes avant l'utilisation <strong>de</strong><br />

l'algorithme 8.11. Les zones a et c représ<strong>en</strong>t<strong>en</strong>t une intersection <strong>de</strong> segm<strong>en</strong>ts. Les zones b et d conti<strong>en</strong>n<strong>en</strong>t<br />

un ville dont le déplacem<strong>en</strong>t dans un segm<strong>en</strong>t proche diminuerait la longueur du chemin. An <strong>de</strong> repérer<br />

plus rapi<strong>de</strong>m<strong>en</strong>t les lieux probables où il est possible <strong>de</strong> raccourcir le chemin, on quadrille l'image puis on<br />

rec<strong>en</strong>se pour chaque case l'<strong>en</strong>semble <strong>de</strong>s arêtes la traversant, puis l'<strong>en</strong>semble <strong>de</strong>s villes aux extrémites <strong>de</strong> ces<br />

arêtes. Ceci permet d'extraire une liste <strong>de</strong> villes pour lesquelles il est intéressant <strong>de</strong> tester <strong>de</strong>s hypothèses <strong>de</strong><br />

retournem<strong>en</strong>ts ou <strong>de</strong> déplacem<strong>en</strong>ts. Par exemple, pour les zones colorées à droite <strong>de</strong> l'image, le chemin peut<br />

être raccourci <strong>de</strong> manière évi<strong>de</strong>nte mais il n'est pas nécessaire <strong>de</strong> tester <strong>de</strong>s hypothèses <strong>de</strong> retournem<strong>en</strong>ts<br />

ou <strong>de</strong> déplacem<strong>en</strong>ts associant <strong>de</strong>s villes situées dans cette zone avec <strong>de</strong>s villes situées à l'extérieur.<br />

Remarque 8.12: retournem<strong>en</strong>t, déplacem<strong>en</strong>t, ...<br />

Il est possible d'imaginer d'autres transformations que les retournem<strong>en</strong>ts ou déplacem<strong>en</strong>ts. Il y a par<br />

exemple les permutations où <strong>de</strong>ux sous-parties qui peuv<strong>en</strong>t être <strong>de</strong> longueurs diér<strong>en</strong>tes sont permutées.<br />

Chaque transformation dép<strong>en</strong>d d'un certain nombre <strong>de</strong> paramètres ou <strong>de</strong>grés <strong>de</strong> liberté, plus ils sont nom-<br />

5 Le coût <strong>de</strong>s algorithmes 8.8, 8.9, 8.10 qui précè<strong>de</strong>nt celui-ci est <strong>en</strong> O(n).<br />

63


eux, plus l'optimisation du chemin a <strong>de</strong> chance d'aboutir au chemin optimal, et plus cette optimisation<br />

sera longue. En règle générale, plus le chemin à optimiser est long, moins les transformations choisies<br />

seront complexes, d'abord parce que cela pr<strong>en</strong>drait trop <strong>de</strong> temps, <strong>en</strong>suite parce que le gain qu'on peut <strong>en</strong><br />

att<strong>en</strong>dre est moins important sur <strong>de</strong> grands problèmes. En eet, pour un circuit optimal avec peu d'étapes,<br />

changer une arête augm<strong>en</strong>te beaucoup sa longueur. Sur un circuit optimal avec beaucoup d'étape, changer<br />

une arête a généralem<strong>en</strong>t peu d'impact comparé à la longueur totale du circuit.<br />

Remarque 8.13: utilité <strong>de</strong> l'arbre <strong>de</strong> poids minimum<br />

L'algorithme 8.11 peut tout-à-fait être utilisé seul à partir d'un circuit hamiltoni<strong>en</strong> initial déterminé <strong>de</strong><br />

manière aléatoire. Pour <strong>de</strong>s problèmes <strong>de</strong> petites tailles (quelques c<strong>en</strong>taines <strong>de</strong> villes), la détermination<br />

d'un premier circuit hamiltoni<strong>en</strong> à partir d'un arbre <strong>de</strong> poids minimum n'est pas nécessaire. En revanche,<br />

pour <strong>de</strong>s problèmes <strong>de</strong> plus gran<strong>de</strong>s tailles, cette première étape dont le coût est <strong>en</strong> O(n) permet d'accélérer<br />

la converg<strong>en</strong>ce <strong>de</strong> l'algorithme 8.11.<br />

Remarque 8.14: converg<strong>en</strong>ce <strong>de</strong> l'algorithme 8.11<br />

Etape après étape, l'algorithme 8.11 fait décroître la longueur du chemin. Il n'est pas toujours nécessaire <strong>de</strong><br />

terminer l'algorithme lorsque cette longueur ne décroît plus mais lorsqu'elle ne décroît plus susamm<strong>en</strong>t.<br />

La gure 8.10 repr<strong>en</strong>d un exemple d'évolution <strong>de</strong> la longueur du chemin étape après étape. La courbe<br />

décroît rapi<strong>de</strong>m<strong>en</strong>t au cours <strong>de</strong>s premières itérations puis décroît l<strong>en</strong>tem<strong>en</strong>t <strong>en</strong>suite.<br />

500 villes 1500 villes<br />

Fig. 8.10 <strong>Exemples</strong> <strong>de</strong> décroissance <strong>de</strong> la longueur du chemin obt<strong>en</strong>ue avec l'algorithme 8.11. L'ess<strong>en</strong>tiel<br />

<strong>de</strong>s améliorations est faite dans la première moitié <strong>de</strong>s itérations. Pour ces <strong>de</strong>ux expéri<strong>en</strong>ces, 500 villes,<br />

puis 1500 villes, ont été réparties aléatoirem<strong>en</strong>t dans un rectangle 800 × 500.<br />

8.2 Programme, explications <strong>de</strong> l'exemple E8<br />

Les paragraphes qui suiv<strong>en</strong>t ne revi<strong>en</strong>n<strong>en</strong>t pas sur les algorithmes prés<strong>en</strong>tés dans l'énoncé. Ils inclu<strong>en</strong>t<br />

<strong>de</strong>ux <strong>programmes</strong>, le premier implém<strong>en</strong>te la recherche du plus court chemin selon l'algorithme <strong>de</strong>s cartes<br />

<strong>de</strong> Kohon<strong>en</strong>, le second implém<strong>en</strong>te les métho<strong>de</strong>s utilisés par l'algorithme 8.5.<br />

8.2.1 Carte <strong>de</strong> Kohon<strong>en</strong><br />

La gure 8.11 donne un exemple <strong>de</strong> résultat retourné par le programme qui suit et implém<strong>en</strong>tant les<br />

cartes <strong>de</strong> Kohon<strong>en</strong>. Ce programme inclut <strong>de</strong>s fonctions d'achage (display_ville, display_neurone,<br />

64


att<strong>en</strong>dre_clic), <strong>de</strong>s fonctions qui eectu<strong>en</strong>t <strong>de</strong>s opérations simples sur un T-uple représ<strong>en</strong>tant les<br />

<strong>de</strong>ux coordonnées d'un point ou d'un vecteur (distance_euclidi<strong>en</strong>ne_carree, ajoute_vecteur,<br />

soustrait_vecteur, multiplie_vecteur, poids_attirance, vecteur_norme).<br />

Les trois fonctions importantes sont tout d'abord la paire iteration et <strong>de</strong>place_neurone qui déplac<strong>en</strong>t<br />

les neurones vers les villes ; puis la fonction modifie_structure qui ajoute ou supprime <strong>de</strong>s neurones<br />

lorsque ceux-ci sont respectivem<strong>en</strong>t attirés par plusieurs villes à la fois ou attirés par aucune ville. Le<br />

programme principal suit l'instruction if __name__ == ”__main__” : qui permet d'utiliser ce programme<br />

<strong>en</strong> tant que module si nécessaire par la suite.<br />

Fig. 8.11 Résultat obt<strong>en</strong>u pour un <strong>en</strong>semble <strong>de</strong> 30 villes, l'algorithme <strong>de</strong> Kohon<strong>en</strong> a convergé vers une<br />

solution.<br />

1 # -*- coding: cp1252 -*-<br />

2 import random # pour tirer aléatoirem<strong>en</strong>t <strong>de</strong>s nombres<br />

3 import math # fonctions cos, sin<br />

4 import pygame # pour les affichages<br />

5<br />

6 <strong>de</strong>f construit_ville(n, x =1000, y = 700):<br />

7 """tire aléatoirem<strong>en</strong>t n villes dans un carrée x * y, on choisit<br />

8 ces villes <strong>de</strong> sort<strong>en</strong>t qu'elles ne soi<strong>en</strong>t pas trop proches"""<br />

9 # <strong>de</strong>ux villes ne pourront pas être plus proches que mind<br />

10 mind = math.sqrt (x*x+y*y) / (n * 0.75)<br />

11 # liste vi<strong>de</strong><br />

12 l = []<br />

13 while n > 0:<br />

14 # on tire aléatoirem<strong>en</strong>t les coordonnées d'une ville<br />

15 xx = x * random.random ()<br />

16 yy = y * random.random ()<br />

17 # on vérifie qu'elle n'est pas trop proche d'aucune autre ville<br />

18 ajout = True<br />

19 for t in l :<br />

20 d1 = t [0] - xx<br />

21 d2 = t [1] - yy<br />

22 d = math.sqrt (d1*d1+d2*d2)<br />

23 if d < mind :<br />

24 ajout = False # ville trop proche<br />

25 # si la ville n'est pas trop proche <strong>de</strong>s autres, on l'ajoute à la liste<br />

26 if ajout:<br />

27 l.app<strong>en</strong>d ((xx,yy))<br />

65


28 n -= 1 # une ville <strong>en</strong> moins à choisir<br />

29 return l<br />

30<br />

31 <strong>de</strong>f display_ville(villes,scre<strong>en</strong>,bv):<br />

32 """<strong>de</strong>ssine les villes à l'écran"""<br />

33 color = 255,0,0<br />

34 color2 = 0,255,0<br />

35 for v in villes:<br />

36 pygame.draw.circle (scre<strong>en</strong>, color, v, 10)<br />

37 pygame.draw.circle (scre<strong>en</strong>, color2, villes [bv], 10)<br />

38<br />

39 <strong>de</strong>f construit_liste_neurones (villes, nb = 0):<br />

40 """place les neurones sur l'écran,<br />

41 il y a autant <strong>de</strong> neurones que <strong>de</strong> villes,<br />

42 le paramètre villes est la liste <strong>de</strong>s villes"""<br />

43<br />

44 if nb == 0 : nb = l<strong>en</strong> (villes)<br />

45<br />

46 # coordonnées maximale<br />

47 maxx,maxy = 0,0<br />

48 for v in villes:<br />

49 if v [0] > maxx : maxx = v [0]<br />

50 if v [1] > maxy : maxy = v [1]<br />

51<br />

52 maxx /= 2<br />

53 maxy /= 2<br />

54<br />

55 if nb > 1 :<br />

56 # dispose les neurones <strong>en</strong> ellipse<br />

57 n = []<br />

58 for i in range (0, nb):<br />

59 x = maxx + maxx * math.cos (math.pi * 2 * float (i) / nb) / 4<br />

60 y = maxy + maxy * math.sin (math.pi * 2 * float (i) / nb) / 4<br />

61 n.app<strong>en</strong>d ((x,y))<br />

62 return n<br />

63 else :<br />

64 n = [ (maxx, maxy) ]<br />

65 return n<br />

66<br />

67 <strong>de</strong>f distance_euclidi<strong>en</strong>ne_carree (p1,p2):<br />

68 """calcule la distance euclidi<strong>en</strong>ne <strong>en</strong>tre <strong>de</strong>ux points"""<br />

69 x = p1[0] - p2[0]<br />

70 y = p1[1] - p2[1]<br />

71 return x*x+y*y<br />

72<br />

73 <strong>de</strong>f ajoute_vecteur (v,n):<br />

74 """ajoute <strong>de</strong>ux vecteurs <strong>en</strong>tre eux"""<br />

75 return ( v [0] + n [0], v [1] + n [1])<br />

76<br />

77 <strong>de</strong>f soustrait_vecteur (v,n):<br />

66


78 """soustrait <strong>de</strong>ux vecteurs"""<br />

79 return ( v [0] - n [0], v [1] - n [1])<br />

80<br />

81 <strong>de</strong>f multiplie_vecteur (v,f):<br />

82 """multiplie un vecteur par un scalaire"""<br />

83 return ( v [0] * f, v [1] * f)<br />

84<br />

85 <strong>de</strong>f poids_attirance(p,dist):<br />

86 """calcul le poids d'attraction d'une neurone vers une ville"""<br />

87 d = p [0] * p [0] + p [1] * p [1]<br />

88 d = math.sqrt (d)<br />

89 d = dist / (d + dist)<br />

90 return d<br />

91<br />

92 <strong>de</strong>f vecteur_norme(p):<br />

93 """calcul la norme d'un vecteur"""<br />

94 return math.sqrt (p [0] * p [0] + p [1] * p [1])<br />

95<br />

96 <strong>de</strong>f <strong>de</strong>place_neurone (n,villes,neurones, dist_w, forces, compte):<br />

97 """déplace le neurone <strong>de</strong> plus proche <strong>de</strong> la ville n, déplace ses voisins<br />

98 @param villes liste <strong>de</strong>s villes<br />

99 @param neurones liste <strong>de</strong>s neurones<br />

100 @param dist distance d'attirance<br />

101 @param forces force <strong>de</strong> déplacem<strong>en</strong>t <strong>de</strong>s voisins du neurones<br />

102 @param compte incrém<strong>en</strong>te compte [n] où n est l'indice du neurone choisi<br />

103 @return indice du neurone le plus proche"""<br />

104<br />

105 # recherche du neurone le plus proche<br />

106 v = villes [n]<br />

107 proche = 0<br />

108 dist = distance_euclidi<strong>en</strong>ne_carree(v, neurones [0])<br />

109 for i in xrange(1,l<strong>en</strong>(neurones)):<br />

110 d = distance_euclidi<strong>en</strong>ne_carree (v, neurones [i])<br />

111 if d < dist :<br />

112 dist = d<br />

113 proche = i<br />

114<br />

115 # vecteur <strong>de</strong> déplacem<strong>en</strong>t<br />

116 i = proche<br />

117 compte [i] += 1<br />

118 n = neurones [i]<br />

119 vec = soustrait_vecteur (v,n)<br />

120 poids = poids_attirance (vec, dist_w)<br />

121 vec = multiplie_vecteur (vec, poids)<br />

122 n = ajoute_vecteur (n, vec)<br />

123 neurones [i] = n<br />

124<br />

125 # déplacem<strong>en</strong>t <strong>de</strong>s voisins<br />

126 for k in xrange(0,l<strong>en</strong> (forces)):<br />

127 i1 = (i + k + 1) % l<strong>en</strong> (neurones)<br />

67


128 i2 = (i - k - 1 + l<strong>en</strong> (neurones)) % l<strong>en</strong> (neurones)<br />

129 n1 = neurones [i1]<br />

130 n2 = neurones [i2]<br />

131<br />

132 vec = soustrait_vecteur (n, n1)<br />

133 poids = poids_attirance (vec, dist_w)<br />

134 vec = multiplie_vecteur (vec, poids)<br />

135 vec = multiplie_vecteur (vec, forces [k])<br />

136 n1 = ajoute_vecteur (n1, vec)<br />

137<br />

138 vec = soustrait_vecteur (n, n2)<br />

139 poids = poids_attirance (vec, dist_w)<br />

140 vec = multiplie_vecteur (vec, poids)<br />

141 vec = multiplie_vecteur (vec, forces [k])<br />

142 n2 = ajoute_vecteur (n2, vec)<br />

143<br />

144 neurones [i1] = n1<br />

145 neurones [i2] = n2<br />

146<br />

147 return proche<br />

148<br />

149 <strong>de</strong>f iteration (villes,neurones, dist, forces, compte_v, compte_n):<br />

150 """choisit une ville aléatoirem<strong>en</strong>t et attire le neurones le plus proche,<br />

151 choisit cette ville parmi les villes les moins <strong>fr</strong>équemm<strong>en</strong>t choisies<br />

152 @param villes liste <strong>de</strong>s villes<br />

153 @param neurones liste <strong>de</strong>s neurones<br />

154 @param dist distance d'attirance<br />

155 @param forces force <strong>de</strong> déplacem<strong>en</strong>t <strong>de</strong>s voisins du neurones<br />

156 @param compte_v incrém<strong>en</strong>te compte_v [n] où n est l'indice <strong>de</strong> la ville choisie<br />

157 @param compte_n incrém<strong>en</strong>te compte_n [n] où n est l'indice du neurone choisi<br />

158 @return indices <strong>de</strong> la ville et du neurone le plus proche"""<br />

159<br />

160 m = min (compte_v)<br />

161 ind = [i for i in xrange (0,l<strong>en</strong> (villes)) if compte_v [i] == m]<br />

162 n = random.randint (0,l<strong>en</strong>(ind)-1)<br />

163 n = ind [n]<br />

164 compte_v [n] += 1<br />

165 return n, <strong>de</strong>place_neurone (n,villes,neurones,dist, forces, compte_n)<br />

166<br />

167 <strong>de</strong>f display_neurone(neurones,scre<strong>en</strong>,bn):<br />

168 """<strong>de</strong>ssine les neurones à l'écran"""<br />

169 color = 0,0,255<br />

170 color2 = 0,255,0<br />

171 for n in neurones :<br />

172 pygame.draw.circle (scre<strong>en</strong>, color, n, 5)<br />

173 pygame.draw.circle (scre<strong>en</strong>, color2, neurones [bn], 5)<br />

174 if l<strong>en</strong> (neurones) > 1:<br />

175 pygame.draw.lines (scre<strong>en</strong>, color, True, neurones, 2)<br />

176<br />

177 <strong>de</strong>f modifie_structure(neurones,compte, nb_sel):<br />

68


178 """modifie la structure <strong>de</strong>s neurones, supprime les neurones jamais<br />

179 déplacés, et ajoute <strong>de</strong>s neurones lorsque certains sont trop sollicités"""<br />

180<br />

181 <strong>de</strong>f cmp_add (i,j):<br />

182 return cmp (i [0], j [0])<br />

183<br />

184 if nb_sel > 0 :<br />

185 # supprime les neurones les moins sollicités<br />

186 sup = [i for i in xrange (0, l<strong>en</strong>(neurones)) if compte [i] == 0 ]<br />

187 if l<strong>en</strong>(sup) > 0:<br />

188 sup.sort ()<br />

189 sup.reverse ()<br />

190 for i in sup:<br />

191 <strong>de</strong>l compte [i]<br />

192 <strong>de</strong>l neurones [i]<br />

193<br />

194 # on ajoute un neurone lorsque max (compte) >= 2 * min (compte)<br />

195 add = []<br />

196 for i in xrange (0,l<strong>en</strong>(compte)):<br />

197 if compte [i] > nb_sel:<br />

198 d1 = math.sqrt ( distance_euclidi<strong>en</strong>ne_carree (neurones [i], \<br />

199 neurones [(i+1) % l<strong>en</strong> (neurones)] ))<br />

200 d2 = math.sqrt ( distance_euclidi<strong>en</strong>ne_carree (neurones [i], \<br />

201 neurones [(i-1 + l<strong>en</strong> (neurones)) % l<strong>en</strong> (neurones)] ))<br />

202 if d1 > d2 : d1 = d2<br />

203 p = neurones [i]<br />

204 p = (p[0] + random.randint (0, int (d1 / 2)) , \<br />

205 p [1] + random.randint (0, int (d1 / 2)) )<br />

206 add.app<strong>en</strong>d ((i,p,0))<br />

207<br />

208 add.sort (cmp_add)<br />

209 add.reverse ()<br />

210 for a in add:<br />

211 neurones.insert (a [0], a [1])<br />

212 compte.insert (a [0], a [2])<br />

213<br />

214 # on remet les compteurs à zéros<br />

215 for i in xrange (0, l<strong>en</strong> (compte)) : compte [i] = 0<br />

216<br />

217 <strong>de</strong>f moy<strong>en</strong>ne_proximite (villes):<br />

218 """retourne la distance moy<strong>en</strong>ne <strong>en</strong>tre <strong>de</strong>ux villes les plus proches"""<br />

219 c = 0<br />

220 m = 0<br />

221 for v in villes:<br />

222 mn = 100000000<br />

223 for vv in villes:<br />

224 if v == vv : continue<br />

225 d = distance_euclidi<strong>en</strong>ne_carree (v,vv)<br />

226 if d < mn : mn = d<br />

227 c += 1<br />

69


228 m += math.sqrt (mn)<br />

229 m /= float (c)<br />

230 return m<br />

231<br />

232 <strong>de</strong>f att<strong>en</strong>dre_clic (scre<strong>en</strong>,x,y):<br />

233 """<strong>de</strong>ssine une croix sur l'écran et att<strong>en</strong>d la pression d'un clic <strong>de</strong> souris"""<br />

234 color = 0,0,0<br />

235 pygame.draw.line (scre<strong>en</strong>, color, (0,0), (x-1,y-1))<br />

236 pygame.draw.line (scre<strong>en</strong>, color, (x-1,0), (0,y-1))<br />

237 pygame.display.flip ()<br />

238 reste = True<br />

239 while reste:<br />

240 for ev<strong>en</strong>t in pygame.ev<strong>en</strong>t.get():<br />

241 if ev<strong>en</strong>t.type == pygame.MOUSEBUTTONUP :<br />

242 reste = False<br />

243 break<br />

244<br />

245 ################################################################################<br />

246<br />

247 if __name__ == "__main__" :<br />

248 pygame.init ()<br />

249 size = width, height = x,y = 800, 500<br />

250 black = 0, 0, 0<br />

251 white = 255,255,255<br />

252 scre<strong>en</strong> = pygame.display.set_mo<strong>de</strong>(size)<br />

253 villes = construit_ville (30, x,y)<br />

254 neurones = construit_liste_neurones (villes, 3)<br />

255 compte_n = [0 for i in neurones]<br />

256 compte_v = [0 for i in villes]<br />

257 tour = 2<br />

258 maj = tour * l<strong>en</strong> (villes)<br />

259<br />

260 dist = moy<strong>en</strong>ne_proximite (villes) * 4<br />

261 fs = [ 1.5, 1, 0.75, 0.5, 0.25 ]<br />

262<br />

263 iter = 0<br />

264 while True:<br />

265 for ev<strong>en</strong>t in pygame.ev<strong>en</strong>t.get():<br />

266 if ev<strong>en</strong>t.type == pygame.QUIT: sys.exit()<br />

267 if ev<strong>en</strong>t.type == pygame.MOUSEBUTTONUP:<br />

268 att<strong>en</strong>dre_clic (scre<strong>en</strong>,x,y)<br />

269<br />

270 iter += 1<br />

271<br />

272 if iter % maj == 0 :<br />

273 modifie_structure (neurones,compte_n, tour)<br />

274 dist *= 0.99<br />

275 f2 = [ w * 0.90 for w in fs]<br />

276 fs = f2<br />

277<br />

70


278 bv, bn = iteration (villes,neurones, dist, fs, compte_v, compte_n)<br />

279<br />

280 scre<strong>en</strong>.fill (white)<br />

281 display_ville (villes, scre<strong>en</strong>, bv)<br />

282 display_neurone (neurones, scre<strong>en</strong>, bn)<br />

283<br />

284 if iter == 1: att<strong>en</strong>dre_clic (scre<strong>en</strong>,x,y)<br />

285<br />

286 pygame.display.flip ()<br />

287<br />

8.2.2 Construction à partir d'un arbre <strong>de</strong> poids minimum<br />

Le programme suivant repr<strong>en</strong>d les mêmes fonctionnalités que celles du programme précé<strong>de</strong>nt liées à l'af-<br />

chage (display_ville, display_neurone, att<strong>en</strong>dre_clic). Il y a toujours quelques manipulations<br />

<strong>de</strong> vecteurs (vecteur_points, vecteur_norme, vecteur_cosinus, vecteur_sinus, oppose_vecteur,<br />

equation_droite, intersection_segm<strong>en</strong>t).<br />

Le programme utilise le module bres<strong>en</strong>ham_ligne (voir paragraphe 4.2, page 23). Ce module permet<br />

<strong>de</strong> transcrire une droite reliant <strong>de</strong>ux points par un <strong>en</strong>semble <strong>de</strong> pixels formant une droite. Il est utilisé<br />

pour savoir quelles zones traverse un segm<strong>en</strong>t <strong>en</strong>tre <strong>de</strong>ux villes (voir gure 8.9). Les autres fonctions<br />

implém<strong>en</strong>t<strong>en</strong>t l'algorithme 8.5.<br />

Les fonctions repartition_zone, voisinage_zone, <strong>de</strong>ssin_arete_zone, voisinage_zone_xy manipul<strong>en</strong>t<br />

<strong>de</strong>s zones. An d'accélérer les algorithmes, l'image est quadrillée (voir gure 8.9). A chaque case<br />

du quadrillage correspond une zone qui rec<strong>en</strong>se toutes les villes incluses dans cette partie <strong>de</strong> l'image. On<br />

considère que <strong>de</strong>ux villes sont voisines si elles apparti<strong>en</strong>n<strong>en</strong>t à la même zone et <strong>de</strong>s zones voisines. Cet<br />

intermè<strong>de</strong> permet ainsi <strong>de</strong> déterminer rapi<strong>de</strong>m<strong>en</strong>t quels sont les voisins d'une ville <strong>en</strong> explorant seulem<strong>en</strong>t<br />

un sous-<strong>en</strong>semble <strong>de</strong>s villes. Implém<strong>en</strong>té à l'ai<strong>de</strong> <strong>de</strong> zones, l'algorithme <strong>de</strong> recherche <strong>de</strong> l'arbre <strong>de</strong> poids<br />

minimum a un coût <strong>en</strong> O(n) au lieu <strong>de</strong> O(n 2 ) avec n égal au nombre <strong>de</strong> villes.<br />

La fonction arbre_poids_minimal détermine l'arbre <strong>de</strong> poids minimum (algorithme 8.8). La fonction<br />

circuit_euleri<strong>en</strong> implém<strong>en</strong>te l'algorithme 8.9. La fonction circuit_hamiltoni<strong>en</strong> implém<strong>en</strong>te l'algorithme<br />

8.10.<br />

Les autres fonctions optimis<strong>en</strong>t la solution obt<strong>en</strong>ue après la construction du circuit hamiltoni<strong>en</strong>. Les<br />

<strong>de</strong>ux fonctions retournem<strong>en</strong>t_essai et echange_position_essai implém<strong>en</strong>t<strong>en</strong>t un retournem<strong>en</strong>t<br />

et un déplacem<strong>en</strong>t, retourné ou non. Les trois fonctions retournem<strong>en</strong>t_essai, echange_position,<br />

supprime_croisem<strong>en</strong>t, test<strong>en</strong>t plusieurs hypothèses <strong>de</strong> retournem<strong>en</strong>ts ou déplacem<strong>en</strong>ts. Enn, la fonction<br />

amelioration_chemin utilise les trois précé<strong>de</strong>ntes pour implém<strong>en</strong>ter l'algorithme 8.11.<br />

Il est possible d'utiliser le module psyco pour aller plus vite. Les <strong>de</strong>ux instructions import psyco et<br />

pysco.full() sus<strong>en</strong>t à multiplier la vitesse du programme par trois ou quatre.<br />

1 # -*- coding: cp1252 -*-<br />

2 import random # pour tirer aléatoirem<strong>en</strong>t <strong>de</strong>s nombres<br />

3 import math # fonctions cos, sin<br />

4 import pygame # pour les affichages<br />

5 import copy # pour copier <strong>de</strong>s résultats intermédiaires<br />

6 import bres<strong>en</strong>ham_ligne as brl # pour <strong>de</strong>ssiner les arêtes dans l'écran <strong>de</strong>s zones<br />

7 #import psyco # pour accélérer le programme<br />

8<br />

9 <strong>de</strong>f att<strong>en</strong>dre_clic (scre<strong>en</strong>,x,y):<br />

71


10 """<strong>de</strong>ssine une croix sur l'écran et att<strong>en</strong>d la pression d'un clic <strong>de</strong> souris"""<br />

11 color = 0,0,0<br />

12 pygame.draw.line (scre<strong>en</strong>, color, (0,0), (x-1,y-1))<br />

13 pygame.draw.line (scre<strong>en</strong>, color, (x-1,0), (0,y-1))<br />

14 pygame.display.flip ()<br />

15 reste = True<br />

16 while reste:<br />

17 for ev<strong>en</strong>t in pygame.ev<strong>en</strong>t.get():<br />

18 if ev<strong>en</strong>t.type == pygame.MOUSEBUTTONUP :<br />

19 reste = False<br />

20 break<br />

21<br />

22<br />

23 <strong>de</strong>f construit_ville(n, x =1000, y = 700):<br />

24 """tire aléatoirem<strong>en</strong>t n villes dans un carrée x * y, on choisit<br />

25 ces villes <strong>de</strong> sort<strong>en</strong>t qu'elles ne soi<strong>en</strong>t pas trop proches"""<br />

26 # <strong>de</strong>ux villes ne pourront pas être plus proches que mind<br />

27 mind = math.sqrt (x*x+y*y) / (n * 0.75)<br />

28 # liste vi<strong>de</strong><br />

29 l = []<br />

30 while n > 0:<br />

31 # on tire aléatoirem<strong>en</strong>t les coordonnées d'une ville<br />

32 xx = x * random.random ()<br />

33 yy = y * random.random ()<br />

34 # on vérifie qu'elle n'est pas trop proche d'aucune autre ville<br />

35 ajout = True<br />

36 for t in l :<br />

37 d1 = t [0] - xx<br />

38 d2 = t [1] - yy<br />

39 d = math.sqrt (d1*d1+d2*d2)<br />

40 if d < mind :<br />

41 ajout = False # ville trop proche<br />

42 # si la ville n'est pas trop proche <strong>de</strong>s autres, on l'ajoute à la liste<br />

43 if ajout:<br />

44 l.app<strong>en</strong>d ((xx,yy))<br />

45 n -= 1 # une ville <strong>en</strong> moins à choisir<br />

46 return l<br />

47<br />

48 <strong>de</strong>f display_ville(villes,scre<strong>en</strong>,bv):<br />

49 """<strong>de</strong>ssine les villes à l'écran"""<br />

50 color = 255,0,0<br />

51 color2 = 0,255,0<br />

52 for v in villes:<br />

53 pygame.draw.circle (scre<strong>en</strong>, color, v, 3)<br />

54 pygame.draw.circle (scre<strong>en</strong>, color2, villes [bv], 3)<br />

55<br />

56 <strong>de</strong>f display_neurone(neurones,scre<strong>en</strong>,bn):<br />

57 """<strong>de</strong>ssine les neurones à l'écran"""<br />

58 color = 0,0,255<br />

59 color2 = 0,255,0<br />

72


60 for n in neurones :<br />

61 pygame.draw.circle (scre<strong>en</strong>, color, n, 3)<br />

62 pygame.draw.circle (scre<strong>en</strong>, color2, neurones [bn], 3)<br />

63 if l<strong>en</strong> (neurones) > 1:<br />

64 pygame.draw.lines (scre<strong>en</strong>, color, True, neurones, 2)<br />

65<br />

66 <strong>de</strong>f distance (p1,p2):<br />

67 """calcule la distance <strong>en</strong>tre <strong>de</strong>ux villes"""<br />

68 x = p1 [0] - p2[0]<br />

69 y = p1 [1] - p2[1]<br />

70 return math.sqrt (x*x + y*y)<br />

71<br />

72 <strong>de</strong>f repartition_zone (villes, zone_taille, ask_zone = False) :<br />

73 """répartit les villes <strong>en</strong> zones, retourne les villes rangées par zones,<br />

74 chaque élém<strong>en</strong>ts zones [z][k] conti<strong>en</strong>t :<br />

75 - les coordonnées <strong>de</strong> la ville<br />

76 - ses coordonnées <strong>en</strong> zone, (zx, zy)<br />

77 - son indice dans la liste villes<br />

78 la fonction retourne égalem<strong>en</strong>t le nombre <strong>de</strong> zones<br />

79 selon l'axe <strong>de</strong>s abscisses et l'axe <strong>de</strong>s ordonnées,<br />

80 retourne aussi le nombre <strong>de</strong> zones, si ask_zone est True,<br />

81 retourne un paramètre supplém<strong>en</strong>taire : zone"""<br />

82 print "gestion <strong>de</strong>s zones"<br />

83 X,Y = 0,0<br />

84 for v in villes:<br />

85 X = max (v [0] // zone_taille, X)<br />

86 Y = max (v [1] // zone_taille, Y)<br />

87 X += 1<br />

88 Y += 1<br />

89<br />

90 # attribution <strong>de</strong>s zones<br />

91 zone = []<br />

92 nb = l<strong>en</strong> (villes)<br />

93 Zmax = 0<br />

94 for i in xrange (0,l<strong>en</strong> (villes)):<br />

95 v = villes [i]<br />

96 x = int (v [0] // zone_taille)<br />

97 y = int (v [1] // zone_taille)<br />

98 z = int (y * X + x)<br />

99 Zmax = max (z,Zmax)<br />

100 zone.app<strong>en</strong>d ((z, v, (x,y), i))<br />

101<br />

102 # rangem<strong>en</strong>t par zone<br />

103 Zmax += 1<br />

104 zones = [ [] for i in xrange (0,Zmax) ]<br />

105 for z in zone:<br />

106 zones [ z [0] ].app<strong>en</strong>d ((z [1], z [2], z [3]))<br />

107<br />

108 if ask_zone :<br />

109 return zones,X,Y,Zmax,zone<br />

73


110 else :<br />

111 return zones,X,Y,Zmax<br />

112<br />

113 <strong>de</strong>f voisinage_zone (z,Zmax,X,Y):<br />

114 """retourne la liste <strong>de</strong>s voisins d'une zone z<br />

115 sachant qu'il y a X zones sur l'axe <strong>de</strong>s abscisses et Y zones sur l'axe <strong>de</strong>s ordonnées,<br />

116 Zmax est le nombre <strong>de</strong> zones,<br />

117 inclus z dans cette liste"""<br />

118 x = z % X<br />

119 y = z // X<br />

120 voisin_ = [z]<br />

121 if x > 0: voisin_.app<strong>en</strong>d (z-1)<br />

122 if x < X: voisin_.app<strong>en</strong>d (z+1)<br />

123 if y > 0: voisin_.app<strong>en</strong>d (z-X)<br />

124 if y < Y: voisin_.app<strong>en</strong>d (z+X)<br />

125 if x > 0 and y > 0 : voisin_.app<strong>en</strong>d (z-1-X)<br />

126 if x > 0 and y < Y : voisin_.app<strong>en</strong>d (z-1+X)<br />

127 if x < X and y > 0 : voisin_.app<strong>en</strong>d (z+1-X)<br />

128 if x < X and y < Y : voisin_.app<strong>en</strong>d (z+1+X)<br />

129 voisin = [ int (i) for i in voisin_ if i >= 0 and i < Zmax ]<br />

130 return voisin<br />

131<br />

132 <strong>de</strong>f arbre_poids_minimal(villes, zone_taille):<br />

133 """construit l'arbre <strong>de</strong> poids minimal, retourne une liste <strong>de</strong><br />

134 listes, chaque sous-liste associée à une ville conti<strong>en</strong>t la liste <strong>de</strong>s ses voisins,<br />

135 zone_taille permet <strong>de</strong> découper l'image <strong>en</strong> zones,<br />

136 les distances ne seront calculées que si<br />

137 <strong>de</strong>ux élém<strong>en</strong>ts sont dans la même zone ou dans une zone voisine"""<br />

138<br />

139 <strong>de</strong>f tri_distance (u,v):<br />

140 if u [2] < v [2] : return -1<br />

141 elif u [2] > v [2] : return 1<br />

142 else : return 0<br />

143<br />

144 zones,X,Y,Zmax = repartition_zone (villes, zone_taille)<br />

145<br />

146 # calcul <strong>de</strong>s distances<br />

147 print "calcul <strong>de</strong>s distances"<br />

148 li = []<br />

149<br />

150 for z in xrange (0,l<strong>en</strong> (zones)):<br />

151 if z % 1000 == 0 : print "zone ", z, l<strong>en</strong>(zones)<br />

152 voisin = voisinage_zone (z,Zmax,X,Y)<br />

153 for v in zones [z]:<br />

154 for zz in voisin:<br />

155 for u in zones [zz]:<br />

156 d = distance (v [0], u [0])<br />

157 li.app<strong>en</strong>d ((v [2], u [2], d))<br />

158<br />

159 print "tri ", l<strong>en</strong>(li)<br />

74


160 li.sort (tri_distance)<br />

161<br />

162 # composantes connexes<br />

163 print "construction <strong>de</strong> l'arbre"<br />

164<br />

165 # nombre <strong>de</strong> composantes connexes<br />

166 nb_comp = l<strong>en</strong> (villes)<br />

167<br />

168 # indice <strong>de</strong> la composante d'une ville<br />

169 num_comp = [ i for i in xrange(0,l<strong>en</strong>(villes)) ]<br />

170<br />

171 # liste <strong>de</strong>s voisins pour chaque ville<br />

172 arbre = [ [] for i in xrange(0,l<strong>en</strong>(villes)) ]<br />

173<br />

174 # liste <strong>de</strong>s villes par composante connexe<br />

175 list_comp = [ [i] for i in xrange(0,l<strong>en</strong>(villes)) ]<br />

176<br />

177 iii = 0<br />

178 for c in li:<br />

179 if iii % 10000 == 0 : print "arête ", iii, l<strong>en</strong>(li)<br />

180 iii += 1<br />

181 i,j = c [0], c [1]<br />

182 if num_comp [i] != num_comp [j]:<br />

183 # on relie les villes i et j car elles apparti<strong>en</strong>n<strong>en</strong>t<br />

184 # à <strong>de</strong>s composantes connexes différ<strong>en</strong>tes<br />

185 arbre [i].app<strong>en</strong>d (j) # i est voisine <strong>de</strong> j<br />

186 arbre [j].app<strong>en</strong>d (i) # j est voisine <strong>de</strong> i<br />

187 cl = num_comp [i] # composante connexe restante<br />

188 ki = num_comp [j] # composante connexe à aggréger à la précé<strong>de</strong>nte<br />

189 for k in list_comp [ki]:<br />

190 num_comp [k] = cl<br />

191 list_comp [cl].app<strong>en</strong>d (k)<br />

192 list_comp [ki] = []<br />

193 nb_comp -= 1 # une composante connexe <strong>en</strong> moins<br />

194<br />

195 if nb_comp == 0:<br />

196 break # il n'y a plus qu'une seule composante connexe, inutile <strong>de</strong> continuer<br />

197<br />

198 return arbre<br />

199<br />

200 <strong>de</strong>f display_arbre (villes, arbre, mult = 1):<br />

201 """<strong>de</strong>ssine le graphe <strong>de</strong> poids minimal défini par arbre"""<br />

202 if mult == 2 :<br />

203 color = 0,255,0<br />

204 l = 4<br />

205 else :<br />

206 l = 1<br />

207 color = 0,0,255<br />

208<br />

209 for i in xrange (0, l<strong>en</strong> (villes)):<br />

75


210 for j in arbre [i]:<br />

211 v = (villes [i][0] * mult, villes [i][1] * mult)<br />

212 vv = (villes [j][0] * mult, villes [j][1] * mult)<br />

213 pygame.draw.line (scre<strong>en</strong>, color, v, vv, l)<br />

214<br />

215 <strong>de</strong>f vecteur_points (p1,p2):<br />

216 """retourne le vecteur <strong>en</strong>tre les points p1 et p2"""<br />

217 return ( p2 [0] - p1 [0], p2 [1] - p1 [1])<br />

218<br />

219 <strong>de</strong>f vecteur_norme (vec):<br />

220 """retourne la norme d'un vecteur"""<br />

221 return math.sqrt (vec [0] * vec [0] + vec [1] * vec [1])<br />

222<br />

223 <strong>de</strong>f vecteur_cosinus (vec1, vec2):<br />

224 """retourne le cosinus <strong>en</strong>tre <strong>de</strong>ux vecteurs, utilise le produit scalaire"""<br />

225 norm1 = vecteur_norme (vec1)<br />

226 norm2 = vecteur_norme (vec2)<br />

227 if norm1 == 0: return 1<br />

228 if norm2 == 0: return 1<br />

229 scal = vec1 [0] * vec2 [0] + vec1 [1] * vec2 [1]<br />

230 return scal / (norm1 * norm2)<br />

231<br />

232 <strong>de</strong>f vecteur_sinus (vec1, vec2):<br />

233 """retourne le sinus <strong>en</strong>tre <strong>de</strong>ux vecteurs, utilise le produit vectoriel"""<br />

234 norm1 = vecteur_norme (vec1)<br />

235 norm2 = vecteur_norme (vec2)<br />

236 if norm1 == 0: return 0<br />

237 if norm2 == 0: return 0<br />

238 scal = vec1 [0] * vec2 [1] - vec1 [1] * vec2 [0]<br />

239 return scal / (norm1 * norm2)<br />

240<br />

241 <strong>de</strong>f oppose_vecteur (vec):<br />

242 """retourne le vecteur opposé"""<br />

243 return (- vec [0], - vec [1])<br />

244<br />

245 <strong>de</strong>f circuit_euleri<strong>en</strong> (villes, arbre):<br />

246 """définit un circuit euléri<strong>en</strong>, villes conti<strong>en</strong>t la liste <strong>de</strong>s villes,<br />

247 tandis que arbre est une liste <strong>de</strong> listes, arbre [i] est la liste <strong>de</strong>s villes<br />

248 connectées à la ville i,<br />

249 on suppose que arbre est un graphe <strong>de</strong> poids minimal non ori<strong>en</strong>té,<br />

250 l'algorithme ne marche pas s'il existe <strong>de</strong>s villes confondues"""<br />

251<br />

252 # on choisit une ville qui est une extrémité et parmi celle-là on la choisit au hasard<br />

253 has = []<br />

254 for i in xrange (0,l<strong>en</strong> (villes)):<br />

255 n = l<strong>en</strong> (arbre [i])<br />

256 if n == 1: has.app<strong>en</strong>d (i)<br />

257<br />

258 bm = random.randint (0, l<strong>en</strong> (has) -1)<br />

259 bm = has [bm]<br />

76


260<br />

261 # vecteur, le circuit euléri<strong>en</strong> conti<strong>en</strong>t<br />

262 # nécessairem<strong>en</strong>t 2 * l<strong>en</strong> (villes) noeuds puisque c'est<br />

263 # le graphe euléri<strong>en</strong> d'un arbre <strong>de</strong> poids minimal non ori<strong>en</strong>té<br />

264 vec = (1,1)<br />

265 chemin = [bm]<br />

266 while l<strong>en</strong> (chemin) < 2 * l<strong>en</strong> (villes)-1 :<br />

267<br />

268 v = villes [bm]<br />

269<br />

270 ma = - math.pi - 1<br />

271 bvec = vec<br />

272 opvec = oppose_vecteur (vec)<br />

273 for k in xrange (0, l<strong>en</strong> (arbre [bm])) :<br />

274 l = arbre [bm][k]<br />

275 vec2 = vecteur_points (v, villes [l])<br />

276 if opvec == vec2 :<br />

277 angle = -math.pi<br />

278 else :<br />

279 cos = vecteur_cosinus (vec, vec2)<br />

280 sin = vecteur_sinus (vec, vec2)<br />

281 angle = math.atan2 (sin, cos)<br />

282 if angle > ma :<br />

283 ma = angle<br />

284 bl = k<br />

285 bvec = vec2<br />

286<br />

287 b = arbre [bm][bl]<br />

288 chemin.app<strong>en</strong>d (b)<br />

289 <strong>de</strong>l arbre [bm][bl] # on supprime l'arc pour ne plus l'utiliser<br />

290 bm = b<br />

291 vec = bvec<br />

292<br />

293 return chemin<br />

294<br />

295 <strong>de</strong>f circuit_hamiltoni<strong>en</strong> (chemin):<br />

296 """extrait un circuit hamiltoni<strong>en</strong> <strong>de</strong>puis un circuit eurléri<strong>en</strong>"""<br />

297 nb = max (chemin) + 1<br />

298 res = []<br />

299 coche = [ False for i in xrange (0, nb) ]<br />

300 for c in chemin :<br />

301 if coche [c] : continue<br />

302 res.app<strong>en</strong>d (c)<br />

303 coche [c] = True<br />

304<br />

305 return res<br />

306<br />

307 <strong>de</strong>f equation_droite (p1,p2) :<br />

308 """retourne l'équation d'une droite passant par p1 et p2,<br />

309 ax + by + c = 0, retourne les coeffici<strong>en</strong>ts a,b,c"""<br />

77


310 vec = vecteur_points (p1,p2)<br />

311 a = vec [1]<br />

312 b = - vec [0]<br />

313 c = - a * p1 [0] - b * p1 [1]<br />

314 return a,b,c<br />

315<br />

316 <strong>de</strong>f evaluation_droite (a,b,c,p):<br />

317 """l'équation d'une droite est : ax + by + c, retourne la valeur<br />

318 <strong>de</strong> cette expression au point p"""<br />

319 return a * p [0] + b * p [1] + c<br />

320<br />

321 <strong>de</strong>f intersection_segm<strong>en</strong>t (p1,p2,p3,p4):<br />

322 """dit si les segm<strong>en</strong>ts [p1 p2] et [p3 p4] ont une intersection,<br />

323 ne retourne pas l'intersection"""<br />

324 # équation <strong>de</strong> la droite (p1 p2)<br />

325 a1,b1,c1 = equation_droite (p1,p2)<br />

326 a2,b2,c2 = equation_droite (p3,p4)<br />

327 s1 = evaluation_droite (a2,b2,c2,p1)<br />

328 s2 = evaluation_droite (a2,b2,c2,p2)<br />

329 s3 = evaluation_droite (a1,b1,c1,p3)<br />

330 s4 = evaluation_droite (a1,b1,c1,p4)<br />

331 if s1 * s2


360 d_ia_j = distance (chemin [ia], chemin [j])<br />

361 d_i_ja = distance (chemin [i], chemin [ja])<br />

362 # amélioration ?<br />

363 d = d_ia_j + d_i_ja - d_j_ja - d_ia_i<br />

364 if d >= - negligeable : return False<br />

365<br />

366 # si amélioration, il faut retourner le chemin <strong>en</strong>tre les indices i et j<br />

367 jp = j<br />

368 if jp < i : jp = j + nb<br />

369 ip = i<br />

370<br />

371 while ip < jp :<br />

372 i = ip % nb<br />

373 j = jp % nb<br />

374 ech = chemin [i]<br />

375 chemin [i] = chemin [j]<br />

376 chemin [j] = ech<br />

377 ip = ip+1<br />

378 jp = jp-1<br />

379<br />

380 return True<br />

381<br />

382 <strong>de</strong>f retournem<strong>en</strong>t (chemin, taille) :<br />

383 """amélioration du chemin par un algorithme simple,<br />

384 utilise <strong>de</strong>s retournem<strong>en</strong>ts <strong>de</strong> taille au plus ,<br />

385 retourne le nombre <strong>de</strong> modifications"""<br />

386<br />

387 # traitem<strong>en</strong>t <strong>de</strong>s petits retournem<strong>en</strong>ts<br />

388 nb = l<strong>en</strong> (chemin)<br />

389 nb_change = 1<br />

390 nbtout = 0<br />

391 retour = { }<br />

392 while nb_change > 0 :<br />

393 nb_change = 0<br />

394 for t in xrange (1,taille+1):<br />

395 retour [t] = 0<br />

396 for i in xrange (0, nb) :<br />

397 j = (i + t) % nb<br />

398 b = retournem<strong>en</strong>t_essai (chemin, i, j)<br />

399 if b :<br />

400 retour [t] += 1<br />

401 nb_change += 1<br />

402 nbtout += nb_change<br />

403 print "nombre <strong>de</strong> retournem<strong>en</strong>ts %d longueur : \t %10.0f détail \t" \<br />

404 % (nbtout, longueur_chemin (chemin)), " détail : ", retour<br />

405 return nbtout<br />

406<br />

407 <strong>de</strong>f echange_position_essai (chemin, a,b, x, inversion, negligeable = 1e-5):<br />

408 """échange la place <strong>de</strong>s villes ka à kb pour les placer <strong>en</strong>tre les villes i et i+1,<br />

409 si inversion est True, on inverse égalem<strong>en</strong>t le chemin inséré, si inversion est False,<br />

79


410 on ne l'inverse pas,<br />

411 si cela améliore, déplace les villes et retourne True, sinon, retourne False"""<br />

412<br />

413 nb = l<strong>en</strong>(chemin)<br />

414 xa = (x + 1) % nb<br />

415 ka = (a - 1 + nb) % nb<br />

416 kb = (b + 1 ) % nb<br />

417<br />

418 if not inversion :<br />

419<br />

420 if x == ka : return False<br />

421 if x == kb : return False<br />

422 if xa == ka : return False<br />

423 if b < a :<br />

424 if a


460<br />

461 return True<br />

462<br />

463 else :<br />

464<br />

465 if x == ka : return False<br />

466 if x == kb : return False<br />

467 if xa == ka : return False<br />

468 if b < a :<br />

469 if a


510 return True<br />

511<br />

512 <strong>de</strong>f <strong>de</strong>ssin_arete_zone (chemin, taille_zone, X,Y):<br />

513 """retourne une liste <strong>de</strong> listes <strong>de</strong> listes,<br />

514 res [i][j] est une liste <strong>de</strong>s arêtes passant près <strong>de</strong> la zone (x,y) = [i][j],<br />

515 si k in res [i][j], alors l'arête k,k+1 est dans la zone (i,j),<br />

516 X est le nombre <strong>de</strong> zones horizontalem<strong>en</strong>t, Y est le nombre <strong>de</strong> zones verticalem<strong>en</strong>t,<br />

517 taille_zone est la longueur du côté du carré d'une zone"""<br />

518 res = [ [ [] for j in xrange (0,Y+1) ] for i in xrange (0,X+1) ]<br />

519 nb = l<strong>en</strong> (chemin)<br />

520 for i in xrange (0,nb):<br />

521 a = chemin [i]<br />

522 b = chemin [(i+1) % nb]<br />

523 x1,x2 = int (a [0] // taille_zone), int (b [0] // taille_zone)<br />

524 y1,y2 = int (a [1] // taille_zone), int (b [1] // taille_zone)<br />

525 line = brl.trace_ligne (x1,y1,x2,y2)<br />

526 for x,y in line:<br />

527 res [x][y].app<strong>en</strong>d (i)<br />

528 return res<br />

529<br />

530 <strong>de</strong>f voisinage_zone_xy (x,y,X,Y):<br />

531 """retourne la liste <strong>de</strong>s voisins d'une zone (x,y)<br />

532 sachant qu'il y a X zones sur l'axe <strong>de</strong>s abscisses et Y zones sur l'axe <strong>de</strong>s ordonnées,<br />

533 inclus z dans cette liste"""<br />

534 voisin = [(x,y)]<br />

535 if x > 0 : voisin.app<strong>en</strong>d ((x-1,y))<br />

536 if x < X-1 : voisin.app<strong>en</strong>d ((x+1,y))<br />

537 if y > 0 : voisin.app<strong>en</strong>d ((x,y-1))<br />

538 if y < Y-1 : voisin.app<strong>en</strong>d ((x,y+1))<br />

539 if x > 0 and y > 0 : voisin.app<strong>en</strong>d ((x-1,y-1))<br />

540 if x > 0 and y < Y-1 : voisin.app<strong>en</strong>d ((x-1,y+1))<br />

541 if x < X-1 and y > 0 : voisin.app<strong>en</strong>d ((x+1,y-1))<br />

542 if x < X-1 and y < Y-1 : voisin.app<strong>en</strong>d ((x+1,y+1))<br />

543 return voisin<br />

544<br />

545 <strong>de</strong>f echange_position (chemin, taille, taille_zone, X,Y, gran<strong>de</strong> = 0.5) :<br />

546 """regar<strong>de</strong> si on ne peut pas déplacer un segm<strong>en</strong>t <strong>de</strong> longueur taille<br />

547 pour supprimer les arêtes les plus longues,<br />

548 au maximum longues arêtes,<br />

549 retourne le nombre <strong>de</strong> changem<strong>en</strong>t effectués,<br />

550 X est le nombre <strong>de</strong> zones horizontalem<strong>en</strong>t,<br />

551 Y est le nombre <strong>de</strong> zones verticalem<strong>en</strong>t,<br />

552 taille_zone est la longueur d'un côté du carré d'une zone"""<br />

553<br />

554 nb = l<strong>en</strong>(chemin)<br />

555<br />

556 <strong>de</strong>f tri_arete (x,y):<br />

557 """pour trier la liste l par ordre décroissant"""<br />

558 if x [2] < y [2] : return 1<br />

559 elif x [2] > y [2] : return -1<br />

82


560 else : return 0<br />

561<br />

562 # list <strong>de</strong>s arêtes triés par ordre décroissant<br />

563 la = []<br />

564 for i in xrange (0,nb) :<br />

565 im = (i+1) % nb<br />

566 la.app<strong>en</strong>d ( (i, im, distance (chemin [i], chemin [im]) ) )<br />

567 la.sort (tri_arete)<br />

568<br />

569 # zone associé à chaque arête<br />

570 zone = <strong>de</strong>ssin_arete_zone (chemin, taille_zone, X, Y)<br />

571<br />

572 dseuil = la [ int (nb * gran<strong>de</strong>) ] [ 2 ]<br />

573 nbtout = 0<br />

574 nb_change = 0<br />

575 iarete = 0<br />

576 retour = { }<br />

577 for t in xrange (1, taille+1): retour [t] = 0<br />

578<br />

579 while iarete < nb :<br />

580 nb_change = 0<br />

581 arete = la [iarete]<br />

582 iarete += 1<br />

583 x = arete [0]<br />

584 xm = arete [1]<br />

585 a = chemin [x]<br />

586 b = chemin [xm]<br />

587 d = distance ( a,b )<br />

588 if d < dseuil : break # arête trop petite<br />

589<br />

590 # zone traversée par la ligne<br />

591 x1,x2 = int (a [0] // taille_zone), int (b [0] // taille_zone)<br />

592 y1,y2 = int (a [1] // taille_zone), int (b [1] // taille_zone)<br />

593 <strong>en</strong>s = brl.trace_ligne (x1,y1,x2,y2)<br />

594 ville = []<br />

595 for k,l in <strong>en</strong>s:<br />

596 voisin = voisinage_zone_xy (k,l,X,Y)<br />

597 for u,v in voisin :<br />

598 ville.ext<strong>en</strong>d (zone [u][v])<br />

599<br />

600 # on supprime les doubles<br />

601 ville.sort ()<br />

602 if l<strong>en</strong> (ville) == 0 : continue<br />

603 sup = []<br />

604 mx = -1<br />

605 for v in ville:<br />

606 if v == mx : sup.app<strong>en</strong>d (v)<br />

607 mx = v<br />

608 for s in sup:<br />

609 ville.remove (s)<br />

83


610<br />

611 # on étudie les possibilités <strong>de</strong> casser l'arête (x,xm) aux al<strong>en</strong>tours <strong>de</strong>s villes<br />

612 # comprise dans l'<strong>en</strong>semble ville<br />

613 for t in xrange (1, taille+1):<br />

614<br />

615 for i in ville:<br />

616<br />

617 # on essaye d'insérer le sous-chemin (x- t + 1 + nb) --> x<br />

618 # au milieu <strong>de</strong> l'arête i,i+1<br />

619 b = echange_position_essai (chemin, (x- t + 1 + nb) % nb,x, i, False)<br />

620 if b :<br />

621 nb_change += 1<br />

622 retour [t] += 1<br />

623 continue<br />

624<br />

625 # on essaye d'insérer le sous-chemin (xm+ t - 1) --> xm<br />

626 # au milieu <strong>de</strong> l'arête i,i+1<br />

627 b = echange_position_essai (chemin, (xm + t - 1) % nb, xm, i, False)<br />

628 if b :<br />

629 nb_change += 1<br />

630 retour [t] += 1<br />

631 continue<br />

632<br />

633 # on essaye <strong>de</strong> casser l'arête x,xm <strong>en</strong> insérant<br />

634 # le sous-chemin i --> (i+t) % nb<br />

635 b = echange_position_essai (chemin, i, (i+t) % nb, x, False)<br />

636 if b :<br />

637 nb_change += 1<br />

638 retour [t] += 1<br />

639 continue<br />

640 # i<strong>de</strong>m<br />

641 b = echange_position_essai (chemin, i, (i+t) % nb, x, True)<br />

642 if b :<br />

643 retour [t] += 1<br />

644 nb_change += 1<br />

645 continue<br />

646 # i<strong>de</strong>m<br />

647 b = echange_position_essai (chemin, (i-t+nb) % nb, i, x, False)<br />

648 if b :<br />

649 nb_change += 1<br />

650 retour [t] += 1<br />

651 continue<br />

652 # i<strong>de</strong>m<br />

653 b = echange_position_essai (chemin, (i-t + nb) % nb, i, x, True)<br />

654 if b :<br />

655 retour [t] += 1<br />

656 nb_change += 1<br />

657 continue<br />

658<br />

659 nbtout += nb_change<br />

84


660<br />

661 print "nombre <strong>de</strong> déplacem<strong>en</strong>ts %d longueur : \t %10.0f détail \t" \<br />

662 % (nbtout, longueur_chemin (chemin)), " détail : ", retour<br />

663 return nbtout<br />

664<br />

665 <strong>de</strong>f supprime_croisem<strong>en</strong>t (chemin, taille_zone, X,Y) :<br />

666 """supprime les croisem<strong>en</strong>ts d'arêtes,<br />

667 retourne le nombre <strong>de</strong> changem<strong>en</strong>t effectués,<br />

668 X est le nombre <strong>de</strong> zones horizontalem<strong>en</strong>t,<br />

669 Y est le nombre <strong>de</strong> zones verticalem<strong>en</strong>t,<br />

670 taille_zone est la longueur d'un côté du carré d'une zone"""<br />

671<br />

672 nb = l<strong>en</strong>(chemin)<br />

673<br />

674 # zone associé à chaque arête<br />

675 zone = <strong>de</strong>ssin_arete_zone (chemin, taille_zone, X, Y)<br />

676 nbtout = 0<br />

677<br />

678 for i in xrange (0,nb) :<br />

679 im = (i+1) % nb<br />

680 a = chemin [i]<br />

681 b = chemin [im]<br />

682<br />

683 # zone traversée par la ligne<br />

684 x1,x2 = int (a [0] // taille_zone), int (b [0] // taille_zone)<br />

685 y1,y2 = int (a [1] // taille_zone), int (b [1] // taille_zone)<br />

686 <strong>en</strong>s = brl.trace_ligne (x1,y1,x2,y2)<br />

687 ville = []<br />

688 for k,l in <strong>en</strong>s:<br />

689 voisin = voisinage_zone_xy (k,l,X,Y)<br />

690 for u,v in voisin :<br />

691 ville.ext<strong>en</strong>d (zone [u][v])<br />

692<br />

693 # on supprime les doubles<br />

694 ville.sort ()<br />

695 if l<strong>en</strong> (ville) == 0 : continue<br />

696 sup = []<br />

697 mx = -1<br />

698 for v in ville:<br />

699 if v == mx : sup.app<strong>en</strong>d (v)<br />

700 mx = v<br />

701 for s in sup:<br />

702 ville.remove (s)<br />

703<br />

704 nb_change = 0<br />

705 for v in ville :<br />

706 b = retournem<strong>en</strong>t_essai (chemin, i,v)<br />

707 if b :<br />

708 nb_change += 1<br />

709 continue<br />

85


710 b = retournem<strong>en</strong>t_essai (chemin, im,v)<br />

711 if b :<br />

712 nb_change += 1<br />

713 continue<br />

714<br />

715 nbtout += nb_change<br />

716<br />

717 print "nombre <strong>de</strong> croisem<strong>en</strong>ts %d longueur : \t %10.0f détail \t" \<br />

718 % (nbtout, longueur_chemin (chemin))<br />

719 return nbtout<br />

720<br />

721 <strong>de</strong>f amelioration_chemin (chemin, taille_zone, X, Y, taille = 10, scre<strong>en</strong> = None):<br />

722 """amélioration du chemin par un algorithme simple,<br />

723 utilise <strong>de</strong>s retournem<strong>en</strong>ts <strong>de</strong> taille au plus taille,<br />

724 traite les arcs qui se crois<strong>en</strong>t,<br />

725 traite les grands arcs, utilise un quadrillage <strong>de</strong> taille window,<br />

726 X est le nombre <strong>de</strong> zones horizontalem<strong>en</strong>t,<br />

727 Y est le nombre <strong>de</strong> zones verticalem<strong>en</strong>t,<br />

728 taille_zone est la longueur d'un côté du carré d'une zone"""<br />

729<br />

730 white = 255,255,255<br />

731<br />

732 #première étape rapi<strong>de</strong><br />

733 nb = 1<br />

734 while nb > 0:<br />

735 nb = retournem<strong>en</strong>t (chemin, taille)<br />

736 if scre<strong>en</strong> != None:<br />

737 scre<strong>en</strong>.fill (white)<br />

738 display_neurone(chemin, scre<strong>en</strong>, 0)<br />

739 pygame.display.flip ()<br />

740<br />

741 # amélioration<br />

742 nb = 1<br />

743 while nb > 0:<br />

744 nb = retournem<strong>en</strong>t (chemin, taille)<br />

745 if scre<strong>en</strong> != None:<br />

746 scre<strong>en</strong>.fill (white)<br />

747 display_neurone(chemin, scre<strong>en</strong>, 0)<br />

748 pygame.display.flip ()<br />

749 nb += echange_position (chemin, taille / 2, taille_zone, X, Y)<br />

750 if scre<strong>en</strong> != None:<br />

751 scre<strong>en</strong>.fill (white)<br />

752 display_neurone(chemin, scre<strong>en</strong>, 0)<br />

753 pygame.display.flip ()<br />

754 nb += supprime_croisem<strong>en</strong>t (chemin, taille_zone,X,Y)<br />

755 if scre<strong>en</strong> != None:<br />

756 scre<strong>en</strong>.fill (white)<br />

757 display_neurone(chemin, scre<strong>en</strong>, 0)<br />

758 pygame.display.flip ()<br />

759<br />

86


760 if scre<strong>en</strong> != None :<br />

761 for ev<strong>en</strong>t in pygame.ev<strong>en</strong>t.get():<br />

762 if ev<strong>en</strong>t.type == pygame.MOUSEBUTTONUP :<br />

763 print "att<strong>en</strong>dre......................................................"<br />

764 att<strong>en</strong>dre_clic (scre<strong>en</strong>,0,0)<br />

765 print "reprise"<br />

766 break<br />

767<br />

768 ################################################################################<br />

769<br />

770 if __name__ == "__main__" :<br />

771 pygame.init ()<br />

772 # pysco.full () # pour accélérer le programme, va trois à quatre fois plus vite<br />

773<br />

774 nb_ville = 500<br />

775 partie = 10<br />

776<br />

777 size = width, height = x,y = 800, 500<br />

778 <strong>de</strong>c = size [0]<br />

779 black = 0, 0, 0<br />

780 white = 255,255,255<br />

781 scre<strong>en</strong> = pygame.display.set_mo<strong>de</strong>(size)<br />

782 villes = construit_ville (nb_ville, x, y)<br />

783 taille_zone = 20<br />

784 X = int (x // taille_zone)<br />

785 Y = int (y // taille_zone)<br />

786<br />

787 print "taille <strong>de</strong> l'image : ", size<br />

788 print "nombre <strong>de</strong> villes : ", l<strong>en</strong>(villes)<br />

789<br />

790 scre<strong>en</strong>.fill (white)<br />

791 display_ville (villes, scre<strong>en</strong>, 0)<br />

792 pygame.display.flip ()<br />

793 att<strong>en</strong>dre_clic(scre<strong>en</strong>,0,0)<br />

794<br />

795 print "calcul"<br />

796 window = int (math.sqrt (x*y / l<strong>en</strong>(villes)) * 2)<br />

797 arbre = arbre_poids_minimal (villes, window)<br />

798 arbre2 = copy.<strong>de</strong>epcopy (arbre)<br />

799<br />

800 print "<strong>de</strong>ssin" ,window<br />

801 display_arbre (villes, arbre)<br />

802 att<strong>en</strong>dre_clic(scre<strong>en</strong>,0,0)<br />

803<br />

804 print "circuit euléri<strong>en</strong>"<br />

805 chemin = circuit_euleri<strong>en</strong> (villes ,arbre)<br />

806<br />

807 print "circuit hamiltoni<strong>en</strong>"<br />

808 neurone = circuit_hamiltoni<strong>en</strong> (chemin)<br />

809<br />

87


810 print "<strong>de</strong>ssin"<br />

811 neurones = [ villes [i] for i in neurone ]<br />

812 display_neurone(neurones, scre<strong>en</strong>, 0)<br />

813 att<strong>en</strong>dre_clic(scre<strong>en</strong>,0,0)<br />

814<br />

815 print "amelioration_chemin et <strong>de</strong>ssin"<br />

816 amelioration_chemin (neurones, taille_zone, X, Y, partie, scre<strong>en</strong>)<br />

817 scre<strong>en</strong>.fill (white)<br />

818 display_ville (villes, scre<strong>en</strong>, 0)<br />

819 display_neurone(neurones, scre<strong>en</strong>, 0)<br />

820 att<strong>en</strong>dre_clic(scre<strong>en</strong>,0,0)<br />

821<br />

822 # taille double<br />

823 print "taille double"<br />

824 size = (size [0], size [1])<br />

825 scre<strong>en</strong> = pygame.display.set_mo<strong>de</strong>(size)<br />

826 scre<strong>en</strong>.fill (white)<br />

827 #display_arbre (villes, arbre2, 2)<br />

828 display_neurone (neurones, scre<strong>en</strong>, 2)<br />

829 att<strong>en</strong>dre_clic(scre<strong>en</strong>,0,0)<br />

830<br />

n correction exemple E8<br />

⊓⊔<br />

88


9 Filtrage d'un signal sonore<br />

9.1 Enoncé E9<br />

Un signal sonore est une série temporelle où chaque valeur est une amplitu<strong>de</strong> qui dénit le son p<strong>en</strong>dant<br />

une très courte durée - quelques dizaines <strong>de</strong> microsecon<strong>de</strong>s - (voir la gure 9.1). Cette information est<br />

celle qu'on trouve sur <strong>de</strong>s supports numériques comme le compact disque. Les algorithmes <strong>de</strong> compression<br />

con<strong>de</strong>ns<strong>en</strong>t cette information <strong>en</strong> minimisant la dégradation du son. Via <strong>de</strong>s formats <strong>de</strong> codage tels que<br />

le format MP3, le son peut être stocké sous une forme compressée. Compressé, le son peut aussi voyager<br />

plus facilem<strong>en</strong>t. La téléphonie mobile utilise sans cesse <strong>de</strong>s algorithmes <strong>de</strong> compression an <strong>de</strong> réduire les<br />

coûts <strong>de</strong> transmission.<br />

9.1.1 Transformée <strong>de</strong> Fourier discrète<br />

La transformée <strong>de</strong> Fourier est une <strong>de</strong>s métho<strong>de</strong>s utilisée pour compresser le son. Le son est composé<br />

d'on<strong>de</strong>s, sa nature se prête bi<strong>en</strong> à sa décomposition <strong>en</strong> somme <strong>de</strong> fonctions sinusoïdales. La transformée<br />

<strong>de</strong> Fourier discrète permet <strong>de</strong> décomposer une série temporelle <strong>de</strong> pério<strong>de</strong> T , (Y 0 , . . . , Y T −1 ) :<br />

)<br />

Les coeci<strong>en</strong>ts<br />

(Ŷn<br />

n<br />

∀t 0, Y t = 1 T<br />

sont calculés comme suit :<br />

n=0<br />

T∑<br />

−1 ( ) 2iπtn<br />

Ŷ n exp<br />

T<br />

n=0<br />

T∑<br />

−1 (<br />

∀t 0, Ŷp = Y n exp − 2iπpn )<br />

T<br />

(9.1)<br />

(9.2)<br />

Le calcul <strong>de</strong>s coeci<strong>en</strong>ts nécessite N 2 additions et multiplications complexes.<br />

9.1.2 Transformée <strong>de</strong> Fourier rapi<strong>de</strong><br />

La transformée <strong>de</strong> Fourier rapi<strong>de</strong> ou Fast Fourier Transform (FFT) permet <strong>de</strong> calculer les coeci<strong>en</strong>ts <strong>de</strong> la<br />

transformée <strong>en</strong> un coût <strong>de</strong> O(T ln T ) au lieu <strong>de</strong> O(T 2 ) comme le suggère le paragraphe 9.1.1. L'algorithme<br />

est itératif, on calcule <strong>de</strong>s transformée <strong>de</strong> Fourier sur <strong>de</strong>s échantillons <strong>de</strong> taille croissante jusqu'à obt<strong>en</strong>ir<br />

les coeci<strong>en</strong>ts <strong>de</strong> la transformée <strong>de</strong> Fourier sur l'échantillon initiale. On considère la série temporelle<br />

(Y 0 , . . . , Y T −1 ), on suppose que T est paire et T = 2R.<br />

T∑<br />

−1<br />

∀t 0, Ŷp = Y n exp<br />

=<br />

=<br />

=<br />

n=0<br />

R−1<br />

∑<br />

Y 2n exp<br />

n=0<br />

R−1<br />

∑<br />

Y 2n exp<br />

n=0<br />

R−1<br />

∑<br />

Y 2n exp<br />

n=0<br />

(<br />

− 2iπpn )<br />

T<br />

(<br />

− 2iπp(2n) ) R−1<br />

∑<br />

+<br />

2R<br />

(<br />

− 2iπpn )<br />

+ exp<br />

R<br />

n=0<br />

Y 2n+1 exp<br />

(<br />

− 2iπp<br />

2R<br />

(<br />

)<br />

2iπp(2n + 1)<br />

−<br />

2R<br />

) R−1 ∑<br />

(<br />

Y 2n+1 exp − 2iπp(2n) )<br />

2R<br />

n=0<br />

(<br />

− 2iπpn ) (<br />

+ exp − 2iπp ) R−1 ∑<br />

(<br />

Y 2n+1 exp − 2iπpn )<br />

R<br />

2R<br />

R<br />

n=0<br />

(9.3)<br />

89


On construit les <strong>de</strong>ux sous-suites extraites <strong>de</strong> la suite (Y 0 , . . . , Y T −1 ). T est toujours pair, T = 2R. La<br />

première suite conti<strong>en</strong>t les termes pairs (Y 2t ) 0t


série, problème qui trouva sa solution à la n du XIX ◦ siècle lorsque lord Kelvin inv<strong>en</strong>ta un calculateur<br />

mécanique permettant <strong>de</strong> prévoir la hauteur <strong>de</strong>s marées.<br />

De grands noms jalonn<strong>en</strong>t l'histoire <strong>de</strong> l'analyse <strong>de</strong> Fourier comme Josiah Willard Gibbss qui se p<strong>en</strong>cha<br />

sur le problème <strong>de</strong> la converg<strong>en</strong>ce <strong>de</strong> la série <strong>de</strong> Fourier <strong>en</strong> un point <strong>de</strong> discontinuité <strong>en</strong> 1899. Laurant<br />

Schwartz au milieu du XX ◦ siècle rapprocha l'analyse <strong>de</strong> Fourier avec la théorie <strong>de</strong> la distribution. James<br />

Cooley inv<strong>en</strong>ta <strong>en</strong> 1965 la transformée <strong>de</strong> Fourier rapi<strong>de</strong>. Cette théorie a permis aussi <strong>de</strong> découvrir la<br />

structure <strong>de</strong> l'ADN, utilisée pour décrypter la diraction <strong>de</strong>s rayons X.<br />

Entre les <strong>de</strong>ux guerres, Hartley émit l'idée que l'homme localisait la prov<strong>en</strong>ance d'un son grâce au déphasage<br />

relatif <strong>de</strong>s on<strong>de</strong>s atteignant ses <strong>de</strong>ux oreilles. Ses recherches aboutir<strong>en</strong>t à la transformée <strong>de</strong> Hartley,<br />

qui décompose elle-aussi une fonction réelles <strong>en</strong> somme <strong>de</strong> fonctions sinusoïdales sans recourir aux nombres<br />

complexes.<br />

T<br />

∀t ∈ N ∩ [−T + 1, T − 1] , Ŷ ∑−1<br />

[<br />

t H = Y n<br />

n=0<br />

cos 2iπkn<br />

N<br />

]<br />

2iπkn<br />

+ sin<br />

N<br />

(9.5)<br />

Les coeci<strong>en</strong>ts <strong>de</strong> la transformée <strong>de</strong> Fourier peuv<strong>en</strong>t se déduire <strong>de</strong> ceux obt<strong>en</strong>us pour la transformée <strong>de</strong><br />

Hartley qui prés<strong>en</strong>te l'avantage d'être plus rapi<strong>de</strong> à calculer. Il existe égalem<strong>en</strong>t une version rapi<strong>de</strong> <strong>de</strong> la<br />

transformée <strong>de</strong> Hartley mise au point <strong>en</strong> 1984 (voir [Bracewell1986]).<br />

9.2 Programme, explications <strong>de</strong> l'exemple E9<br />

Le programme suivant ltre un signal sonore. Il décompose le signal à l'ai<strong>de</strong> d'une transformée <strong>de</strong> Fourier<br />

discrète puis le recompose après qu'une partie <strong>de</strong> son spectre a été <strong>en</strong>levée. Les gures 9.1 et 9.2 illustr<strong>en</strong>t<br />

les signaux originaux et modiée ainsi que le spectre du signal pour un court extrait du signal sonore choisi<br />

au milieu du sound10.wav. Les moy<strong>en</strong>nes <strong>fr</strong>équ<strong>en</strong>ces ont été conservées, intervalles [20, 30].<br />

La principale fonction du programme est la fonction filtre_son_extrait qui décompose un signal par<br />

l'intermédiaire <strong>de</strong> la fonction fft du module Numeric.FFT. Le signal est ltré, seules sont conservées les<br />

<strong>fr</strong>équ<strong>en</strong>ces comprises <strong>en</strong>tre a et b. Puis le signal est recomposé grâce à la fonction inverse_fft.<br />

La fonction filtre_son se cont<strong>en</strong>te <strong>de</strong> découper le signal par pério<strong>de</strong> <strong>de</strong> longueur div puis <strong>en</strong>voie chaque<br />

segm<strong>en</strong>t à la fonction précé<strong>de</strong>mm<strong>en</strong>t décrite. Les autres fonctions eectu<strong>en</strong>t les tâches annexes, lire un<br />

chier cont<strong>en</strong>ant un signal sonore, le jouer, acher un segm<strong>en</strong>t, acher les coeci<strong>en</strong>ts <strong>de</strong> sa décomposition.<br />

1 import pygame<br />

2 import pygame.mixer<br />

3 import pygame.sndarray<br />

4 import FFT<br />

5 import math<br />

6 import numpy as Numeric<br />

7 import string<br />

8 import copy<br />

9 import pylab<br />

10 import numpy<br />

11<br />

91


Fig. 9.1 Signal original et signal ltrée. Les basses et hautes <strong>fr</strong>équ<strong>en</strong>ces ont été <strong>en</strong>levées. Le spectre<br />

correspond est donné par la gure 9.2.<br />

12 pygame.mixer.init ()<br />

13 pygame.init ()<br />

14<br />

15<br />

16 fourier = None<br />

17 indice = None<br />

18<br />

19 <strong>de</strong>f get_sound ():<br />

20 """charge le son sound010.wav"""<br />

21 s = pygame.mixer.Sound ("sound010.wav")<br />

22 t = pygame.sndarray.array (s)<br />

23 return s<br />

24<br />

25 <strong>de</strong>f play_sound(s):<br />

26 """joue un son"""<br />

27 s.play ()<br />

28<br />

29 <strong>de</strong>f convert_array(t, s):<br />

30 """joue un son <strong>de</strong>crit dans un tableau a une dim<strong>en</strong>sion"""<br />

31<br />

32 s = pygame.sndarray.array (s)<br />

33 for i in range (0, l<strong>en</strong> (s)) :<br />

34 s [i]= t [i]<br />

35 #tt = Numeric.array ([ [x, x] for x in t] )<br />

36 #print tt [0:10]<br />

37 s = pygame.sndarray.make_sound (s)<br />

38 return s<br />

39<br />

92


Fig. 9.2 Transformée <strong>de</strong> Fourier du signal <strong>de</strong> la gure 9.1.<br />

40 <strong>de</strong>f array_sound(s):<br />

41 """convertit un son <strong>en</strong> un tableau d'<strong>en</strong>tiers"""<br />

42 a = pygame.sndarray.array(s)<br />

43 t = Numeric.array([i for i in xrange(0,l<strong>en</strong>(a))])<br />

44 for i in xrange(0,l<strong>en</strong>(a)): t [i] = a [i][0]<br />

45 return t<br />

46<br />

47 <strong>de</strong>f <strong>de</strong>ssine_son(mem,t,fourier,ind,a,b):<br />

48 """<strong>de</strong>ssine une partie du son, limite la taille a 512"""<br />

49 m = l<strong>en</strong> (mem)<br />

50 if m > 256 : m = 256<br />

51 x = [ i for i in xrange (ind,ind+m) ]<br />

52 y1 = [ mem[i] for i in xrange (ind,ind+m) ]<br />

53 y2 = [ t[i] for i in xrange (ind,ind+m) ]<br />

54 pylab.figure (1)<br />

55 p1 = pylab.plot (x,y1)<br />

56 p2 = pylab.plot (x,y2)<br />

57 pylab.title ("Fourier")<br />

58 pylab.xlabel ("<strong>fr</strong>equ<strong>en</strong>ce")<br />

59 pylab.ylabel ("amplitu<strong>de</strong>")<br />

60 pylab.leg<strong>en</strong>d ( ("son", "son + filtre"))<br />

61<br />

62 m = l<strong>en</strong> (fourier)<br />

63 if m > 256 : m = 256<br />

64 #x = [ i for i in xrange (0,m) ]<br />

65 pylab.figure (2)<br />

66 x = [ i for i in xrange (0,m) ]<br />

67 y1 = [ abs(fourier[i]) for i in xrange (0,m) ]<br />

68 y2 = []<br />

69 for i in x :<br />

70 if a


73 p4 = pylab.plot (x,y2)<br />

74 pylab.leg<strong>en</strong>d ( ("fourrier", "filtre"))<br />

75 pylab.show()<br />

76<br />

77 <strong>de</strong>f filtre_son_extrait(t,a,b):<br />

78 """calcul <strong>de</strong> la transformee <strong>de</strong> Fourier, application du filtre [a,b],<br />

79 recomposition du signal"""<br />

80 fft = FFT.fft (t)<br />

81 global fourier<br />

82 if fourier == None and indice != None : fourier = copy.copy(fft)<br />

83 for i in xrange(0,l<strong>en</strong>(t)):<br />

84 if a


123 print "filtrage [%d,%d]" % (a,b)<br />

124 filtre_son (t,a,b)<br />

125<br />

126 print "<strong>de</strong>ssin <strong>de</strong>s premiers instants, son filtre"<br />

127 <strong>de</strong>ssine_son (mem,t,fourier,indice, a,b)<br />

128<br />

129 print "son filtre"<br />

130 s = convert_array (t, s)<br />

131 play_sound (s)<br />

132<br />

133 pygame.time.<strong>de</strong>lay (6000)<br />

134<br />

135 essai ()<br />

n correction exemple E9<br />

⊓⊔<br />

95


10 Images <strong>de</strong> synthèse, lancer <strong>de</strong> rayon<br />

10.1 Enoncé E10<br />

Le lancer <strong>de</strong> rayon est une métho<strong>de</strong> couramm<strong>en</strong>t utilisée pour réaliser <strong>de</strong>s images <strong>de</strong> synthèse. L'exposé se<br />

limite au <strong>de</strong>ssin <strong>de</strong> décor incluant <strong>de</strong>s sphères et <strong>de</strong>s facettes triangulaires ou quadrilaètres. La métho<strong>de</strong><br />

peut être aisém<strong>en</strong>t ét<strong>en</strong>due à d'autres gures.<br />

10.1.1 La scène<br />

La scène est l'<strong>en</strong>semble <strong>de</strong>s objets et <strong>de</strong>s sources <strong>de</strong> lumière qui doiv<strong>en</strong>t être représ<strong>en</strong>tés par l'image <strong>de</strong><br />

synthèse. La surface <strong>de</strong> ces objets peut avoir diér<strong>en</strong>tes propriétés (surface mate, rééchissante, création<br />

d'un rayon ré<strong>fr</strong>acté). L'image <strong>de</strong> synthèse est une vue possible <strong>de</strong> cette scène dénie par la position <strong>de</strong> l'÷il<br />

d'un obervateur, matérialisé par un point (le c<strong>en</strong>tre <strong>de</strong> l'÷il), un angle <strong>de</strong> vue et un écran <strong>de</strong> projection<br />

qui sert <strong>de</strong> rétine.<br />

Un ÷il - ou un appareil photographique - capte tous les rayons <strong>de</strong> lumière qui travers<strong>en</strong>t l'objectif jusqu'à<br />

la pellicule (voir gure 10.1). La première idée pour construire une image <strong>de</strong> synthèse est simplem<strong>en</strong>t <strong>de</strong><br />

pr<strong>en</strong>dre une à une les sources <strong>de</strong> lumière, <strong>de</strong> calculer tous les rayons qu'elles produis<strong>en</strong>t et <strong>de</strong> s'intéresser<br />

à ceux qui intercept<strong>en</strong>t notre écran (voir gure 10.2). Malheureusem<strong>en</strong>t, le nombre <strong>de</strong> rayons qui vi<strong>en</strong>n<strong>en</strong>t<br />

toucher l'écran est négligeable par rapport à ceux émis par les sources <strong>de</strong> lumière. Il est préférable <strong>de</strong> ne<br />

considérer que ceux qui nous sont utiles pour calculer l'image <strong>de</strong> synthèse, c'est-à-dire ceux qui atteign<strong>en</strong>t<br />

la pellicule.<br />

Fig. 10.1 Principe <strong>de</strong>s appareils photographiques, la l<strong>en</strong>tille fait converger tous les rayons parallèles <strong>en</strong><br />

un seul point <strong>de</strong> l'écran. Lors du calcul d'une image <strong>de</strong> synthèse, on se conc<strong>en</strong>tre sur le seul rayon qui<br />

passe par le c<strong>en</strong>tre <strong>de</strong> la l<strong>en</strong>tille.<br />

On considère que tous les rayons utiles pour l'image arriv<strong>en</strong>t <strong>en</strong> un même point O qui est le c<strong>en</strong>tre <strong>de</strong> l'÷il<br />

<strong>de</strong> l'observateur. C'est le principe <strong>de</strong> la chambre noire. L'idée du lancer <strong>de</strong> rayon est <strong>de</strong> parcourir le même<br />

chemin que la lumière mais <strong>en</strong> s<strong>en</strong>s inverse (voir gure 10.3), c'est-à-dire <strong>de</strong> l'÷il vers la source <strong>de</strong> lumière.<br />

Pour cela, un écran quadrillé <strong>en</strong> pixel est placé <strong>de</strong>vant le point O (ou <strong>de</strong>rrière comme sur la gure 10.1).<br />

On lance un rayon partant <strong>de</strong> O, passant par le pixel <strong>de</strong> coordonnées (i, j) <strong>de</strong> l'image et on calcule la<br />

trajectoire <strong>de</strong> ce rayon an <strong>de</strong> déterminer la couleur <strong>de</strong> ce pixel. Si le rayon n'intercepte aucun objet et<br />

se perd à l'inni (le ciel par exemple), la couleur <strong>de</strong> l'image au point <strong>de</strong> l'écran traversé par le rayon sera<br />

donc noire (ou bleue si le rayon atteint le ciel). En revanche, si le rayon intercepte un objet <strong>de</strong> couleur<br />

rouge <strong>en</strong> un point P , on regar<strong>de</strong> si le point P est éclairé par une source <strong>de</strong> lumière :<br />

Si c'est le cas, la couleur <strong>de</strong> l'image au point <strong>de</strong> l'écran traversé par le rayon sera donc rouge.<br />

Si ce n'est pas le cas, cela veut dire que ce point <strong>de</strong> l'objet n'est pas éclairé (il y a un objet <strong>en</strong>tre la<br />

source et le point P ou la source n'éclaire pas dans cette direction), l'image au point <strong>de</strong> l'écran traversé<br />

par le rayon sera noire.<br />

96


Fig. 10.2 Peu nombreux les rayons qui part<strong>en</strong>t <strong>de</strong> la source lumineuse et qui atteign<strong>en</strong>t l'÷il. L'idée du<br />

lancer <strong>de</strong> rayon consiste donc à partir <strong>de</strong> l'÷il pour atteindre la source lumineuse.<br />

Une couleur est composée <strong>de</strong> trois couleurs primaires : rouge, vert, bleu. La dénition d'une couleur repose<br />

donc sur trois int<strong>en</strong>sités. Par soucis d'économie <strong>de</strong> mémoire et <strong>de</strong> traitem<strong>en</strong>t, les ordinateurs utilis<strong>en</strong>t <strong>en</strong><br />

général 256 niveaux d'int<strong>en</strong>sité pour chaque couleur primaire, soit 256 × 256 × 256 = 2 24 = 16 × 2 20 ≈ 16<br />

millions <strong>de</strong> couleurs. Le tableau suivant conti<strong>en</strong>t quelques couleurs triplets correspondant à <strong>de</strong>s couleurs<br />

usuelles.<br />

(0,0,0) noir (0,0,255) bleu<br />

(255,255,255) blanc (100,100,100) gris foncé<br />

(255,0,0) rouge (200,200,200) gris clair<br />

(0,255,0) vert (200,200,0) jaune clair<br />

Selon cette dénition, une source <strong>de</strong> lumière ponctuelle sera composée <strong>en</strong> fait <strong>de</strong> trois sources <strong>de</strong> lumières<br />

ponctuelles. Dans l'algorithme du lancer <strong>de</strong> rayon, chaque couleur sera traitée séparém<strong>en</strong>t. Une source <strong>de</strong><br />

lumière est <strong>en</strong> fait la juxtaposition <strong>de</strong> trois sources <strong>de</strong> couleurs indép<strong>en</strong>dantes. De cette manière un objet<br />

parfaitem<strong>en</strong>t bleu éclairé par une lumière parfaitem<strong>en</strong>t rouge sera noir.<br />

La métho<strong>de</strong> décrite plus haut ne considère que <strong>de</strong>s objets avec <strong>de</strong>s surfaces régulières et mates. Le rayon<br />

<strong>de</strong> lumière ne peut les traverser (ré<strong>fr</strong>ation dans une goutte d'eau) ou rebondir comme dans un miroir<br />

(réexion). Le schéma 10.3 (voir égalem<strong>en</strong>t la gure 10.8) illustre <strong>de</strong> quelle manière ce problème peut être<br />

résolu partiellem<strong>en</strong>t.<br />

10.1.2 Modèle d'illumination<br />

Les images produites par la métho<strong>de</strong> précé<strong>de</strong>nte ne sont pas satisfaisantes. Les surfaces non directem<strong>en</strong>t<br />

accessibles <strong>de</strong>puis une source lumière n'apparaiss<strong>en</strong>t pas à l'écran alors que dans la réalité, il existe une<br />

lumière diuse qui illumine toute la pièce et qui fait que l'on peut voir sous une table même si les lumières<br />

sont xées au plafond. De même, un plan faisant face à une source lumière sera beaucoup plus éclairé<br />

qu'un plan incliné par rapport à cette même source.<br />

La gure 10.4 permet <strong>de</strong> comparer l'illumination d'un plan Y incliné d'un angle c par rapport à un plan<br />

X non incliné. La quantité <strong>de</strong> lumière I reçue par les plans X et Y est proportionnelle aux angles a et b.<br />

Le plan Y est incliné et sur le schéma b < a, par conséqu<strong>en</strong>t Y recevra moins <strong>de</strong> lumière que X.<br />

Les modèles d'illumination propos<strong>en</strong>t <strong>de</strong>s moy<strong>en</strong>s <strong>de</strong> calculer l'int<strong>en</strong>sité lumineuse <strong>de</strong> chaque rayon <strong>de</strong><br />

manière à se rapprocher le plus possible <strong>de</strong> la réalité. Plus le modèle est complexe, plus le temps <strong>de</strong> calcul<br />

97


Fig. 10.3 Le rayon ré<strong>fr</strong>acté et le rayon rééchi peuv<strong>en</strong>t être considérés comme <strong>de</strong>ux autres rayons<br />

éman<strong>en</strong>ts <strong>de</strong> l'÷il même s'ils n'<strong>en</strong> vi<strong>en</strong>n<strong>en</strong>t pas directem<strong>en</strong>t. On peut <strong>de</strong> cette manière voir un objet à<br />

travers un miroir, mais il n'est pas possible qu'un objet mate soit éclairé à travers un miroir.<br />

Fig. 10.4 Les <strong>de</strong>ux plans <strong>de</strong> cette gure ont la même taille mais le second est incliné, il reçoit moins <strong>de</strong><br />

lumière et <strong>en</strong> r<strong>en</strong>voie d'autant moins.<br />

d'une image est long. Les modèles d'illumination sont <strong>de</strong>s compromis <strong>en</strong>tre temps <strong>de</strong> calcul et réalisme.<br />

Par exemple :<br />

Une source <strong>de</strong> lumière réelle est rarem<strong>en</strong>t ponctuelle et éclaire rarem<strong>en</strong>t dans toute les directions mais<br />

sa modélisation peut l'être car cette simplication ne nuit pas trop à la qualité <strong>de</strong> l'image.<br />

Une surface peut être parfaitem<strong>en</strong>t rééchissante comme un miroir mais aussi simplem<strong>en</strong>t brillante ou<br />

mate, ces trois diér<strong>en</strong>ts types <strong>de</strong> surfaces ne capt<strong>en</strong>t pas la lumière <strong>de</strong> la même manière. Si un miroir<br />

est facile à modéliser, une surface brillante n'est jamais tout-à-fait rééchissante ni tout-à-fait mate :<br />

c'est un compromis <strong>en</strong>tre les <strong>de</strong>ux. On pourra considérer qu'une partie k du rayon est rééchie, l'autre<br />

1 − k est diusée dans toutes les directions.<br />

10.1.3 Géométrie et sphère<br />

Le technique du lancer <strong>de</strong> rayon nécessite le calcul d'intersection <strong>en</strong>tre une droite et tout type d'objet<br />

représ<strong>en</strong>té. Par la suite, le modèle d'illumination choisi (voir paragraphe 10.1.7) requiert le calcul du<br />

vecteur normal <strong>en</strong> tout point <strong>de</strong> la surface <strong>de</strong> l'objet. Ce paragraphe s'intéresse au cas particulier où les<br />

98


objets <strong>de</strong> la scène sont tous <strong>de</strong>s sphères.<br />

Dénition 10.1 : rayon<br />

Un rayon est simplem<strong>en</strong>t une <strong>de</strong>mi-droite avec une origine et un s<strong>en</strong>s <strong>de</strong> direction. L'origine est<br />

<strong>en</strong> général le c<strong>en</strong>tre <strong>de</strong> l'÷il.<br />

Dans le cas du lancer <strong>de</strong> rayon, chaque rayon partira <strong>de</strong> l'÷il <strong>de</strong> l'observateur, ira ou non intercepter un<br />

objet. Dans le cas où le rayon est intercepté, on regar<strong>de</strong> si un rayon prov<strong>en</strong>ant d'une source <strong>de</strong> lumière<br />

peut atteindre ce point donc si le rayon reliant ce point à la source <strong>de</strong> lumière n'intercepte pas un autre<br />

objet.<br />

Les calculs du point d'intersection sont prés<strong>en</strong>tés dans le cas où cet objet est une sphère. Un rayon est<br />

représ<strong>en</strong>té <strong>de</strong> manière paramétrique comme suit :<br />

⎧<br />

⎨<br />

⎩<br />

⎛<br />

x = λv x + O x<br />

y = λv y + O y , λ 0 où O est l'origine du rayon et −→ V = ⎝<br />

z = λv z + O z<br />

v x<br />

v y<br />

v z<br />

⎞<br />

⎠ sa direction (10.1)<br />

L'équation d'une sphère est la suivante :<br />

(x − C x ) 2 + (y − C y ) 2 + (z − C z ) 2 = R 2 (10.2)<br />

R est le rayon et C le c<strong>en</strong>tre <strong>de</strong> la sphère. Un point M = (x, y, z) est l'intersection d'une droite et d'une<br />

sphère s'il apparti<strong>en</strong>t à la fois à la droite et à la sphère. On obti<strong>en</strong>t l'équation :<br />

(λv x + O x − C x ) 2 + (λv y + O y − C y ) 2 + (λv z + O z − C z ) 2 = R 2 (10.3)<br />

On pose −→ OC = (Cx − O x , C y − O y , C z − O z ) et :<br />

⇔ λ 2 ∥ ∥∥ −→ V<br />

∥ ∥∥<br />

2<br />

− 2λ<br />

−→ V .<br />

−→ OC +<br />

∥ ∥∥ −→ OC<br />

∥ ∥∥<br />

2<br />

− R 2 = 0 (10.4)<br />

Cette équation admet <strong>de</strong>s solutions si son discriminant réduit vérie :<br />

∆ ′ =<br />

( −→V −→<br />

) 2 ∥ ∥∥ −→<br />

∥ ( ∥∥<br />

2 ∥ ∥∥ −→<br />

∥ )<br />

∥∥<br />

2<br />

. OC − V OC − R<br />

2<br />

0 (10.5)<br />

C'est une équation du second <strong>de</strong>gré qui a au plus <strong>de</strong>ux solutions λ 1 et λ 2 :<br />

⎧<br />

⎪⎨<br />

⎪⎩<br />

λ 1 = −→ V . −→<br />

∥<br />

λ 2 = −→ V . −→<br />

∥<br />

√<br />

OC− ∆ ′<br />

∥ −→ =<br />

V ∥ 2<br />

√<br />

OC+ ∆ ′<br />

∥ −→ =<br />

V ∥ 2<br />

√<br />

−→ −→ (−→V<br />

−→<br />

) 2− ∥ ∥∥ −→<br />

∥ ( ∥∥ 2 ∥ ∥∥ −→<br />

∥ )<br />

∥∥ 2<br />

V · OC− . OC V OC −R 2<br />

∥ −→ V ∥ 2<br />

−→ V ·<br />

−→ OC+<br />

√ (−→V<br />

.<br />

−→ OC<br />

) 2− ∥ ∥∥ −→ V<br />

∥ ∥∥ 2 ( ∥ ∥∥ −→ OC<br />

∥ ∥∥ 2<br />

−R 2<br />

∥ −→ V<br />

)<br />

(10.6)<br />

∥ 2<br />

99


En remplaçant λ dans la représ<strong>en</strong>tation paramètrique <strong>de</strong> la droite, on obti<strong>en</strong>t les coordonnées <strong>de</strong>s intersections<br />

T (λ 1 ) et T (λ 2 ) si elles exist<strong>en</strong>t. Dans ce cas, on sait que T (λ 1 ) est placé <strong>de</strong>vant T (λ 2 ) et <strong>de</strong>vant<br />

l'observateur si :<br />

0 −→ V . −−−−−→ OT (λ 1 ) −→ V . −−−−−→ OT (λ 2 ) ⇐⇒ 0 λ 1 λ 2 (10.7)<br />

Pour un rayon, on calcule l'intersection <strong>de</strong> ce rayon avec chaque objet <strong>de</strong> la scène. L'observateur ne pourra<br />

percevoir que l'objet le plus proche placé <strong>de</strong>vant lui, c'est-à-dire le plus proche <strong>de</strong>s points d'intersection.<br />

De plus, ce point le plus proche ne sera visible que s'il éclairé (voir gure 10.5). Ceci signie qu'il existe un<br />

autre rayon qui relie une source lumineuse à ce point, qui n'intercepte aucun objet y compris celui dont<br />

fait partie ce point.<br />

Fig. 10.5 Partie non éclairées par la source lumineuse, la plus petite <strong>de</strong>s trois sphères s'interpos<strong>en</strong>t<br />

<strong>en</strong>tre la plus gran<strong>de</strong> et la source lumineuse. La gran<strong>de</strong> sphère arborera une zone d'ombre. Elle s'interpose<br />

égalem<strong>en</strong>t <strong>en</strong>tre l'÷il et l'autre petite sphère dont une partie seulem<strong>en</strong>t sera visible.<br />

A partir du mom<strong>en</strong>t où on connaît le c<strong>en</strong>tre C d'une sphère, on peut déterminer la normale à cette sphère<br />

<strong>en</strong> tout point P <strong>de</strong> cette surface :<br />

−→ −→<br />

−→ CP CP<br />

N =<br />

CP = R<br />

(10.8)<br />

10.1.4 Géométrie et facettes<br />

Le paragraphe précé<strong>de</strong>nt étudiait l'intersection d'un rayon et d'une sphère. Celui-ci étudie l'intersection<br />

d'un rayon et d'une triangle ou d'un quadrilatère plan. Ces résultats peuv<strong>en</strong>t être ét<strong>en</strong>dus à tout polygone<br />

inscrit dans un même plan.<br />

Il faut d'abord calculer l'intersection <strong>en</strong>tre une droite et un plan <strong>de</strong> l'espace. Tout d'abord, on suppose<br />

que les points A,B,C form<strong>en</strong>t un triangle non réduit à une droite. A a pour coordonnées (x a , y a , z a ), B a<br />

pour coordonnées (x b , y b , z b ), C a pour coordonnées (x c , y c , z c ). Un vecteur normal au plan qui conti<strong>en</strong>t ce<br />

triangle est obt<strong>en</strong>u par :<br />

⎛<br />

−→<br />

AB ∧ −→ AC = ⎝<br />

(y b − y a ) (z c − z a ) − (y c − y a ) (z b − z a )<br />

(z b − z a ) (x c − x a ) − (z c − z a ) (x b − x a )<br />

(x b − x a ) (y c − y a ) − (x c − x a ) (y b − y a )<br />

⎞<br />

⎛<br />

⎠ = ⎝<br />

n x<br />

n y<br />

n z<br />

⎞<br />

⎠ = −→ n (10.9)<br />

Soit M un point <strong>de</strong> coordonnées M(x, y, z), le plan P qui conti<strong>en</strong>t le triangle ABC a pour équation<br />

cartési<strong>en</strong>ne :<br />

100


−→<br />

AB ∧ −→ AC · −−→ AM = 0 ⇐⇒ n x (x − x a ) + n y (y − y a ) + n z (z − z n ) = 0 (10.10)<br />

On suppose que la droite D passant par O = (x o , y o , z o ) et <strong>de</strong> vecteur directeur −→ v = (v x , v y , v z ) a pour<br />

équation paramétrique :<br />

⎧<br />

⎨ x o + λv x<br />

D = y o + λv y , λ ∈ R (10.11)<br />

⎩<br />

z o + λv z<br />

L'intersection <strong>en</strong>tre le plan P et la droite D est la solution <strong>de</strong> l'équation :<br />

n x (x o + λv x − x a ) + n y (y o + λv y − y a ) + n z (z o + λv z − z a ) = 0<br />

λ (n x v x + n y v y + n z v z ) + n x (x o − x a ) + n y (y o − y a ) + n z (z o − z a ) = 0<br />

λ −→ n · −→ v + −→ n · −→ AO = 0 (10.12)<br />

On suppose que la droite n'est pas parallèle au plan (c'est-à-dire −→ n · −→ v ≠ 0). Dans ce cas, on peut écrire<br />

que :<br />

λ = −<br />

Le point d'intersection I <strong>en</strong>tre le plan P et la droite D est donc :<br />

−→ n · −→ AO<br />

−→ n · −→ v<br />

(10.13)<br />

I = O −<br />

−→ −→ n · AO<br />

−→ −→<br />

−→ n · OA<br />

−→ n · −→ v = O +<br />

−→<br />

v<br />

−→ n · −→ v (10.14)<br />

v<br />

Après avoir calculé l'intersection <strong>en</strong>tre la droite D et le plan P , il faut déterminer si le point d'intersection<br />

apparti<strong>en</strong>t au triangle ABC. Si c'est le cas, la somme <strong>de</strong>s angles est égale à 2π (voir gure 10.6) :<br />

( −→ −→<br />

) ( −→ −→<br />

) ( −→ −→<br />

)<br />

IA, IB + IB, IC + IC, IA = 2π (10.15)<br />

Pour savoir si un point I apparti<strong>en</strong>t à un quadrilatère plan ABCD, la condition précé<strong>de</strong>nte <strong>de</strong>vi<strong>en</strong>t :<br />

( −→ −→<br />

) ( −→ −→<br />

) ( −→ −→<br />

) ( −→ −→<br />

)<br />

IA, IB + IB, IC + IC, ID + ID, IA = 2π (10.16)<br />

Il est facile d'adapter l'expression précé<strong>de</strong>nte pour tout polygone inscrit dans un plan dont l'intérieur ne<br />

forme qu'une seule composante connexe.<br />

101


Fig. 10.6 Si I est à l'intérieur du rectangle, la somme <strong>de</strong>s angles <strong>de</strong> c<strong>en</strong>tre I est égale à 2π. Si I est à<br />

l'extérieur du rectangle, la somme <strong>de</strong>s angles ce c<strong>en</strong>tre I est nulle.<br />

Fig. 10.7 Lancer d'un rayon, il part du c<strong>en</strong>tre <strong>de</strong> l'÷il, traverse l'écran avant d'atteindre un objet.<br />

10.1.5 Lancer <strong>de</strong> rayon<br />

Cette partie n'utilise plus le fait que les objets soi<strong>en</strong>t <strong>de</strong>s sphères, seuls compt<strong>en</strong>t les coordonnées <strong>de</strong>s<br />

points d'intersection et la normale à la surface <strong>de</strong>s objets <strong>en</strong> ces points. Il ne reste plus qu'à placer l'÷il <strong>de</strong><br />

l'observateur dans la scène et construire une image <strong>en</strong> fonction d'un angle <strong>de</strong> vue â qui décrit l'ouverture<br />

<strong>de</strong> l'÷il. Nous allons pour le mom<strong>en</strong>t lancer un seul rayon par pixel. Ce rayon a pour origine l'÷il O et<br />

pour direction un vecteur que nous allons constuire. Les gures 10.7, 10.8, 10.9 illustr<strong>en</strong>t cela.<br />

Pour caractériser précisem<strong>en</strong>t le point <strong>de</strong> vue <strong>en</strong> plus <strong>de</strong> l'angle <strong>de</strong> vue, il est nécessaire d'avoir une<br />

direction <strong>de</strong> visée −→ D qui détermine un cône <strong>de</strong> visée et un axe −→ ( A orthogonal au précé<strong>de</strong>nt qui détermine<br />

−→D, −→ −→ −→<br />

)<br />

( −→A −→ −→<br />

)<br />

la verticale. A , D ∧ A forme une base orthonormée <strong>de</strong> l'espace, , D ∧ A forme une base <strong>de</strong><br />

l'écran. On suppose que l'image à construire admet une résolution <strong>de</strong> n pixels sur m pixels. La gure 10.10<br />

illustre ces notations. On considère l'<strong>en</strong>semble <strong>de</strong> points :<br />

P n =<br />

{<br />

P nm<br />

ij<br />

= O + −→ D + i tan â<br />

2<br />

n<br />

−→ j tan â<br />

A +<br />

2<br />

−→ −→<br />

{<br />

D ∧ A , (i, j) ∈ − n n<br />

2 , . . . , n } {<br />

× − m 2 2 , . . . , m } } (10.17)<br />

2<br />

Le rayon<br />

(<br />

O, −−−−→ )<br />

OP nm<br />

ij<br />

est le rayon qui partira <strong>de</strong> l'÷il <strong>de</strong> l'observateur <strong>en</strong> O et passera par le pixel <strong>de</strong><br />

102


Fig. 10.8 Lancer d'un rayon, autre vue, le quadrillage matérialise les pixels. Le pixel pr<strong>en</strong>dra la couleur<br />

<strong>de</strong> l'objet qui recouvra le c<strong>en</strong>tre du pixel.<br />

Fig. 10.9 Lancer d'un rayon, le rayon part du c<strong>en</strong>tre <strong>de</strong> l'÷il, coupe l'écran <strong>en</strong> un pixel puis s'<strong>en</strong> va<br />

intercepter un objet.<br />

l'image <strong>de</strong> coordonnées ( i + n 2 , j + m 2<br />

)<br />

. On peut maint<strong>en</strong>ant décrire l'algorithme du lancer <strong>de</strong> rayon.<br />

Algorithme 10.2 : lancer <strong>de</strong> rayon<br />

On suppose que l'÷il est placé à l'origine. Le vecteur −→ D = (0, 0, 1) sépare l'÷il du c<strong>en</strong>tre <strong>de</strong><br />

l'écran composé <strong>de</strong> n × m pixels. −→ A = (1, 0, 0) et −→ D ∧ −→ A = (0, 1, 0). La scène conti<strong>en</strong>t N objets<br />

(O 1 , . . . , O N ) et M sources lumineuses (S 1 , . . . , S M ).<br />

pour i = 0 à n − 1 faire<br />

pour j = 0 à m − 1 faire<br />

On calcule les coordonnées du point P (x, y, z).<br />

x ←− (i− n 2 ) tan â<br />

2<br />

n<br />

y ←− (j− m 2 ) tan â<br />

2<br />

n<br />

z ←− 1<br />

pour k = 1 à N faire<br />

On calcul le point d'intersection <strong>en</strong>tre l'objet O k et le rayon<br />

(<br />

O, −→ OP<br />

). On<br />

obti<strong>en</strong>t le point P k et la normale à la surface <strong>de</strong> l'objet au point d'intersection<br />

−→<br />

N k .<br />

n pour On s'intéresse au point<br />

103<br />

P k ∗ qui minimise −→ −−→ OP · OPk et vérie<br />

−→<br />

OP · −−→ OP k > 0.


Fig. 10.10 Choix du repère, le vecteur → D ∧ → A pointe vers le lecteur.<br />

10.1.6 Antialiasing<br />

L'antialiasing a pour but <strong>de</strong> palier aux défauts <strong>de</strong> la discrétisation <strong>de</strong>s images <strong>en</strong> pixels. Il est possible<br />

que <strong>de</strong>ux rayons très proches l'un <strong>de</strong> l'autre ai<strong>en</strong>t <strong>de</strong>s trajectoires très diér<strong>en</strong>tes, il sut que l'un d'eux<br />

intercepte un objet et l'autre non, que l'un <strong>de</strong>ux soit presque tang<strong>en</strong>te à la surface <strong>de</strong> l'objet et l'autre<br />

ne fasse que l'eeurer. Dans ce cas, l'image montrera quelques irrégularités. Pour y remédier, on lance<br />

plusieurs rayons par pixel et l'int<strong>en</strong>sité d'un pixel sera une moy<strong>en</strong>ne <strong>de</strong>s int<strong>en</strong>sités obt<strong>en</strong>ues pour chaque<br />

rayon. Sans antialiasing, l'arête oblique d'un cube sera <strong>en</strong> forme d'escalier, l'antialiasing permet d'introduire<br />

un peu <strong>de</strong> ou plus discret pour l'÷il (voir gure 10.11).<br />

Fig. 10.11 Utilisation <strong>de</strong> l'antialiasing, les contours ne sont plus saccadés. Le nombre <strong>de</strong> pixels <strong>de</strong><br />

l'image n'a pas augm<strong>en</strong>té mais on ne passe plus brutalem<strong>en</strong>t <strong>de</strong> la couleur du fond à celle <strong>de</strong> l'objet, ce<br />

passage est lissé par l'antialiasing.<br />

L'antialiasing consiste à découper chaque pixel <strong>en</strong> un un carré <strong>de</strong> p petits pixels <strong>de</strong> côté. On note<br />

c nmp<br />

ip+p 1 j,jp+p 2<br />

le vecteur <strong>de</strong>s int<strong>en</strong>sités <strong>de</strong>s trois registres <strong>de</strong> couleurs obt<strong>en</strong>us par lancer <strong>de</strong> rayon pour<br />

(<br />

le rayon O, −−→ )<br />

OPij<br />

n pour une image <strong>de</strong> np × mp pixels. Le vecteur −−−→<br />

a n,m,p<br />

ij<br />

obt<strong>en</strong>u par antialiasing d'ordre<br />

p sur une image <strong>de</strong> n × m pixels est :<br />

a n,m,p<br />

ij<br />

= 1 p 2 ∑<br />

0p 1 ,p 2 p<br />

c nmp<br />

pi+p 1 ,pj+p 2<br />

(10.18)<br />

104


10.1.7 Modèle d'illumination <strong>de</strong> Phong<br />

Selon le modèle d'illumination <strong>de</strong> Phong, l'int<strong>en</strong>sité (ou quantité <strong>de</strong> lumière) <strong>de</strong> chacune <strong>de</strong>s trois composantes<br />

(rouge, vert, bleu) d'un rayon interceptant un objet est calculée comme suit :<br />

I = k a C<br />

} {{ } s + ∑<br />

I j<br />

1<br />

I 2<br />

{ }} {<br />

−→ −→<br />

k d C sN . Lj +<br />

S j<br />

I 3<br />

{ ⎛ }} {<br />

k s C r<br />

⎝ −→ −→<br />

L j + −→ ⎞n<br />

E<br />

N .<br />

∥ −→ L j + −→ ⎠<br />

E ∥<br />

(10.19)<br />

C + D j<br />

Chaque int<strong>en</strong>sité modélise une facette <strong>de</strong> la réalité :<br />

I 1 modélise la lumière ambi<strong>en</strong>te : même <strong>en</strong> pleine nuit, une faible lumière éclaire les objets <strong>en</strong> masquant<br />

les reliefs : c'est cette lumière qui est modélisée.<br />

I 2 modélise les impressions <strong>de</strong> relief : un plan incliné par rapport à la source <strong>de</strong> lumière reçoit moins <strong>de</strong><br />

lumière qu'un plan faisant face à cette source.<br />

I 3 modélise les reets : certaines surfaces ont <strong>de</strong>s propriétés rééchissantes, une surface brillante éblouit<br />

plus qu'une surface mate.<br />

Chaque paramètre à un s<strong>en</strong>s physique dans le modèle choisi :<br />

k a , k d , k s sont <strong>de</strong>s coeci<strong>en</strong>ts.<br />

C s est la couleur <strong>de</strong> l'objet.<br />

C r est la couleur <strong>de</strong> réexion <strong>de</strong> la surface, <strong>en</strong> générale égale à celle <strong>de</strong> la source.<br />

S j est l'int<strong>en</strong>sité <strong>de</strong> la j ◦ source <strong>de</strong> lumière.<br />

−→ N est la normale à la surface <strong>de</strong> l'objet au point d'interception.<br />

−→ L j est la direction <strong>de</strong> la droite passant par le point d'interception et la source <strong>de</strong> lumière.<br />

−→ E est la direction <strong>de</strong> la droite passant par l'÷il et le point d'interception.<br />

<br />

−→<br />

L j + −→ E<br />

∥ −→ L j + −→ ∥<br />

E<br />

est alors la bissectrice <strong>en</strong>tre −→ L j et −→ E .<br />

∥<br />

n est un paramètre (égal à 6 dans le programme) ce paramètre modélise l'ét<strong>en</strong>due du reet (n grand ⇒<br />

faible ét<strong>en</strong>due).<br />

D j est un coeci<strong>en</strong>t lié à l'objet.<br />

C est une constante.<br />

La gure 10.12 illustre <strong>de</strong>ux exemples obt<strong>en</strong>us avec le modèle d'illumination <strong>de</strong> Phong.<br />

10.1.8 Interpolation <strong>de</strong> Gouraud<br />

Les surfaces complexes <strong>de</strong>s objets sont souv<strong>en</strong>t approchés à l'ai<strong>de</strong> d'une multitu<strong>de</strong> <strong>de</strong> facettes triangulaires.<br />

Ce modèle introduit <strong>de</strong>s discontinuités visibles lors du calcul <strong>de</strong> l'image <strong>de</strong> synthèse. Ces points <strong>de</strong> discontinuités<br />

sont les arêtes et les sommets <strong>de</strong>s surfaces. Gouraud propose dans un premier temps <strong>de</strong> dénir la<br />

normale à une jonction <strong>en</strong>tre facettes comme la moy<strong>en</strong>ne <strong>de</strong>s vecteurs normaux <strong>de</strong> chaque facette.<br />

Chaque facette est un triangle plan, la normale le long <strong>de</strong> cette surface est constante. Dans le modèle <strong>de</strong><br />

Gourand, an <strong>de</strong> réduire <strong>en</strong>core les discontinuités, la couleur est le baryc<strong>en</strong>tre <strong>de</strong>s couleurs calculées à<br />

l'ai<strong>de</strong> <strong>de</strong>s normales estimées <strong>en</strong> chaque sommet <strong>de</strong>s facettes. Par ext<strong>en</strong>sion, il est possible d'associer les<br />

modèles d'illumination <strong>de</strong> Phong et <strong>de</strong> Gouraud <strong>en</strong> supposant que la normale <strong>en</strong> un point d'une facette<br />

est le baryc<strong>en</strong>tre <strong>de</strong>s normales <strong>en</strong> chaque sommet.<br />

105


Fig. 10.12 Deux exemples d'image <strong>de</strong> synthèse obt<strong>en</strong>ues avec le modèle d'illumination <strong>de</strong> Phong.<br />

10.1.9 Horizons<br />

La métho<strong>de</strong> du lancer <strong>de</strong> rayon permet d'obt<strong>en</strong>ir <strong>de</strong>s images <strong>de</strong> synthèse <strong>de</strong> bonne qualité. Cep<strong>en</strong>dant, elle<br />

gère dicilem<strong>en</strong>t les multiples réexions qui découle <strong>de</strong> la prés<strong>en</strong>ce d'un miroir dans la scène. Lorsqu'un<br />

rayon issu <strong>de</strong> l'÷il touche une surface, comm<strong>en</strong>t savoir quels seront les rayons qui atteindront une source<br />

<strong>de</strong> lumière après plusieurs reexions dans <strong>de</strong>s miroirs.<br />

Une autre technique, complém<strong>en</strong>taire à celle du lancer <strong>de</strong> rayon permet <strong>de</strong> traiter ce g<strong>en</strong>re <strong>de</strong> problème<br />

ainsi que celui <strong>de</strong> la lumière ambi<strong>en</strong>te. C'est la radiosité. Gourman<strong>de</strong> <strong>en</strong> calcul, elle découpe les surfaces<br />

<strong>en</strong> morceaux innitésimaux an d'estimer une int<strong>en</strong>sité lumineuse ambi<strong>en</strong>te. Il ne s'agit plus d'un rayon<br />

qui traverse l'espace mais d'une <strong>de</strong>nsité lumineuse qui occupe l'espace.<br />

10.2 Programme, explications <strong>de</strong> l'exemple E10<br />

Le programme est scindé <strong>en</strong> cinq chiers. Le premier implém<strong>en</strong>te les objets <strong>de</strong> base tels que les vecteurs,<br />

les pixels, les couleurs, les rayons. Un rayon est déni par une origine et une direction. Cette classe conti<strong>en</strong>t<br />

aussi sa couleur et le pixel <strong>de</strong> l'image auquel il correspond. Ce chier dénit aussi l'interface pour un objet<br />

et une source. Une source n'est qu'un point. La classe objet dénit cinq fonctions que tout objet doit<br />

possé<strong>de</strong>r pour pouvoir être <strong>de</strong>ssiné.<br />

La métho<strong>de</strong> intersection calcule l'intersection <strong>en</strong>tre l'objet et un rayon, elle retourne None si elle<br />

n'existe pas.<br />

Les métho<strong>de</strong>s normale et couleur retourne la normale et la couleur <strong>en</strong> un point <strong>de</strong> la surface <strong>de</strong> l'objet.<br />

Les métho<strong>de</strong>s rayon_re<strong>fr</strong>acte et rayon_reflechi retourn<strong>en</strong>t les rayons ré<strong>fr</strong>actés et rééchis s'ils<br />

exist<strong>en</strong>t.<br />

10.2.1 Fichier : image_synthese_base.py<br />

1 # -*- coding: cp1252 -*-<br />

106


Fig. 10.13 La première est obt<strong>en</strong>ue sans utiliser la métho<strong>de</strong> Gouraud, la secon<strong>de</strong> l'utilise.<br />

2 """définition <strong>de</strong>s objets permettant <strong>de</strong> construire une image <strong>de</strong> synthèse"""<br />

3 import math<br />

4 import copy<br />

5<br />

6 class vecteur (object) :<br />

7 """définit ce qu'est un point"""<br />

8 __slots__ = "x","y","z"<br />

9<br />

10 <strong>de</strong>f __str__ (self):<br />

11 """pour l'affichage"""<br />

12 return "(%3.2f,%3.2f,%3.2f)" % (self.x, self.y, self.z)<br />

13<br />

14 <strong>de</strong>f __init__(self,x,y,z):<br />

15 """initialisation"""<br />

16 self.x, self.y, self.z = float (x), float (y), float (z)<br />

17<br />

18 <strong>de</strong>f __add__ (self,p):<br />

19 """addition <strong>de</strong> <strong>de</strong>ux points"""<br />

20 return vecteur (self.x + p.x, self.y + p.y, self.z + p.z)<br />

21<br />

22 <strong>de</strong>f __neg__ (self):<br />

23 """retourne l'opposé d'un vecteur"""<br />

24 return vecteur (-self.x,-self.y,-self.z)<br />

25<br />

26 <strong>de</strong>f __iadd__ (self,p):<br />

27 """addition <strong>de</strong> <strong>de</strong>ux points"""<br />

28 self.x += p.x<br />

29 self.y += p.y<br />

30 self.z += p.z<br />

31 return self<br />

32<br />

107


33 <strong>de</strong>f __sub__ (self,p):<br />

34 """soustraction <strong>de</strong> <strong>de</strong>ux points"""<br />

35 return vecteur (self.x - p.x, self.y - p.y, self.z - p.z)<br />

36<br />

37 <strong>de</strong>f __isub__ (self,p):<br />

38 """soustraction <strong>de</strong> <strong>de</strong>ux points"""<br />

39 self.x -= p.x<br />

40 self.y -= p.y<br />

41 self.z -= p.z<br />

42 return self<br />

43<br />

44 <strong>de</strong>f __mul__ (self,x):<br />

45 """multiplication par un scalaire"""<br />

46 return vecteur (self.x * x, self.y * x, self.z * x)<br />

47<br />

48 <strong>de</strong>f __imul__ (self,x):<br />

49 """multiplication par un scalaire"""<br />

50 self.x *= x<br />

51 self.y *= x<br />

52 self.z *= x<br />

53 return self<br />

54<br />

55 <strong>de</strong>f __div__ (self,x):<br />

56 """division par un scalaire"""<br />

57 return vecteur (self.x / x, self.y / x, self.z / x)<br />

58<br />

59 <strong>de</strong>f __idiv__ (self,x):<br />

60 """division par un scalaire"""<br />

61 self.x /= x<br />

62 self.y /= x<br />

63 self.z /= x<br />

64 return self<br />

65<br />

66 <strong>de</strong>f norme2 (self) :<br />

67 """retourne la norme du vecteur au carrée"""<br />

68 return self.x * self.x + self.y * self.y + self.z * self.z<br />

69<br />

70 <strong>de</strong>f scalaire (self, v) :<br />

71 """calcule le produit scalaire <strong>en</strong>tre self et v"""<br />

72 return self.x * v.x + self.y * v.y + self.z * v.z<br />

73<br />

74 <strong>de</strong>f vectoriel (self, v) :<br />

75 """calcule le produit vectoriel <strong>en</strong>tre self et v"""<br />

76 res = vecteur (0,0,0)<br />

77 res.x = self.y * v.z - self.z * v.y<br />

78 res.y = self.z * v.x - self.x * v.z<br />

79 res.z = self.x * v.y - self.y * v.x<br />

80 return res<br />

81<br />

82 <strong>de</strong>f norme (self) :<br />

108


83 """retourne la norme du vecteur"""<br />

84 return math.sqrt (self.norme2 ())<br />

85<br />

86 <strong>de</strong>f r<strong>en</strong>orme (self) :<br />

87 """r<strong>en</strong>orme ce vecteur"""<br />

88 n = self.norme ()<br />

89 if n > 0 : return self / n<br />

90 else : return copy.copy (self)<br />

91<br />

92 <strong>de</strong>f cosinus (self, v) :<br />

93 """retourne le cosinus <strong>de</strong> <strong>en</strong>tre le vecteur self et le vecteur r"""<br />

94 sc = self.scalaire (v)<br />

95 n1 = self.norme ()<br />

96 n2 = v.norme ()<br />

97 n = n1 * n2<br />

98 if n == 0 : return 0<br />

99 return float (sc) / float (n)<br />

100<br />

101 <strong>de</strong>f sinus (self, v, norm) :<br />

102 """retourne le sinus <strong>de</strong> <strong>en</strong>tre le vecteur self et le vecteur r,<br />

103 norm est un vecteur normal et <strong>de</strong> norme 1 permettant d'ori<strong>en</strong>ter<br />

104 le plan dans lequel se trouve les <strong>de</strong>ux vecteurs dont il faut mesurer le sinus"""<br />

105 sc = self.vectoriel (v)<br />

106 n1 = self.norme ()<br />

107 n2 = v.norme ()<br />

108 n = n1 * n2<br />

109 if n == 0 : return 0<br />

110 return sc.scalaire (norm) / float (n)<br />

111<br />

112 <strong>de</strong>f angle (self, v, norm) :<br />

113 """retourne l'angle <strong>en</strong>tre les vecteur self et v,<br />

114 retourne un angle compris <strong>en</strong>tre -pi et pi,<br />

115 norm est la direction du vecteur normal au plan <strong>de</strong>s <strong>de</strong>ux vecteurs"""<br />

116 cos = self.cosinus (v)<br />

117 sin = self.sinus (v, norm)<br />

118 angle = math.atan2 (sin, cos)<br />

119 if angle > math.pi : angle -= math.pi * 2<br />

120 return angle<br />

121<br />

122 <strong>de</strong>f diff_abs (self, v):<br />

123 """retourne la somme <strong>de</strong>s valeurs absolues <strong>de</strong>s différ<strong>en</strong>tes <strong>en</strong>tre coordonnées"""<br />

124 r = abs (self.x - v.x)<br />

125 r += abs (self.y - v.y)<br />

126 r += abs (self.z - v.z)<br />

127 return r<br />

128<br />

129 <strong>de</strong>f __eq__ (self, v) :<br />

130 """définit l'égalité <strong>en</strong>tre <strong>de</strong>ux vecteurs"""<br />

131 if v == None : return False<br />

132 return self.diff_abs (v) < 1e-10<br />

109


133<br />

134 <strong>de</strong>f __ne__ (self, v) :<br />

135 """définit l'égalité <strong>en</strong>tre <strong>de</strong>ux vecteurs"""<br />

136 if v == None : return True<br />

137 return self.diff_abs (v) > 1e-10<br />

138<br />

139<br />

140 class couleur (vecteur) :<br />

141 """une couleur est un vecteur dont les coordonnées sont comprises <strong>en</strong>tre 0 et 1,<br />

142 x rouge, y vert, z bleu"""<br />

143<br />

144 <strong>de</strong>f __init__ (self, x,y,z) :<br />

145 vecteur.__init__(self, x,y,z)<br />

146 self.borne ()<br />

147<br />

148 <strong>de</strong>f borne (self) :<br />

149 """si une couleur est hors bornes, réajuste la couleur, pr<strong>en</strong>d le maximum <strong>de</strong>vi<strong>en</strong>t 1,<br />

150 les autres int<strong>en</strong>sités sont ajustées selon ce facteur d'échelle"""<br />

151 if self.x < 0 : self.x = 0<br />

152 if self.y < 0 : self.y = 0<br />

153 if self.z < 0 : self.z = 0<br />

154 m = max (self.x, self.y)<br />

155 m = max (m, self.z)<br />

156 if m > 1 :<br />

157 self.x /= m<br />

158 self.y /= m<br />

159 self.z /= m<br />

160<br />

161 <strong>de</strong>f __add__ (self,p):<br />

162 """addition <strong>de</strong> <strong>de</strong>ux couleurs"""<br />

163 return couleur (self.x + p.x, self.y + p.y, self.z + p.z)<br />

164<br />

165 <strong>de</strong>f produit_terme (self, v) :<br />

166 """effectue un produit terme à terme"""<br />

167 return couleur (self.x * v.x, self.y * v.y, self.z * v.z)<br />

168<br />

169 <strong>de</strong>f __mul__ (self,x):<br />

170 """multiplication par un scalaire"""<br />

171 return couleur (self.x * x, self.y * x, self.z * x)<br />

172<br />

173<br />

174<br />

175 class repere (object) :<br />

176 """définition d'un repère orthonormé"""<br />

177<br />

178 <strong>de</strong>f __init__ (self, origine = vecteur (0,0,0), \<br />

179 axex = vecteur (1,0,0), \<br />

180 axey = vecteur (0,1,0), \<br />

181 axez = vecteur (0,0,1)) :<br />

182 """initialisation, origine et les trois axes"""<br />

110


183 self.origine = origine<br />

184 self.x = axex<br />

185 self.y = axey<br />

186 self.z = axez<br />

187<br />

188 <strong>de</strong>f coordonnees (self, v) :<br />

189 """on suppose que les coordonnées <strong>de</strong> v sont exprimées dans ce repère,<br />

190 calcule les coordonnées <strong>de</strong> v dans le repère d'origine"""<br />

191 res = copy.copy (self.origine)<br />

192 res.x += v.x * self.x.x + v.y * self.y.x + v.z * self.z.x<br />

193 res.y += v.x * self.x.y + v.y * self.y.y + v.z * self.z.y<br />

194 res.z += v.x * self.x.z + v.y * self.y.z + v.z * self.z.z<br />

195 return res<br />

196<br />

197 <strong>de</strong>f __str__ (self):<br />

198 """affichage"""<br />

199 s = "origine : " + str (self.origine) + "\n"<br />

200 s += "axe <strong>de</strong>s x : " + str (self.x) + "\n"<br />

201 s += "axe <strong>de</strong>s y : " + str (self.y) + "\n"<br />

202 s += "axe <strong>de</strong>s z : " + str (self.z) + "\n"<br />

203 return s<br />

204<br />

205 class pixel (object) :<br />

206 """définit ce qu'est un pixel"""<br />

207 __slots__ = "x","y"<br />

208<br />

209 <strong>de</strong>f __init__(self,x,y):<br />

210 """initialisation"""<br />

211 self.x, self.y = int (x), int (y)<br />

212<br />

213 <strong>de</strong>f __str__ (self):<br />

214 """pour l'affichage"""<br />

215 return "(%d, %d)" % (self.x, self.y)<br />

216<br />

217<br />

218<br />

219<br />

220 class rayon (object) :<br />

221 """définit ce qu'est un rayon"""<br />

222 __slots__ = "origine", "direction", "pixel", "couleur"<br />

223<br />

224 <strong>de</strong>f __init__ (self, origine, direction, pixel, couleur):<br />

225 """initialisation"""<br />

226 self.origine, self.direction, self.pixel, self.couleur = \<br />

227 origine, direction, pixel, couleur<br />

228<br />

229 <strong>de</strong>f __str__ (self):<br />

230 """pour l'affichage"""<br />

231 s = "origine : " + str (self.origine)<br />

232 s += " direction : " + str (self.direction)<br />

111


233 s += " pixel : " + str (self.pixel)<br />

234 s += " couleur : " + str (self.couleur)<br />

235 return s<br />

236<br />

237<br />

238 class objet (object):<br />

239 """définit l'interface pour un objet à <strong>de</strong>ssiner dans une image <strong>de</strong> synthese"""<br />

240<br />

241 <strong>de</strong>f intersection (self, r) :<br />

242 """retourne le point d'intersection avec le rayon r,<br />

243 retourne None s'il n'y pas d'intersection"""<br />

244 return None<br />

245<br />

246 <strong>de</strong>f normale (self, p, rayon) :<br />

247 """retourne la normale au point <strong>de</strong> coordonnée p, et connaissant le rayon"""<br />

248 return None<br />

249<br />

250 <strong>de</strong>f couleur_point (self, p) :<br />

251 """retourne la couleur au point <strong>de</strong> coordonnée p"""<br />

252 return None<br />

253<br />

254 <strong>de</strong>f rayon_re<strong>fr</strong>acte (self, rayon, p) :<br />

255 """retourne le rayon ré<strong>fr</strong>acté au point p <strong>de</strong> la surface,<br />

256 si aucune, retourne None"""<br />

257 return None<br />

258<br />

259 <strong>de</strong>f rayon_reflechi (self, rayon, p) :<br />

260 """retourne le rayon réfléchi au point p <strong>de</strong> la surface,<br />

261 si aucune, retourne None"""<br />

262 return None<br />

263<br />

264 <strong>de</strong>f phong_coeffici<strong>en</strong>t (self):<br />

265 """retourne un coeffici<strong>en</strong>t propre à l'objet pour<br />

266 le modèle d'illumination <strong>de</strong> Phong"""<br />

267 return float (0)<br />

268<br />

269<br />

270<br />

271 class source (object) :<br />

272 """définition d'une source ponctuelle"""<br />

273 __slots__ = "origine", "couleur"<br />

274<br />

275 <strong>de</strong>f __init__ (self, origine, couleur):<br />

276 """initialisation"""<br />

277 self.origine, self.couleur = origine, couleur<br />

278<br />

279 <strong>de</strong>f __str__ (self) :<br />

280 """affichage"""<br />

281 return "source : " + str (self.origine) + " couleur : " + str (self.couleur)<br />

282<br />

112


283 if __name__ == "__main__" :<br />

284 v = vecteur (0,1,2)<br />

285 u = vecteur (0,1,2)<br />

286 w = u + v<br />

287 print u,v,w<br />

288 print w * 6<br />

289 p = pixel (5,5)<br />

290 print p<br />

291 c = couleur (1,1,1)<br />

292 print c<br />

293 r = rayon (u,w,p,c)<br />

294 print r<br />

295 s = source (v, c)<br />

296 print s<br />

297<br />

10.2.2 Fichier : image_synthese_sphere.py<br />

Le chier suivant décrit une sphère. Cette classe hérite <strong>de</strong> objet et surcharge les métho<strong>de</strong>s intersection,<br />

normale, couleur. Cette sphère ne rééchit aucun rayon et ne produit aucun rayon ré<strong>fr</strong>acté.<br />

1 # -*- coding: cp1252 -*-<br />

2 """définition d'une sphère"""<br />

3 import image_synthese_base as base<br />

4 import math<br />

5<br />

6 class sphere (base.objet):<br />

7 """définit une sphère"""<br />

8 __slots__ = "c<strong>en</strong>tre", "rayon", "couleur"<br />

9<br />

10 <strong>de</strong>f __init__ (self, c<strong>en</strong>tre, rayon, couleur):<br />

11 """initialisation"""<br />

12 self.c<strong>en</strong>tre, self.rayon, self.couleur = c<strong>en</strong>tre, float (rayon), couleur<br />

13<br />

14 <strong>de</strong>f intersection (self, r) :<br />

15 """retourne le point d'intersection avec le rayon r,<br />

16 retourne None s'il n'y pas d'intersection"""<br />

17 oc = self.c<strong>en</strong>tre - r.origine<br />

18 vn = r.direction.norme2 ()<br />

19 s = r.direction.scalaire (oc)<br />

20 <strong>de</strong>lta = s*s - vn * (oc.norme2 () - self.rayon * self.rayon)<br />

21 if <strong>de</strong>lta < 0 : return None<br />

22 <strong>de</strong>lta = math.sqrt (<strong>de</strong>lta)<br />

23 l1 = (s - <strong>de</strong>lta) / vn<br />

24 l2 = (s + <strong>de</strong>lta) / vn<br />

25<br />

26 if 0 < l1 < l2 : l = l1<br />

27 elif l1 < 0 < l2 : l = l2<br />

28 elif 0 < l2 < l1 : l = l2<br />

29 elif l2 < 0 < l1 : l = l1<br />

113


30 else : l = None<br />

31<br />

32 if l == None : return None<br />

33<br />

34 v = r.origine + r.direction * l<br />

35 return v<br />

36<br />

37 <strong>de</strong>f normale (self, p, rayon) :<br />

38 """retourne la normale au point <strong>de</strong> coordonnée p"""<br />

39 v = (p - self.c<strong>en</strong>tre) / self.rayon<br />

40 return v<br />

41<br />

42 <strong>de</strong>f couleur_point (self, p) :<br />

43 """retourne la couleur au point <strong>de</strong> coordonnée p"""<br />

44 return self.couleur<br />

45<br />

46 <strong>de</strong>f __str__ (self):<br />

47 """affichage"""<br />

48 s = "sphère --- c<strong>en</strong>tre : " + str (self.c<strong>en</strong>tre)<br />

49 s += " rayon : " + str (self.rayon)<br />

50 s += " couleur : " + str (self.couleur)<br />

51 return s<br />

52<br />

53<br />

54<br />

55 if __name__ == "__main__" :<br />

56 s = sphere (base.vecteur (0,0,0), 5, base.couleur (0,1,0))<br />

57 r = base.rayon ( base.vecteur (10,0,0), base.vecteur (1,0,0), \<br />

58 base.pixel (0,0), base.couleur (0,0,0))<br />

59 print s<br />

60 print r<br />

61 p = s.intersection (r)<br />

62 print p<br />

63<br />

64<br />

10.2.3 Fichier : image_synthese_sc<strong>en</strong>e.py<br />

Le troisième chier dénit ce qu'est une scène, elle implém<strong>en</strong>te les fonctions qui permett<strong>en</strong>t <strong>de</strong> calculer<br />

les intersections <strong>en</strong>tre un rayon et les objets. La métho<strong>de</strong> mo<strong>de</strong>le_illumination implém<strong>en</strong>te un modèle<br />

d'illumination simpliée pour lequel l'int<strong>en</strong>sité <strong>de</strong>s couleurs est proportionnelle au cosinus <strong>en</strong>tre la normale<br />

à un objet et la prov<strong>en</strong>ance <strong>de</strong> la lumière. Ce petit exemple, qui ne dép<strong>en</strong>d d'aucun paramètre, permet <strong>de</strong><br />

corriger les diér<strong>en</strong>tes fonctions jusqu'ici implém<strong>en</strong>tées. En eet, il est préférable <strong>de</strong> tester son programme<br />

avant que celui-ci ne soit terminé. La détection <strong>de</strong>s erreurs est une étape parfois aussi longue que sa<br />

conception. Il est important <strong>de</strong> réduire le plus possible les sources possibles d'erreurs <strong>en</strong> ne testant que<br />

<strong>de</strong>s petites portions <strong>de</strong> co<strong>de</strong>s.<br />

1 # -*- coding: cp1252 -*-<br />

2 """définition d'une scène"""<br />

3 import image_synthese_base as base<br />

114


4 import image_synthese_sphere as obj<br />

5 import math<br />

6 import pygame<br />

7<br />

8 class sc<strong>en</strong>e (object):<br />

9 """définit une scène, les axes x,y sont ceux <strong>de</strong> l'écran,<br />

10 z-1 est la distance à l'écran du point (x,y,z)"""<br />

11<br />

12 <strong>de</strong>f __init__ (self, repere, alpha, x,y) :<br />

13 """définit la position <strong>de</strong> l'oeil, l'angle d'ouverture,<br />

14 et la taille <strong>de</strong> l'écran"""<br />

15 self.repere = repere<br />

16 self.alpha = float (alpha)<br />

17 self.dim = (int (x), int (y))<br />

18<br />

19 <strong>de</strong>f ajoute_source (self, source):<br />

20 """ajoute une source ponctuelle <strong>de</strong> lumière"""<br />

21 if not self.__dict__.has_key ("sources") : self.sources = []<br />

22 self.sources.app<strong>en</strong>d (source)<br />

23<br />

24 <strong>de</strong>f ajoute_objet (self, objet):<br />

25 """ajoute un objet à la scène"""<br />

26 if not self.__dict__.has_key ("objets") : self.objets = []<br />

27 self.objets.app<strong>en</strong>d (objet)<br />

28<br />

29 <strong>de</strong>f __str__ (self) :<br />

30 """affichage"""<br />

31 s = "scène ----------------------------\n"<br />

32 s += "repère : " + str (self.repere) + "\n"<br />

33 s += "angle d'ouverture : " + str (self.alpha) + "\n"<br />

34 s += "dim<strong>en</strong>sion <strong>de</strong> l'écran : " + str (self.dim) + "\n"<br />

35 if self.__dict__.has_key ("sources") :<br />

36 for a in self.sources : s += " " +str (a) + "\n"<br />

37 if self.__dict__.has_key ("objets") :<br />

38 for a in self.objets : s += " " + str (a) + "\n"<br />

39 return s<br />

40<br />

41 <strong>de</strong>f intersection (self, rayon) :<br />

42 """calcule le point d'intersection <strong>en</strong>tre un rayon et le plus proche <strong>de</strong>s objets,<br />

43 retourne l'objet et le point d'intersection"""<br />

44 if not self.__dict__.has_key ("objets") : return None, None<br />

45 p = rayon.origine<br />

46 sp,so = None, None<br />

47 for o in self.objets :<br />

48 i = o.intersection (rayon)<br />

49 if i == None : continue<br />

50 if rayon.direction.scalaire (i - p)


54 so = o<br />

55 else :<br />

56 v = i - p<br />

57 d = sp - p<br />

58 if v.norme2 () < d.norme2 () :<br />

59 sp = i<br />

60 so = o<br />

61 return so, sp<br />

62<br />

63 <strong>de</strong>f sources_atteintes (self, p) :<br />

64 """retourne la liste <strong>de</strong>s sources atteintes <strong>de</strong>puis une position p <strong>de</strong> l'espace,<br />

65 vérifie qu'aucun objet ne fait obstacle"""<br />

66 res = []<br />

67 for s in self.sources:<br />

68 r = base.rayon (s.origine, p - s.origine, base.pixel (0,0), s.couleur)<br />

69 o,i = self.intersection (r)<br />

70 if i == None : continue<br />

71 if (i - p).norme2 () < 1e-10 : # possible problème d'arrondi<br />

72 res.app<strong>en</strong>d (s)<br />

73 continue<br />

74 return res<br />

75<br />

76 <strong>de</strong>f construit_rayon (self, pixel) :<br />

77 """construit le rayon correspondant au pixel pixel"""<br />

78 x = (pixel.x - self.dim [0] / 2) * math.tan (self.alpha / 2) / min (self.dim)<br />

79 y = (pixel.y - self.dim [1] / 2) * math.tan (self.alpha / 2) / min (self.dim)<br />

80 v = base.vecteur (x,y,1)<br />

81 r = base.rayon (self.repere.origine, self.repere.coordonnees (v), \<br />

82 pixel, base.couleur (1,1,1))<br />

83 return r<br />

84<br />

85 <strong>de</strong>f mo<strong>de</strong>le_illumination (self, rayon, p, obj, source) :<br />

86 """calcule la couleur pour un rayon donné, un point p, un objet obj,<br />

87 et une source <strong>de</strong> lumière source"""<br />

88 n = obj.normale (p, rayon)<br />

89 cos = n.cosinus (source.origine - p)<br />

90 cl = obj.couleur_point (p) * cos<br />

91 cl = cl.produit_terme (rayon.couleur)<br />

92 return cl<br />

93<br />

94 <strong>de</strong>f couleur_fond (self) :<br />

95 """retourne la couleur du fond"""<br />

96 return base.couleur (0,0,0)<br />

97<br />

98 <strong>de</strong>f rayon_couleur (self, rayon, ref = True) :<br />

99 """retourne la couleur d'un rayon connaissant les objets,<br />

100 cette fonction doit être surchargée pour chaque modèle d'illumination,<br />

101 si ref == True, on ti<strong>en</strong>t compte <strong>de</strong>s rayons ré<strong>fr</strong>acté et réfléchi"""<br />

102<br />

103 list_rayon = [ rayon ]<br />

116


104 c = base.couleur (0,0,0)<br />

105 b = False<br />

106 while l<strong>en</strong> (list_rayon) > 0 :<br />

107 r = list_rayon.pop ()<br />

108 o,p = self.intersection (r)<br />

109<br />

110 if p == None : continue<br />

111<br />

112 if ref :<br />

113 t = o.rayon_re<strong>fr</strong>acte (r, p)<br />

114 if t != None : list_rayon.app<strong>en</strong>d (t)<br />

115 t = o.rayon_reflechi (r, p)<br />

116 if t != None : list_rayon.app<strong>en</strong>d (t)<br />

117<br />

118 sources = self.sources_atteintes (p)<br />

119 if l<strong>en</strong> (sources) == 0 : return base.couleur (0,0,0)<br />

120 for s in sources :<br />

121 cl = self.mo<strong>de</strong>le_illumination (r, p, o, s)<br />

122 c += cl<br />

123 b = True<br />

124<br />

125 if not b : c = self.couleur_fond ()<br />

126 else : c.borne ()<br />

127 return c<br />

128<br />

129 <strong>de</strong>f construit_image (self, scre<strong>en</strong>):<br />

130 """construit l'image <strong>de</strong> synthèse où scre<strong>en</strong> est un objet du module pygame"""<br />

131 count = 0<br />

132 nbpixel = int (self.dim [0] * self.dim [1] / 100)<br />

133 for y in xrange (0, self.dim [1]) :<br />

134 for x in xrange (0, self.dim [0]) :<br />

135 p = base.pixel (x,y)<br />

136 r = self.construit_rayon (p)<br />

137 c = self.rayon_couleur (r, True)<br />

138 q = (p.x,self.dim [1] - p.y - 1)<br />

139 d = (int (c.x * 255), int (c.y * 255), int (c.z * 255))<br />

140 pygame.draw.line (scre<strong>en</strong>, d, q,q)<br />

141 count += 1<br />

142 if count % 150 == 0 : pygame.display.flip ()<br />

143 if count % nbpixel == 0 : print "avancem<strong>en</strong>t " , count // nbpixel , "%"<br />

144 pygame.display.flip ()<br />

145<br />

146<br />

147 <strong>de</strong>f att<strong>en</strong>dre_clic ():<br />

148 """<strong>de</strong>ssine une croix sur l'écran et att<strong>en</strong>d la pression d'un clic <strong>de</strong> souris"""<br />

149 reste = True<br />

150 while reste:<br />

151 for ev<strong>en</strong>t in pygame.ev<strong>en</strong>t.get():<br />

152 if ev<strong>en</strong>t.type == pygame.MOUSEBUTTONUP :<br />

153 reste = False<br />

117


154 break<br />

155<br />

156<br />

157 if __name__ == "__main__" :<br />

158 s = sc<strong>en</strong>e (base.repere (), math.pi / 1.5, 400, 300)<br />

159 s.ajoute_source ( base.source (base.vecteur (0,10,10), \<br />

160 base.couleur (1,1,1) ) )<br />

161 s.ajoute_source ( base.source (base.vecteur (10,10,5), \<br />

162 base.couleur (0.5,0.5,0.5) ) )<br />

163 s.ajoute_objet ( obj.sphere (base.vecteur (0,0,12), \<br />

164 3, base.couleur (1,0,0) ) )<br />

165 s.ajoute_objet ( obj.sphere (base.vecteur (0,-400,12), \<br />

166 396, base.couleur (0.5,0.5,0.5) ) )<br />

167 print s<br />

168<br />

169 scre<strong>en</strong> = pygame.display.set_mo<strong>de</strong> (s.dim)<br />

170 scre<strong>en</strong>.fill ((255,255,255))<br />

171 s.construit_image (scre<strong>en</strong>)<br />

172<br />

173 print "image terminée"<br />

174 att<strong>en</strong>dre_clic ()<br />

175<br />

Fig. 10.14 Image <strong>de</strong> synthèse créée lors <strong>de</strong> l'éxecution du chier image_synthese_sc<strong>en</strong>e.py. Le modèle<br />

d'illumination est plus simple que celui <strong>de</strong> Phong. L'int<strong>en</strong>sité <strong>de</strong>s couleurs perceptible par l'observateur est<br />

proportionnelle au cosinus <strong>en</strong>tre la normale à la sphère et la direction <strong>de</strong> la lumière. La scène est composée<br />

<strong>de</strong> <strong>de</strong>ux sources <strong>de</strong> lumière, une petite sphère, une gran<strong>de</strong> qui joue le rôle du sol. Aucun anti-aliasing n'a<br />

été implém<strong>en</strong>té et on peut observer que le contour <strong>de</strong> la sphère est <strong>en</strong> escalier.<br />

10.2.4 Fichier : image_synthese_phong.py<br />

Le programme précé<strong>de</strong>nt permet d'obt<strong>en</strong>ir l'image <strong>de</strong> la gure 10.14. La gran<strong>de</strong> sphère et le bas <strong>de</strong> la<br />

sphère c<strong>en</strong>trale sont plutôt réussis. En revanche, le haut <strong>de</strong> sphère c<strong>en</strong>trale est uniformém<strong>en</strong>t rouge, le<br />

modèle est <strong>en</strong>core éloigné <strong>de</strong> la réalité. Le <strong>de</strong>rnier chier construit la classe sc<strong>en</strong>e_phong qui hérite <strong>de</strong><br />

118


sc<strong>en</strong>e. Elle redénit la métho<strong>de</strong> mo<strong>de</strong>le_illumination qui calcule la couleur d'un rayon lancé <strong>de</strong>puis<br />

l'÷il.<br />

1 # -*- coding: cp1252 -*-<br />

2 """implém<strong>en</strong>tation du modèle d'illumination <strong>de</strong> Phong"""<br />

3 import image_synthese_sc<strong>en</strong>e as sc<strong>en</strong>e<br />

4 import image_synthese_base as base<br />

5 import image_synthese_sphere as obj<br />

6 import math<br />

7 import pygame<br />

8<br />

9<br />

10 class sc<strong>en</strong>e_phong (sc<strong>en</strong>e.sc<strong>en</strong>e):<br />

11 """définit une scène et utilise le modèle d'illumination <strong>de</strong> Phong<br />

12 pour construire l'image <strong>de</strong> synthèse"""<br />

13<br />

14 <strong>de</strong>f __init__ (self, repere, alpha, x,y,<br />

15 ka = 0.1,<br />

16 kb = 0.8,<br />

17 kc = 0.3,<br />

18 reflet = 6,<br />

19 fond = base.couleur (200,200,200)) :<br />

20 """définit la position <strong>de</strong> l'oeil, l'angle d'ouverture,<br />

21 et la taille <strong>de</strong> l'écran"""<br />

22 sc<strong>en</strong>e.sc<strong>en</strong>e.__init__ (self, repere, alpha, x, y)<br />

23 self.ka, self.kb, self.kc = ka,kb,kc<br />

24 self.reflet = reflet<br />

25 self.fond = fond<br />

26 self.constante = float (1)<br />

27<br />

28 <strong>de</strong>f __str__ (self) :<br />

29 """affichage"""<br />

30 s = sc<strong>en</strong>e.sc<strong>en</strong>e.__str__ (self)<br />

31 s += "ka = %1.3f kb = %1.3f kc = %1.3f\n" % (self.ka,self.kb,self.kc)<br />

32 s += "reflet " + str (self.reflet) + "\n"<br />

33 s += "couleur du fond " + str (self.fond) + "\n"<br />

34 return s<br />

35<br />

36 <strong>de</strong>f couleur_fond (self) :<br />

37 """retourne la couleur du fond"""<br />

38 return self.fond * self.ka<br />

39<br />

40 <strong>de</strong>f mo<strong>de</strong>le_illumination (self, rayon, p, obj, source) :<br />

41 """calcule la couleur pour un rayon donné, un point p, un objet obj,<br />

42 et une source <strong>de</strong> lumière source"""<br />

43 n = obj.normale (p, rayon).r<strong>en</strong>orme ()<br />

44 vr = rayon.direction.r<strong>en</strong>orme ()<br />

45 vr *= float (-1)<br />

46 vs = source.origine - p<br />

47 vs = vs.r<strong>en</strong>orme ()<br />

119


48 bi = vs + vr<br />

49 bi = bi.r<strong>en</strong>orme ()<br />

50<br />

51 # premier terme<br />

52 cos = n.scalaire (vs)<br />

53 couleur = source.couleur.produit_terme (obj.couleur_point (p)) * (cos * self.kb)<br />

54<br />

55 # second terme : reflet<br />

56 cos = n.scalaire (bi) ** self.reflet<br />

57 couleur += source.couleur.produit_terme (source.couleur) * (cos * self.kc)<br />

58 couleur = couleur.produit_terme (rayon.couleur)<br />

59<br />

60 return couleur<br />

61<br />

62<br />

63 if __name__ == "__main__" :<br />

64 s = sc<strong>en</strong>e_phong (base.repere (), math.pi / 1.5, 400, 300)<br />

65<br />

66 s.ajoute_source ( base.source (base.vecteur (0,10,10), \<br />

67 base.couleur (1,1,1) ) )<br />

68 s.ajoute_source ( base.source (base.vecteur (10,10,5), \<br />

69 base.couleur (0.5,0.5,0.5) ) )<br />

70 s.ajoute_objet ( obj.sphere (base.vecteur (0,0,12), \<br />

71 3, base.couleur (1,0,0) ) )<br />

72 s.ajoute_objet ( obj.sphere (base.vecteur (0,-400,12), \<br />

73 396, base.couleur (0.5,0.5,0.5) ) )<br />

74 print s<br />

75<br />

76 scre<strong>en</strong> = pygame.display.set_mo<strong>de</strong> (s.dim)<br />

77 scre<strong>en</strong>.fill ((255,255,255))<br />

78 s.construit_image (scre<strong>en</strong>)<br />

79<br />

80 print "image terminée"<br />

81 sc<strong>en</strong>e.att<strong>en</strong>dre_clic ()<br />

82<br />

La gure 10.15 est la même scène que la gure 10.14 mais calculée avec le modèle d'illumination <strong>de</strong> Phong<br />

implém<strong>en</strong>té dans le <strong>de</strong>rnier chier. La sphère rouge paraît plus pâle, la couleur est att<strong>en</strong>uée par le reet.<br />

10.2.5 Fichier : image_synthese_facette.py<br />

Le <strong>de</strong>rnier chier implém<strong>en</strong>te les facettes triangulaires et quadrilatères. Une facette triangulaire dérive<br />

<strong>de</strong> la classe objet dénie dans le module image_synthese_base.py. Elle dénit l'intersection <strong>en</strong>tre un<br />

rayon et une facette. L'objet qui implém<strong>en</strong>te une facette <strong>en</strong> forme <strong>de</strong> quadrilatère dérive à son tour d'une<br />

facette triangulaire. Le résultat <strong>de</strong> ce programme est donné par la gure 10.16.<br />

Lorsque les interfaces <strong>de</strong>s objets sont bi<strong>en</strong> conçues, l'ajout <strong>de</strong> classe d'objets <strong>de</strong>vi<strong>en</strong>t une tâche indép<strong>en</strong>dante<br />

du choix d'un modèle d'illumination. Pour construire un nouvel objet, il n'est pas nécessaire <strong>de</strong> connaître<br />

l'objet sc<strong>en</strong>e ou sc<strong>en</strong>e_phong, il sut <strong>de</strong> surcharger les métho<strong>de</strong>s <strong>de</strong> la classe objet pour adapter son<br />

comportem<strong>en</strong>t à celui du nouvel objet.<br />

120


Fig. 10.15 Cette scène est la même que celle <strong>de</strong> la gure 10.14 mais calculée avec le modèle d'illumination<br />

<strong>de</strong> Phong.<br />

1 # -*- coding: cp1252 -*-<br />

2 """définition d'une sphère"""<br />

3 import image_synthese_base as base<br />

4 import image_synthese_sphere as obj<br />

5 import image_synthese_phong as sc<strong>en</strong>e<br />

6 import image_synthese_sc<strong>en</strong>e as sc<strong>en</strong>e_p<br />

7 import pygame<br />

8 import math<br />

9<br />

10 class facette (base.objet):<br />

11 """définit un triangle dans l'espace"""<br />

12<br />

13 <strong>de</strong>f __init__ (self, a,b,c, couleur):<br />

14 """initialisation"""<br />

15 self.a, self.b, self.c = a,b,c<br />

16 ab = b - a<br />

17 ac = c - a<br />

18 self.vnorm = ab.vectoriel (ac)<br />

19 self.vnorm = self.vnorm.r<strong>en</strong>orme ()<br />

20 self.couleur = couleur<br />

21<br />

22 <strong>de</strong>f intersection_plan (self, r) :<br />

23 """retourne le point d'intersection <strong>en</strong>tre le plan et le rayon r"""<br />

24 if r.direction.scalaire (self.vnorm) == 0 :<br />

25 return None<br />

26 oa = self.a - r.origine<br />

27 l = self.vnorm.scalaire (oa) / self.vnorm.scalaire (r.direction)<br />

28 p = r.origine + r.direction * l<br />

29 return p<br />

30<br />

31 <strong>de</strong>f point_interieur (self, p) :<br />

32 """dit si un point apparti<strong>en</strong>t à l'intérieur du triangle"""<br />

121


33 pa = self.a - p<br />

34 pb = self.b - p<br />

35 pc = self.c - p<br />

36 theta = pa.angle (pb, self.vnorm)<br />

37 theta += pb.angle (pc, self.vnorm)<br />

38 theta += pc.angle (pa, self.vnorm)<br />

39 theta = abs (theta)<br />

40 if theta >= math.pi * 0.9 : return True<br />

41 else : return False<br />

42<br />

43 <strong>de</strong>f intersection (self, r) :<br />

44 """retourne le point d'intersection avec le rayon r,<br />

45 retourne None s'il n'y pas d'intersection"""<br />

46 p = self.intersection_plan (r)<br />

47 if p == None : return None<br />

48 if self.point_interieur (p) : return p<br />

49 else : return None<br />

50<br />

51 <strong>de</strong>f normale (self, p, rayon) :<br />

52 """retourne la normale au point <strong>de</strong> coordonnée p et connaissant le rayon"""<br />

53 if rayon.direction.scalaire (self.vnorm) < 0 :<br />

54 return self.vnorm<br />

55 else :<br />

56 return - self.vnorm<br />

57<br />

58 <strong>de</strong>f couleur_point (self, p) :<br />

59 """retourne la couleur au point <strong>de</strong> coordonnée p"""<br />

60 return self.couleur<br />

61<br />

62 <strong>de</strong>f __str__ (self):<br />

63 """affichage"""<br />

64 s = "facette --- a : " + str (self.a)<br />

65 s += " b : " + str (self.b)<br />

66 s += " c : " + str (self.c)<br />

67 s += " couleur : " + str (self.couleur)<br />

68 return s<br />

69<br />

70<br />

71 class rectangle (facette):<br />

72 """définit un rectangle dans l'espace"""<br />

73<br />

74 <strong>de</strong>f __init__ (self, a,b,c,d, couleur):<br />

75 """initialisation, si d == None, d est calculé comme étant<br />

76 le symétrique <strong>de</strong> b par rapport au milieu du segm<strong>en</strong>t [ac]"""<br />

77 facette.__init__(self, a,b,c, couleur)<br />

78 if d != None : self.d = d<br />

79 else :<br />

80 i = (a + c) / 2<br />

81 self.d = b + (i-b) * 2<br />

82<br />

122


83 <strong>de</strong>f point_interieur (self, p) :<br />

84 """dit si un point apparti<strong>en</strong>t à l'intérieur du triangle"""<br />

85 pa = self.a - p<br />

86 pb = self.b - p<br />

87 pc = self.c - p<br />

88 pd = self.d - p<br />

89 theta = pa.angle (pb, self.vnorm)<br />

90 theta += pb.angle (pc, self.vnorm)<br />

91 theta += pc.angle (pd, self.vnorm)<br />

92 theta += pd.angle (pa, self.vnorm)<br />

93 theta = abs (theta)<br />

94 if theta >= math.pi * 0.9 : return True<br />

95 else : return False<br />

96<br />

97 <strong>de</strong>f __str__ (self):<br />

98 """affichage"""<br />

99 s = "rectangle --- a : " + str (self.a)<br />

100 s += " b : " + str (self.b)<br />

101 s += " c : " + str (self.c)<br />

102 s += " d : " + str (self.d)<br />

103 s += " couleur : " + str (self.couleur)<br />

104 return s<br />

105<br />

106<br />

107<br />

108<br />

109<br />

110 if __name__ == "__main__" :<br />

111<br />

112 s = sc<strong>en</strong>e.sc<strong>en</strong>e_phong (base.repere (), math.pi / 1.5, 400, 300)<br />

113<br />

114 s.ajoute_source ( base.source (base.vecteur (0,8,8), \<br />

115 base.couleur (0.6,0.6,0.6) ) )<br />

116 s.ajoute_source ( base.source (base.vecteur (10,0,0), \<br />

117 base.couleur (0.6,0.6,0.6) ) )<br />

118 s.ajoute_source ( base.source (base.vecteur (8,8,4.5), \<br />

119 base.couleur (0.6,0.6,0.6) ) )<br />

120 s.ajoute_objet ( obj.sphere (base.vecteur (1,0,5), \<br />

121 1, base.couleur (1,0,0) ) )<br />

122 s.ajoute_objet ( obj.sphere (base.vecteur (0,-400,12), \<br />

123 396, base.couleur (0.5,0.5,0.5) ) )<br />

124 s.ajoute_objet (facette ( base.vecteur (0,-2.5,6), \<br />

125 base.vecteur (-2,-2.5,3), \<br />

126 base.vecteur (1,-3.5,4.5), \<br />

127 base.couleur (0.2,0.8,0)))<br />

128 s.ajoute_objet (rectangle ( base.vecteur (0,-2.5,6), \<br />

129 base.vecteur (-2,-2.5,3), \<br />

130 base.vecteur (-2,2.8,3.5), \<br />

131 None, \<br />

132 base.couleur (0,0,1)))<br />

123


133 print s<br />

134<br />

135 scre<strong>en</strong> = pygame.display.set_mo<strong>de</strong> (s.dim)<br />

136 scre<strong>en</strong>.fill ((255,255,255))<br />

137 s.construit_image (scre<strong>en</strong>)<br />

138<br />

139 print "image terminée"<br />

140 sc<strong>en</strong>e_p.att<strong>en</strong>dre_clic ()<br />

141<br />

142<br />

143<br />

Fig. 10.16 Cette scène est la même que celle <strong>de</strong> la gure 10.15, une facette triangulaire et une facette<br />

<strong>en</strong> forme <strong>de</strong> quadrilatère ont été ajoutées.<br />

n correction exemple E10<br />

⊓⊔<br />

124


11 Cor<strong>de</strong> susp<strong>en</strong>due<br />

11.1 Enoncé E11<br />

11.1.1 Equation théorique <strong>de</strong> la cor<strong>de</strong> <strong>en</strong> équilibre<br />

On cherche dans cette partie à calculer l'équation <strong>de</strong> la courbe y = f(x) correspondant à une cor<strong>de</strong><br />

susp<strong>en</strong>due comme celle <strong>de</strong> la gure 11.1. On suppose que la cor<strong>de</strong> a pour masse m et pour longueur L.<br />

Elle est attachée aux points <strong>de</strong> coordonnées (x 1 , y 1 ) et (x 2 , y 2 ). La cor<strong>de</strong> est modélisée par une innité<br />

<strong>de</strong> billes <strong>de</strong> masse dm = m L<br />

dl reliées par <strong>de</strong>s segm<strong>en</strong>ts <strong>de</strong> longueur dl. Trois forces s'exerc<strong>en</strong>t sur chaque<br />

masse, le poids et <strong>de</strong>ux t<strong>en</strong>sions dues aux voisins.<br />

Fig. 11.1 Dessin d'une cor<strong>de</strong> susp<strong>en</strong>due <strong>en</strong>tre ses <strong>de</strong>ux extrémités, cette cor<strong>de</strong> est modélisée par un<br />

<strong>en</strong>semble <strong>de</strong> masses ponctuelles reliées par <strong>de</strong>s morceaux <strong>de</strong> cor<strong>de</strong> sans masse. Trois forces s'exerc<strong>en</strong>t sur<br />

chaque masse, son poids et les <strong>de</strong>ux t<strong>en</strong>sions dues à ses <strong>de</strong>ux voisins.<br />

A l'équilibre, la somme <strong>de</strong>s forces qui s'exerc<strong>en</strong>t sur une masse est nulle. Le poids est égale à gdm où g<br />

est la constante <strong>de</strong> gravitation. Les <strong>de</strong>ux t<strong>en</strong>sions sont colinéaires aux segm<strong>en</strong>ts <strong>de</strong> cor<strong>de</strong>s reliés <strong>de</strong> part et<br />

d'autre <strong>de</strong> la masse ponctuelle dm. La t<strong>en</strong>sion a pour amplitu<strong>de</strong> T et sa projection sur l'axe <strong>de</strong>s abscisses<br />

est constante tout le long <strong>de</strong> la cor<strong>de</strong>.<br />

(<br />

0<br />

−gdm<br />

)<br />

+ T (<br />

dx<br />

−dx<br />

f(x − dx) − f(x)<br />

)<br />

+ T (<br />

dx<br />

dx<br />

f(x + dx) − f(x)<br />

)<br />

= 0 (11.1)<br />

On peut approcher f(x + dx) − f(x) par f ′ (x + dx)dx. De même, f(x − dx) − f(x) = −f ′ (x)dx. Cette<br />

remarque permet <strong>de</strong> transformer la relation (11.1) pour obt<strong>en</strong>ir la relation suivante :<br />

− gm L dl + T dx<br />

(<br />

f ′ (x + dx)dx − f ′ (x)dx ) = 0 (11.2)<br />

Comme dl = √ (dx) 2 + (dy) 2 = dx √ 1 + f ′2 (x), on obti<strong>en</strong>t :<br />

− gm L<br />

√<br />

1 + f ′2 (x)dx + T f ′′ (x)dx = 0 (11.3)<br />

On <strong>en</strong> déduit que :<br />

f ′′ (x) = gm √<br />

1 + f<br />

T L<br />

′2 (x) (11.4)<br />

125


On pose α = gm<br />

T L , f ′ est solution <strong>de</strong> l'équation diér<strong>en</strong>tielle g ′ = α √ 1 + g 2 . g est égalem<strong>en</strong>t solution <strong>de</strong><br />

l'équation g ′2 = α 2 ( 1 + g 2) . En dérivant, on obti<strong>en</strong>t :<br />

g ′′ = α 2 g =⇒ g(x) = Ae αx + Be −αx (11.5)<br />

Il reste à vérier à quelle condition sur les constantes A et B, la fonction g est solution <strong>de</strong> l'équation<br />

g ′2 = α 2 ( 1 + g 2) . Ceci donne :<br />

Par conséqu<strong>en</strong>t :<br />

α 2 [ A 2 e 2αx + B 2 e −2αx − 2AB ] = α 2 [ 1 + A 2 e 2αx + B 2 e −2αx + 2AB ] (11.6)<br />

On <strong>en</strong> déduit que g(x) = Ae αx − 1<br />

4A e−αx et :<br />

f(x) = 1 α<br />

4AB + 1 = 0 (11.7)<br />

[<br />

Ae αx + 1<br />

4A e−αx ]<br />

+ C (11.8)<br />

On peut transformer cette expression, comme A > 0, on pose x 0 = ln 2A ⇐⇒ 2A = e x 0<br />

. On note égalem<strong>en</strong>t<br />

x −→ cosh(x) la fonction cosinus hyperbolique.<br />

f(x) = 1 [<br />

2Ae αx + 1 ]<br />

2α 2A e−αx + C<br />

f(x) = 1 [<br />

e<br />

αx+x 0<br />

+ e −αx−x ] 0<br />

+ C<br />

2α<br />

f(x) = 1 α cosh (αx + x 0) + C (11.9)<br />

Il reste à déterminer les constantes x 0 , C et T à l'ai<strong>de</strong> <strong>de</strong>s conditions initiales :<br />

⎧<br />

⎨<br />

⎩<br />

f(x 1 ) = y 1<br />

f(x 2 ) = y 2<br />

∫ x2<br />

x 1<br />

√<br />

1 + f ′2 (x)dx = L<br />

(11.10)<br />

Cette <strong>de</strong>rnière condition signie que la longueur <strong>de</strong> la cor<strong>de</strong> est L. Comme f ′ vérie l'équation (11.4), on<br />

peut simplier cette <strong>de</strong>rnière contrainte par ∫ x 2<br />

x 1<br />

f ′′ (x)dx = αL ⇐⇒ f ′ (x 2 ) − f ′ (x 1 ) = αL. Le système<br />

(11.10) <strong>de</strong>vi<strong>en</strong>t :<br />

⎧<br />

⎨<br />

⎩<br />

f(x 1 ) = y 1<br />

f(x 2 ) = y 2<br />

f ′ (x 2 ) − f ′ (x 1 ) = αL<br />

(11.11)<br />

126


11.1.2 Solution dans un cas particulier<br />

On résoud le système (11.11) dans un cas particulier pour lequel x 1 = −x 2 , x 2 > 0 et y 1 = y 2 = 0. La<br />

fonction f est donc paire et ∀x, f(−x) = f(x). On <strong>en</strong> déduit que x 0 = 0.<br />

Par conséqu<strong>en</strong>t, f(x) = 1 α cosh(αx) + C. Comme f(x 1) = f(x 2 ) = 0, la constance C vaut C =<br />

− 1 α cosh(αx 1). Il reste à déterminer la valeur du paramètre α qui <strong>en</strong>globe la constante T . Comme<br />

f ′ (x) = sinh(αx), la <strong>de</strong>rnière condition du système (11.11) équivaut à :<br />

sinh(αx 2 ) − sinh(αx 1 ) = αL<br />

⇐⇒ 2 sinh(αx 2 ) = αL<br />

⇐⇒ 1 α sinh(αx 2) = L 2<br />

(11.12)<br />

Cette <strong>de</strong>rnière équation n'est pas évi<strong>de</strong>nte à résoudre. On peut toutefois remarquer que la fonction h (α) =<br />

1<br />

α sinh(αx 2) est croissante 6 . Il est possible <strong>de</strong> déterminer numériquem<strong>en</strong>t une valeur approchée pour α <strong>en</strong><br />

utilisant par exemple un algorithme <strong>de</strong> recherche par dichotomie.<br />

11.1.3 Simulation informatique<br />

On cherche ici à retrouver ce résultat théorique à l'ai<strong>de</strong> d'une simulation informatique. La cor<strong>de</strong> est<br />

représ<strong>en</strong>tée par une suite <strong>de</strong> n masses ponctuelles reliées par <strong>de</strong>s ressorts <strong>de</strong> masse nulle qui exerc<strong>en</strong>t une<br />

traction lorsque leur longueur dépasse un certain seuil. On considère une cor<strong>de</strong> <strong>de</strong> longueur L et <strong>de</strong> masse<br />

M, elle est divisée <strong>en</strong> n masses <strong>de</strong> masses M n<br />

. Ces masses sont reliés par n − 1 ressorts qui exerc<strong>en</strong>t une<br />

L<br />

traction sur les masses qu'ils reli<strong>en</strong>t lorsque leur longueur dépasse le seuil<br />

n−1<br />

. Les masses sont initialem<strong>en</strong>t<br />

placées sur une droite reliant les <strong>de</strong>ux extremités xes <strong>de</strong> la cor<strong>de</strong>.<br />

Chaque masse est soumis à une accélération égale à la somme <strong>de</strong> trois forces qui sont son poids et les<br />

tractions exercées par les ressorts auxquels elle est attachée. A chaque instant t, on peut calculer cette<br />

accélération, <strong>en</strong> déduire la vitesse puis la position à l'instant t + dt. Ce mécanisme permet d'obt<strong>en</strong>ir<br />

une animation <strong>de</strong> la cor<strong>de</strong> atteignant sa position d'équilibre (voir gure 11.2). On construit l'algorithme<br />

suivant.<br />

6 On vérie que la dérivée secon<strong>de</strong> est positive sur R + et que la dérivée est nulle à l'origine, donc croissante et positive.<br />

127


a<br />

b<br />

c<br />

d<br />

Fig. 11.2 Dessin d'une cor<strong>de</strong> susp<strong>en</strong>due <strong>en</strong>tre ses <strong>de</strong>ux extrémités, cette cor<strong>de</strong> est modélisée par un<br />

<strong>en</strong>semble <strong>de</strong> masses ponctuelles reliées par <strong>de</strong>s morceaux <strong>de</strong> cor<strong>de</strong> élastiques sans masse. Ces quatre images<br />

a, b, c, d représ<strong>en</strong>t<strong>en</strong>t la cor<strong>de</strong> lorsqu'on la laisse tomber. La courbe bleue est la courbe théorique <strong>de</strong> la<br />

cor<strong>de</strong> calculée au paragraphe 11.1.2. Ces images ont été obt<strong>en</strong>ues avec l'algorithme 11.1.<br />

Algorithme 11.1 : mouvem<strong>en</strong>t d'une cor<strong>de</strong><br />

Soit une cor<strong>de</strong> <strong>de</strong> longueur L susp<strong>en</strong>due <strong>en</strong>tre les points d'abscisse (−x 0 , 0) et (x 0 , 0) <strong>de</strong> telle<br />

sorte que 2x 0 < L. Cette cor<strong>de</strong> à une masse M et une rai<strong>de</strong>ur k. La pesanteur est notée g. On<br />

divise cette cor<strong>de</strong> <strong>en</strong> n masses ponctuelles <strong>de</strong> masse ∀i ∈ {1, . . . , n} , m i = M n<br />

. Chaque masse<br />

a une position p i . On note pour chaque point p i sa vitesse v i . On désigne par f un coeci<strong>en</strong>t<br />

<strong>de</strong> <strong>fr</strong>einage, plus cette vitesse est gran<strong>de</strong>, plus la cor<strong>de</strong> convergera rapi<strong>de</strong>m<strong>en</strong>t vers sa position<br />

d'équilibre. dt désigne un pas <strong>de</strong> temps.<br />

Etape A : initialisation<br />

∀i ∈ {1, . . . , n} , p i = −x 0 + 2x 0<br />

i−1<br />

n−1<br />

Etape B : calcul <strong>de</strong>s forces<br />

Pour chaque masse, on calcule une accélération a i ∈ R 2 . a 0 = a n = 0 (les<br />

extrémités sont xes).<br />

pour i = 1 à n − 1 faire<br />

a i ←− (0, −m i g) − fv i<br />

si ‖ −−−−→ p i−1 , p i ‖ ><br />

L<br />

n−1 [<br />

a i ←− a i + k ‖ −−−−→ p i−1 , p i ‖ −<br />

n si<br />

si ‖ −−−−→ p i+1 , p i ‖ ><br />

L<br />

n−1<br />

[<br />

a i ←− a i + k ‖ −−−−→ p i+1 , p i ‖ −<br />

n si<br />

n pour<br />

Etape C : mise à jour <strong>de</strong>s positions<br />

pour i = 1 à n − 1 faire<br />

p i ←− p i + v i dt<br />

v i ←− v i + a i dt<br />

n pour<br />

] −−−−→<br />

L<br />

n−1<br />

p i p i−1<br />

‖ −−−−→ p i−1 ,p i‖<br />

] −−−−→<br />

L<br />

n−1<br />

p i p i+1<br />

‖ −−−−→ p i+1 ,p i‖<br />

128


Etape D : terminaison<br />

Tant que l'algorithme n'a pas convergé, on retourne à l'étape B.<br />

Cet algorithme reproduit la chute libre <strong>de</strong> masses ponctuelles reliées par <strong>de</strong>s ressorts. Ce type <strong>de</strong> modèle est<br />

parfois utilisé pour reproduire le comportem<strong>en</strong>t d'un vêtem<strong>en</strong>t autour d'un corps <strong>en</strong> mouvem<strong>en</strong>t. Chaque<br />

masse n'est plus reliée à <strong>de</strong>ux voisins mais à quatre voisins.<br />

11.2 Programme, explications <strong>de</strong> l'exemple E11<br />

Le programme qui suit implém<strong>en</strong>te l'algorithme 11.1. La classe point dénit ce qu'est un point dans<br />

plan. La classe cor<strong>de</strong> dénit ce qu'est une cor<strong>de</strong>. Le constructeur reproduit l'étape A d'initialisation. La<br />

métho<strong>de</strong> force_point calcule les forces qui s'appliqu<strong>en</strong>t à chaque masse ponctuelle, c'est-à-dire l'étape B.<br />

La métho<strong>de</strong> iteration met à jour les positions <strong>de</strong> chaque masse selon les formules décrites lors <strong>de</strong> l'étape C.<br />

La métho<strong>de</strong> display ache la cor<strong>de</strong>, elle appelle égalem<strong>en</strong>t la métho<strong>de</strong> display_courbe_theorique qui<br />

<strong>de</strong>ssine la courbe théorique <strong>de</strong> la cor<strong>de</strong> (voir paragraphe 11.1.2) lorsque les extrémités ont même ordonnée.<br />

La <strong>de</strong>rnière métho<strong>de</strong> __str__ permet d'écrire <strong>en</strong>tre autres les positions <strong>de</strong> chaque masse, la distance<br />

<strong>en</strong>tre chaque masse, la distance att<strong>en</strong>due.<br />

La métho<strong>de</strong> display_courbe_theorique nécessite la détermination du paramètre α qui véri<strong>en</strong>t l'équation<br />

1 α sinh(αx 2) − L 2<br />

= 0 (voir paragraphe 11.1.2). Cette détermination est faite grâce à l'algorithme qui<br />

suit. Il s'appuie sur le faite que la fonction α ←− 1 α sinh(αx 2) − L 2 est croissante pour x 2 > 0.<br />

Algorithme 11.2 : recherche dichotomique<br />

Soit f une fonction croisante, soi<strong>en</strong>t a, b ∈ R tels que a < b et f(a) 0 f(b). On cherche à<br />

approcher x ∈ [a, b] tel que f(x) = 0. Soit s ∈ R ∗ + une petite valeur. x ←− a+b<br />

2<br />

tant que (|f(x)| > s) faire<br />

si f(x) > 0<br />

b ←− x<br />

sinon<br />

a ←− x<br />

n si<br />

x ←− a+b<br />

2<br />

n tant que<br />

Cet algorithme divise l'intervalle <strong>de</strong> recherche <strong>en</strong> <strong>de</strong>ux à chaque itération.<br />

1 # -*- coding: cp1252 -*-<br />

2 import pygame # pour les affichages<br />

3 import math # pour les racines carrées<br />

4<br />

5<br />

6 class point (object) :<br />

7 """définition d'un point : <strong>de</strong>ux coordonnées et une masse"""<br />

8 __slots__ = "x","y","m"<br />

129


9<br />

10 <strong>de</strong>f __init__ (self, x,y,m) :<br />

11 """définit un point <strong>de</strong> la cor<strong>de</strong>, <strong>de</strong> coordonnées (x,y)<br />

12 et <strong>de</strong> masse m"""<br />

13 self.x, self.y, self.m = float (x), float (y), float (m)<br />

14<br />

15 <strong>de</strong>f <strong>de</strong>place (self, <strong>de</strong>p, dt) :<br />

16 """déplace un point"""<br />

17 self.x += <strong>de</strong>p [0] * dt<br />

18 self.y += <strong>de</strong>p [1] * dt<br />

19<br />

20 <strong>de</strong>f <strong>de</strong>place_point (self, <strong>de</strong>p, dt) :<br />

21 """déplace un point"""<br />

22 self.x += <strong>de</strong>p.x * dt<br />

23 self.y += <strong>de</strong>p.y * dt<br />

24<br />

25 <strong>de</strong>f differ<strong>en</strong>ce (self, p) :<br />

26 """retourne le vecteur qui relie <strong>de</strong>ux points,<br />

27 retourne le couple <strong>de</strong> ses coordonnées"""<br />

28 return p.x - self.x, p.y - self.y<br />

29<br />

30 <strong>de</strong>f __str__ (self) :<br />

31 """afficher le point"""<br />

32 return "(x,y) = (%4.1f,%4.1f) masse %f" % (self.x,self.y,self.m)<br />

33<br />

34 class cor<strong>de</strong> (object) :<br />

35 """définition d'une cor<strong>de</strong>, une liste <strong>de</strong> points"""<br />

36<br />

37 <strong>de</strong>f __init__(self, nb, p1, p2, m, k, g, f, l) :<br />

38 """initialisation d'une cor<strong>de</strong><br />

39 @param nb nombre <strong>de</strong> points<br />

40 @param p1 = x1,y1 coordoonnées du premier point (fixe)<br />

41 @param p2 = x2,y2 coordoonnées du <strong>de</strong>rnier point (fixe)<br />

42 @param m masse <strong>de</strong> la cor<strong>de</strong>,<br />

43 répartie <strong>en</strong>tre tous les pointstous les points<br />

44 @param k rai<strong>de</strong>ur <strong>de</strong> l'élastique<br />

45 @param g int<strong>en</strong>sité <strong>de</strong> l'apesanteur,<br />

46 valeur positive<br />

47 @param f vitesse <strong>de</strong> <strong>fr</strong>einage<br />

48 @param l longueur <strong>de</strong> la cor<strong>de</strong>"""<br />

49 x1,y1 = p1[0], p1[1]<br />

50 x2,y2 = p2[0], p2[1]<br />

51 self.list = []<br />

52 self.vitesse = []<br />

53 for i in range (0,nb) :<br />

54 x = x1 + float (i) * (x2 - x1) / float (nb - 1)<br />

55 y = y1 + float (i) * (y2 - y1) / float (nb - 1)<br />

56 self.list.app<strong>en</strong>d ( point (x,y, float (m) / nb))<br />

57 self.vitesse.app<strong>en</strong>d (point (0,0,0))<br />

58 self.k = k * nb<br />

130


59 self.g = g<br />

60 self.l = float (l) / (nb-1)<br />

61 self.f = f<br />

62<br />

63 <strong>de</strong>f display (self, scre<strong>en</strong>) :<br />

64 """affichage <strong>de</strong> la cor<strong>de</strong> à l'ai<strong>de</strong> du module pyagame"""<br />

65 self.display_courbe_theorique (scre<strong>en</strong>)<br />

66 x,y = scre<strong>en</strong>.get_size ()<br />

67 color = (0,0,0)<br />

68 for p in self.list :<br />

69 pygame.draw.circle (scre<strong>en</strong>, color, (int (p.x), int (y - p.y)), int (p.m+1))<br />

70 for i in xrange (0,l<strong>en</strong> (self.list)-1) :<br />

71 pygame.draw.line (scre<strong>en</strong>, color, \<br />

72 (int (self.list [i].x), int (y - self.list [i].y)), \<br />

73 (int (self.list [i+1].x), int (y - self.list [i+1].y)))<br />

74<br />

75 <strong>de</strong>f display_courbe_theorique (self, scre<strong>en</strong>) :<br />

76<br />

77 <strong>de</strong>f cosh (x) :<br />

78 """retourne le cosinus hyperbolique <strong>de</strong> x"""<br />

79 return math.cosh (x)<br />

80<br />

81 <strong>de</strong>f sinh (x) :<br />

82 """retourne le sinus hyperbolique <strong>de</strong> x"""<br />

83 return math.sinh (x)<br />

84<br />

85 <strong>de</strong>f asinh (x) :<br />

86 """retourne la réciproque du sinus hyperbolique <strong>de</strong> x"""<br />

87 t = x + math.sqrt (x*x + 1)<br />

88 return math.log (t)<br />

89<br />

90 <strong>de</strong>f acosh (x) :<br />

91 """retourne la réciproque du cosinus hyperbolique <strong>de</strong> x"""<br />

92 t = abs (x) + math.sqrt (x*x - 1)<br />

93 return math.log (t)<br />

94<br />

95 <strong>de</strong>f trouve_alpha (x2, L) :<br />

96 """détermine la valeur <strong>de</strong> alpha une seule fois"""<br />

97 if self.__dict__.has_key ("alpha") : return self.alpha<br />

98<br />

99 <strong>de</strong>f fonction (a,x2,L) :<br />

100 if a == 0.0 : return 0.0<br />

101 else : return 1.0 / a * sinh (a * x2) - L / 2<br />

102<br />

103 a1 = 1.0 / x2<br />

104 a2 = 1.0 / x2<br />

105 m = 1.0 / x2<br />

106 diff = fonction (m, x2, L)<br />

107 while abs (diff) > 1e-3 :<br />

108 if diff > 0 : # faire décroître alpha<br />

131


109 if fonction (a1, x2, L) > 0 : a1 /= 2.0<br />

110 else : a2 = m<br />

111 else :<br />

112 if fonction (a2, x2, L) < 0 : a2 *= 2.0<br />

113 else : a1 = m<br />

114 m = (a1 + a2) / 2<br />

115 diff = fonction (m, x2, L)<br />

116 self.alpha = m<br />

117 return m<br />

118<br />

119 p1 = self.list [0]<br />

120 p2 = self.list [ l<strong>en</strong> (self.list) - 1 ]<br />

121 if p1.y != p2.y : return None # cas non prévu<br />

122 # on trace la courbe y = cosh (alpha x)<br />

123 L = self.l * (l<strong>en</strong> (self.list) - 1)<br />

124 mx = (p1.x + p2.x) / 2<br />

125 x1 = p1.x - mx<br />

126 x2 = p2.x - mx<br />

127 alpha = trouve_alpha (x2, L) # est solution <strong>de</strong> 1/alpha sinh (alpha x1) = L/2<br />

128 C = - 1.0 / alpha * cosh (alpha * x2)<br />

129<br />

130 sx,sy = scre<strong>en</strong>.get_size ()<br />

131 color = (0,0,255)<br />

132<br />

133 ix1 = int (x1)<br />

134 ix2 = int (x2)<br />

135 x0,y0 = p1.x,p1.y<br />

136 for i in xrange (ix1, ix2+1) :<br />

137 x = int (mx + i)<br />

138 y = 1.0 / alpha * cosh (alpha * float (i)) + C + p1.y<br />

139 if 0 < y < sy and 0 < y0 < sy :<br />

140 pygame.draw.line (scre<strong>en</strong>, color, (x0,sy-y0), (x, sy-y), 2)<br />

141 x0,y0 = x,y<br />

142<br />

143<br />

144 <strong>de</strong>f force_point (self, i) :<br />

145 """calcule les forces qui s'exerce <strong>en</strong> un point,<br />

146 retourne un couple x,y"""<br />

147 x,y = 0,0<br />

148 # poids<br />

149 y -= self.g * self.list [i].m<br />

150 # voisin <strong>de</strong> gauche<br />

151 dx, dy = self.list [i].differ<strong>en</strong>ce (self.list [i-1])<br />

152 d = math.sqrt (dx *dx + dy *dy)<br />

153 if d > self.l :<br />

154 dx = (d - self.l) / d * dx<br />

155 dy = (d - self.l) / d * dy<br />

156 x += self.k * dx<br />

157 y += self.k * dy<br />

158 # voisin <strong>de</strong> droite<br />

132


159 dx, dy = self.list [i].differ<strong>en</strong>ce (self.list [i+1])<br />

160 d = math.sqrt (dx *dx + dy *dy)<br />

161 if d > self.l :<br />

162 dx = (d - self.l) / d * dx<br />

163 dy = (d - self.l) / d * dy<br />

164 x += self.k * dx<br />

165 y += self.k * dy<br />

166 # <strong>fr</strong>einage<br />

167 x += - self.f * self.vitesse [i].x<br />

168 y += - self.f * self.vitesse [i].y<br />

169<br />

170 return x,y<br />

171<br />

172 <strong>de</strong>f iteration (self, dt) :<br />

173 """calcule les déplacem<strong>en</strong>ts <strong>de</strong> chaque point et les met à jour,<br />

174 on ne déplace pas les points situés aux extrémités,<br />

175 retourne la somme <strong>de</strong>s vitesses et <strong>de</strong>s accélérations au carré"""<br />

176 force = [ (0,0) ]<br />

177 for i in xrange (1, l<strong>en</strong> (self.list)-1) :<br />

178 x,y = self.force_point (i)<br />

179 force.app<strong>en</strong>d ((x,y))<br />

180 force.app<strong>en</strong>d ((0,0))<br />

181<br />

182 # déplacem<strong>en</strong>t<br />

183 for i in xrange (1, l<strong>en</strong> (self.list)-1) :<br />

184 self.vitesse [i].<strong>de</strong>place ( force [i], dt )<br />

185 self.list [i].<strong>de</strong>place_point ( self.vitesse [i], dt )<br />

186<br />

187 d = 0<br />

188 for f in force :<br />

189 d += self.vitesse [i].x ** 2 + force [i][0] **2<br />

190 d += self.vitesse [i].y ** 2 + force [i][1] **2<br />

191<br />

192 return d<br />

193<br />

194 <strong>de</strong>f __str__ (self):<br />

195 """affiche chaque point <strong>de</strong> la cor<strong>de</strong>"""<br />

196 s = ""<br />

197 l = 0<br />

198 for i in xrange (0, l<strong>en</strong> (self.list)) :<br />

199 s += "point " + str (i) + " : " + str (self.list [i])<br />

200 if i < l<strong>en</strong> (self.list) -1 :<br />

201 x,y = self.list [i].differ<strong>en</strong>ce (self.list [i+1])<br />

202 d = math.sqrt (x*x + y*y)<br />

203 s += "\t segm<strong>en</strong>t : %4.0f" % d<br />

204 s += " ( %4.0f )" % self.l<br />

205 if i != 0 and i != l<strong>en</strong> (self.list)-1 :<br />

206 x,y = self.force_point (i)<br />

207 s += "\t force <strong>en</strong> ce point (%f,%f) " % (x,y)<br />

208 s += "\n"<br />

133


209 if i > 0 :<br />

210 x,y = self.list [i].differ<strong>en</strong>ce (self.list [i-1])<br />

211 l += math.sqrt (x*x + y*y)<br />

212 s += "longueur <strong>de</strong> la cor<strong>de</strong> " + str (l) + "\n"<br />

213 s += "longueur att<strong>en</strong>due " + str ((l<strong>en</strong> (self.list)-1) * self.l) + "\n"<br />

214 return s<br />

215<br />

216 <strong>de</strong>f att<strong>en</strong>dre_clic (scre<strong>en</strong>,x,y):<br />

217 """<strong>de</strong>ssine un rectangle rouge sur l'écran et<br />

218 att<strong>en</strong>d la pression d'un clic <strong>de</strong> souris"""<br />

219 color = 255,0,0<br />

220 pygame.draw.line (scre<strong>en</strong>, color, (10,10), (x-10,10), 2)<br />

221 pygame.draw.line (scre<strong>en</strong>, color, (x-10,10), (x-10,y-10), 2)<br />

222 pygame.draw.line (scre<strong>en</strong>, color, (x-10,y-10), (10,y-10), 2)<br />

223 pygame.draw.line (scre<strong>en</strong>, color, (10,y-10), (10,10), 2)<br />

224 pygame.display.flip ()<br />

225 reste = True<br />

226 while reste:<br />

227 for ev<strong>en</strong>t in pygame.ev<strong>en</strong>t.get():<br />

228 if ev<strong>en</strong>t.type == pygame.MOUSEBUTTONUP :<br />

229 reste = False<br />

230 break<br />

231<br />

232 if __name__ == "__main__" :<br />

233<br />

234 pygame.init ()<br />

235 size = width, height = x,y = 800, 500<br />

236 black = 0, 0, 0<br />

237 white = 255,255,255<br />

238 scre<strong>en</strong> = pygame.display.set_mo<strong>de</strong>(size)<br />

239 nb = 10<br />

240 c = cor<strong>de</strong> (nb, (100,450), (700,450), 100, 1, 0.1, 0.05, 800)<br />

241 dt = 0.1<br />

242 print "cor<strong>de</strong> initial"<br />

243 print c<br />

244<br />

245 iter = 0<br />

246 <strong>de</strong>p = l<strong>en</strong> (c.list) * (x*x + y*y)<br />

247 while True and <strong>de</strong>p > 1e-6 :<br />

248<br />

249 for ev<strong>en</strong>t in pygame.ev<strong>en</strong>t.get():<br />

250 if ev<strong>en</strong>t.type == pygame.QUIT: sys.exit()<br />

251 if ev<strong>en</strong>t.type == pygame.MOUSEBUTTONUP:<br />

252 print c<br />

253 att<strong>en</strong>dre_clic (scre<strong>en</strong>,x,y)<br />

254<br />

255 if iter % 10 == 0 :<br />

256 scre<strong>en</strong>.fill (white)<br />

257 c.display (scre<strong>en</strong>)<br />

258 pygame.display.flip ()<br />

134


259<br />

260 iter += 1<br />

261<br />

262 if iter == 1: att<strong>en</strong>dre_clic (scre<strong>en</strong>,x,y)<br />

263<br />

264 <strong>de</strong>p = c.iteration (dt)<br />

265 #print "<strong>de</strong>p =", <strong>de</strong>p<br />

266<br />

267 pygame.display.flip ()<br />

268<br />

269 print c<br />

270 att<strong>en</strong>dre_clic (scre<strong>en</strong>,x,y)<br />

n correction exemple E11<br />

⊓⊔<br />

135


12 Classication à l'ai<strong>de</strong> <strong>de</strong>s plus proches voisins<br />

12.1 Enoncé E12<br />

La gure 12.1 représ<strong>en</strong>te un problème <strong>de</strong> classication classique. On dispose d'un nuage <strong>de</strong> points réparti<br />

<strong>en</strong> <strong>de</strong>ux classes. Un nouveau point semblable aux précé<strong>de</strong>nts se prés<strong>en</strong>te, sa classe est inconnue. L'objectif<br />

est <strong>de</strong> lui attribuer une classe <strong>en</strong> utilisant le fait qu'on connaît la classe d'appart<strong>en</strong>ance <strong>de</strong>s autres points.<br />

Fig. 12.1 Problème <strong>de</strong> classication classique. Cette image représ<strong>en</strong>te un nuage <strong>de</strong> points, chacun d'<strong>en</strong>tre<br />

eux apparti<strong>en</strong>t à l'une <strong>de</strong>s <strong>de</strong>ux classes, ici représ<strong>en</strong>tées par un cercle ou une croix. A partir d'un nuage <strong>de</strong><br />

points pour lesquels la classe d'appart<strong>en</strong>ance est connue, comm<strong>en</strong>t classer un nouveau point pour lequel<br />

cette classe est inconnue ?<br />

Une métho<strong>de</strong> simple consiste à attribuer à ce nouveau point la même classe que le plus proche <strong>de</strong>s points<br />

appart<strong>en</strong>ant au nuage initial. C'est la métho<strong>de</strong> <strong>de</strong>s plus proches voisins (ou nearest neighbours). Elle est<br />

facile à implém<strong>en</strong>ter mais peu utilisée car souv<strong>en</strong>t très gourman<strong>de</strong> <strong>en</strong> temps <strong>de</strong> calcul lorsque le nuage<br />

<strong>de</strong> points est conséqu<strong>en</strong>t. Le premier paragraphe décrit cette métho<strong>de</strong>, les suivants cherch<strong>en</strong>t à accélérer<br />

l'algorithme selon que le nuage <strong>de</strong> points apparti<strong>en</strong>t à un espace vectoriel ou non. La <strong>de</strong>rnière partie prés<strong>en</strong>te<br />

l'algorithme LAESA pour le cas où le nuage <strong>de</strong> points apparti<strong>en</strong>t à un espace métrique quelconque.<br />

12.1.1 Principe<br />

Cette métho<strong>de</strong> est la plus simple puisqu'elle consiste à associer à x, l'élém<strong>en</strong>t à classer, le label c (x i ∗)<br />

<strong>de</strong> l'élém<strong>en</strong>t le plus proche x i ∗ dans l'<strong>en</strong>semble (x 1 , . . . , x N ). Ceci mène à l'algorithme <strong>de</strong> classication<br />

suivant :<br />

Algorithme 12.1 : 1-PPV ou plus proche voisin<br />

Soit X = (x 1 , . . . , x N ) ⊂ E un <strong>en</strong>semble d'élém<strong>en</strong>ts d'un espace métrique quelconque, soit<br />

(c (x 1 ) , . . . , c (x N )) les classes associées à chacun <strong>de</strong>s élém<strong>en</strong>ts <strong>de</strong> X. On note d la distance<br />

dénie sur l'espace métrique E. Soit x un élém<strong>en</strong>t à classer, on cherche à déterminer la classe<br />

ĉ(x) associée à x. On dénit x i ∗ comme étant :<br />

x i ∗<br />

= arg min d (x i , x)<br />

i∈{1,··· ,N}<br />

Alors : ĉ(x) = c (x ∗ i )<br />

Cet algorithme est souv<strong>en</strong>t appelé 1-PPV (ou 1-NN pour Nearest Neighbors). Il existe une version amé-<br />

136


liorée k-PPV qui consiste à attribuer à x la classe la plus représ<strong>en</strong>tée parmi ses k plus proches voisins.<br />

Algorithme 12.2 : k-PPV ou k-plus proches voisins<br />

Soit X = (x 1 , . . . , x N ) ⊂ E un <strong>en</strong>semble d'élém<strong>en</strong>ts d'un espace métrique quelconque, soit<br />

(c (x 1 ) , . . . , c (x N )) les classes associées à chacun <strong>de</strong>s élém<strong>en</strong>ts <strong>de</strong> X. On note d la distance dénie<br />

sur l'espace métrique E. ω (x, y) est une fonction strictem<strong>en</strong>t positive mesurant la ressemblance<br />

<strong>en</strong>tre x et y. Soit x un élém<strong>en</strong>t à classer, on cherche à déterminer la classe c(x) associée à x. On<br />

dénit l'<strong>en</strong>semble Sk ∗ incluant les k-plus proches voisins <strong>de</strong> x, cet <strong>en</strong>semble vérie :<br />

card (Sk ∗ ) = 0 et max d (y, x) min d (y, x)<br />

y∈Sk<br />

∗ y∈X−Sk<br />

∗<br />

On calcule les occurr<strong>en</strong>ces f(i) <strong>de</strong> chaque classe i dans l'<strong>en</strong>semble S ∗ k :<br />

f(i) = ∑<br />

ω (x, y) 1 {c(y)=i} (12.1)<br />

y∈S ∗ k<br />

On assigne alors à x la classe c(x) choisie dans l'<strong>en</strong>semble :<br />

ĉ(x) ∈ arg max<br />

i∈N<br />

Dans sa version la plus simple, la fonction ω (x, y) utilisée lors du calcul <strong>de</strong> la contribution f (12.1) est<br />

constante. Mais il est possible <strong>de</strong> lui aecter une valeur t<strong>en</strong>ant compte <strong>de</strong> la proximité <strong>en</strong>tre x et y. La<br />

table 12.1 donne quelques exemples <strong>de</strong> contributions possibles.<br />

f(i)<br />

fonction constante ω (x, y) = 1<br />

distance inverse ω (x, y) =<br />

1<br />

1+d(x,y)<br />

noyau ω (x, y) = exp ( −d 2 (x, y) )<br />

Tab. 12.1 Exemple <strong>de</strong> contribution w (x, y) pour l'algorithme 12.2 <strong>de</strong>s k-PPV. Ces fonctions sont toutes<br />

décroissantes (strictem<strong>en</strong>t ou non) par rapport à la distance d.<br />

L'inconvéni<strong>en</strong>t majeur <strong>de</strong> la métho<strong>de</strong> <strong>de</strong>s plus proches voisins est sa longueur puisqu'elle implique le calcul<br />

<strong>de</strong>s distances <strong>en</strong>tre x et chacun <strong>de</strong>s élém<strong>en</strong>ts <strong>de</strong> l'<strong>en</strong>semble (x 1 , . . . , x N ). C'est pourquoi <strong>de</strong> nombreuses<br />

métho<strong>de</strong>s d'optimisation ont été développées an d'accélérer ce processus. Les <strong>de</strong>ux premiers paragraphes<br />

trait<strong>en</strong>t le cas où les points x i apparti<strong>en</strong>n<strong>en</strong>t à un espace vectoriel et ont donc <strong>de</strong>s coordonnées. Les suivant<br />

trait<strong>en</strong>t le cas où les points x i n'ont pas <strong>de</strong> coordonnées et apparti<strong>en</strong>n<strong>en</strong>t à un espace métrique quelconque.<br />

12.1.2 B+ tree<br />

Ce premier algorithme s'applique dans le cas réel an d'ordonner <strong>de</strong>s nombres dans un arbre <strong>de</strong> sorte que<br />

chaque n÷ud ait un père et pas plus <strong>de</strong> n ls (voir gure 12.2).<br />

137


Fig. 12.2 Illustration d'un B+ tree.<br />

Dénition 12.3 : B+ tree<br />

Soit B n un B+ tree, soit N un n÷ud <strong>de</strong> B n , il conti<strong>en</strong>t un vecteur V (N) = (x 1 , . . . , x t )<br />

avec 0 t n et x 1 < ... < x t . Ce n÷ud conti<strong>en</strong>t aussi exactem<strong>en</strong>t t − 1 n÷uds<br />

ls notés (N 1 , . . . , N t−1 ). On désigne par D (N t ) l'<strong>en</strong>semble <strong>de</strong>s <strong>de</strong>sc<strong>en</strong>dants du n÷ud N t et<br />

G (N t ) = {V (M) | M ∈ D (N t )}. Le n÷ud N vérie :<br />

∀x ∈ G (N t ) , x t x < x t+1<br />

avec par conv<strong>en</strong>tion x 0 = −∞ et x t+1 = +∞<br />

Cet arbre permet <strong>de</strong> trier une liste <strong>de</strong> nombres, c'est une généralisation du tri "quicksort" pour lequel<br />

n = 2. Comme pour le tri quicksort, l'arbre est construit à partir d'une série d'insertions et <strong>de</strong> cet ordre<br />

dép<strong>en</strong>d la rapidité du tri. L'espérance du coût (moy<strong>en</strong>ne sur tous les permutations possibles <strong>de</strong> k élém<strong>en</strong>ts),<br />

le coût <strong>de</strong> l'algorithme est <strong>en</strong> O (k log n k).<br />

12.1.3 R-tree ou Rectangular Tree<br />

L'arbre R-tree est l'adaptation du mécanisme du B+ tree au cas multidim<strong>en</strong>sionnel (voir [Guttman1984]).<br />

La construction <strong>de</strong> cet arbre peut se faire <strong>de</strong> manière globale - construction <strong>de</strong> l'arbre sachant l'<strong>en</strong>semble<br />

<strong>de</strong> points à classer - ou <strong>de</strong> manière progressive - insertion <strong>de</strong>s points dans l'arbre les uns à la suite <strong>de</strong>s<br />

autres -. Toutefois, ces métho<strong>de</strong>s sont resteintes à <strong>de</strong>s espaces vectoriels.<br />

Il n'existe pas une seule manière <strong>de</strong> construire un R-tree, les n÷uds <strong>de</strong> ces arbres suiv<strong>en</strong>t toujours la<br />

contrainte <strong>de</strong>s B+ Tree qui est d'avoir un père et au plus n ls. Les R-Tree ont la même structure que les<br />

B+ Tree ôtée <strong>de</strong> leurs contraintes d'ordonnancem<strong>en</strong>t <strong>de</strong>s ls. De plus, ces arbres organis<strong>en</strong>t spatialem<strong>en</strong>t<br />

<strong>de</strong>s rectangles ou boîtes <strong>en</strong> plusieurs dim<strong>en</strong>sions comme le suggère la gure 12.3. Les boîtes à organiser<br />

seront nommés les objets, ces objets sont <strong>en</strong>suite regroupés dans <strong>de</strong>s boîtes <strong>en</strong>globantes. Un n÷ud n d'un<br />

R-tree est donc soit une feuille, auquel cas la boîte qu'il désigne est un objet, dans ce cas, il n'a aucun<br />

ls, soit le n÷ud désigne une boîte <strong>en</strong>globante B (n). On désigne par B l'<strong>en</strong>semble <strong>de</strong>s boîtes d'un espace<br />

vectoriel quelconque et v (b) désigne son volume. Pour un n÷ud n non feuille, A (n) désigne l'<strong>en</strong>semble <strong>de</strong>s<br />

<strong>de</strong>sc<strong>en</strong>dants <strong>de</strong> ce n÷ud. B (n) est déni par :<br />

B (n) = arg min { v (b) | b ∈ B et ∀n ′ ∈ A ( n ′) , B ( n ′) ⊂ B (n) }<br />

La recherche dans un R-tree consiste à trouver tous les objets ayant une intersection avec une autre boîte<br />

ou f<strong>en</strong>être W , soit l'<strong>en</strong>semble L :<br />

138


Fig. 12.3 Illustration d'un R-tree <strong>en</strong> <strong>de</strong>ux dim<strong>en</strong>sions, gure extraite <strong>de</strong> [Sellis1987], la première image<br />

montre <strong>de</strong>s rectangles pointillés <strong>en</strong>globant d'autres rectangles <strong>en</strong> trait plein. Chaque style <strong>de</strong> trait correspond<br />

à un niveau dans le graphe <strong>de</strong> la secon<strong>de</strong> image.<br />

L = {B (n) | B (n) est un objet et B (n) ∩ W ≠ ∅}<br />

Cet <strong>en</strong>semble est construit grâce à l'algorithme suivant :<br />

Algorithme 12.4 : recherche dans un R-tree<br />

Les notations sont celles utilisées dans ce paragraphe. On désigne par r le n÷ud racine d'un<br />

R-tree. Soit n un n÷ud, on désigne par F (n) l'<strong>en</strong>semble <strong>de</strong>s ls <strong>de</strong> ce n÷ud.<br />

Etape A : initialisation<br />

L ←− 0<br />

N ←− {r}<br />

Etape B : itération<br />

tant que (N ≠ ∅) faire<br />

pour chaque n ∈ N faire<br />

si W ∩ B (n) ≠ ∅<br />

N ←− N ∪ F (n)<br />

si B (n) est un objet<br />

L ←− B (n)<br />

n si<br />

n si<br />

n pour<br />

n tant que<br />

L est l'<strong>en</strong>semble cherché.<br />

Il reste à construire le R-tree, opération eectuée par la répétition successive <strong>de</strong> l'algorithme 12.5 permet-<br />

139


tant d'insérer un objet dans un R-tree.<br />

Algorithme 12.5 : insertion d'un objet dans un R-tree<br />

Les notations utilisées sont les mêmes que celles <strong>de</strong> l'algorithme 12.4. On cherche à insérer l'object<br />

E désigné par son n÷ud feuille e. On suppose que l'arbre conti<strong>en</strong>t au moins un n÷ud, sa racine<br />

r. On désigne égalem<strong>en</strong>t par p (n){<br />

le père du n÷ud n. Chaque n÷ud ne peut cont<strong>en</strong>ir plus <strong>de</strong> s<br />

ls. On désigne par v ∗ (G) = min P | P ∈ B et ⋃<br />

}<br />

B (g) ⊂ P .<br />

Etape A : sélection du n÷ud d'insertion<br />

n ∗ ←− r<br />

tant que (n ∗ n'est pas un n÷ud feuille) faire<br />

On choisit le ls f <strong>de</strong> n ∗ qui minimise l'accroissem<strong>en</strong>t v f −v (B (f)) du volume<br />

avec v f déni par :<br />

g∈G<br />

v f = min {v (P ) | P ∈ B et B (f) ∪ B (e) ⊂ P } (12.2)<br />

n ∗ ←− f<br />

n tant que<br />

Etape B : ajout du n÷ud<br />

Si p (n ∗ ) a moins <strong>de</strong> s ls, alors le n÷ud e <strong>de</strong>vi<strong>en</strong>t le ls <strong>de</strong> p (n ∗ ) et B (p (n ∗ ))<br />

est mis à jour d'après l'expression (12.2). L'insertion est terminée. Dans le cas<br />

contraire, on sépare découpe le n÷ud p (n ∗ ) <strong>en</strong> <strong>de</strong>ux grâce à l'étape suivante.<br />

Etape C : découpage <strong>de</strong>s n÷uds<br />

L'objectif est <strong>de</strong> diviser le groupe G composé <strong>de</strong> s + 1 n÷uds <strong>en</strong> <strong>de</strong>ux groupes<br />

G 1 et G 1 . Tout d'abord, on cherche le couple (n 1 , n 2 ) qui minimise le critère<br />

d = v ∗ ({n 1 , n 2 }) − v (B (n 1 )) − v (B (n 2 ))<br />

Alors : G 1 ←− n 1 , G 2 ←− n 2 et G ←− G − G 1 ∪ G 2<br />

tant que (G ≠ ∅) faire<br />

On choisit un n÷ud n ∈ G, on détermine i ∗ tel que v ({n} ∪ G i ) − v (G i ) soit<br />

minimal.<br />

G ←− G − {n}<br />

G i ∗ ←− G i ∗ ∪ {n}<br />

n tant que<br />

Si la recherche est i<strong>de</strong>ntique quel que soit l'arbre construit, chaque variante <strong>de</strong> la construction <strong>de</strong> l'arbre<br />

t<strong>en</strong>te <strong>de</strong> minimiser les intersections <strong>de</strong>s boîtes et leur couverture. Plus précisém<strong>en</strong>t, l'étape C qui permet<br />

<strong>de</strong> découper les n÷uds est conçue <strong>de</strong> manière à obt<strong>en</strong>ir <strong>de</strong>s boîtes <strong>en</strong>globantes <strong>de</strong> volume minimale et/ou<br />

d'intersection minimale avec d'autres boîtes <strong>en</strong>globantes. L'algorithme R+ Tree (voir [Sellis1987]) essaye <strong>de</strong><br />

minimiser les intersections <strong>en</strong>tre boîtes et les objets à organiser sont supposés n'avoir aucune intersection<br />

commune. La variante R ∗ Tree (voir [Beckmann1990]) eectue un compromis <strong>en</strong>tre l'intersection et la<br />

couverture <strong>de</strong>s boîtes <strong>en</strong>globantes. L'algorithme X-Tree (voir [Berchtold1996]) conserve l'historique <strong>de</strong> la<br />

construction <strong>de</strong> l'arbre ce qui lui permet <strong>de</strong> mieux éviter les intersections communes <strong>en</strong>tre boîtes. Ces<br />

techniques apparti<strong>en</strong>n<strong>en</strong>t à une classe plus larges d'algorithmes <strong>de</strong> type Branch and Bound.<br />

12.1.4 LAESA<br />

Cet algorithme permet <strong>de</strong> chercher les plus proches voisins dans un <strong>en</strong>semble inclus dans un espace métrique<br />

quelconque. Il s'appuie sur l'inégalité triangulaire. L'algorithme LAESA (Linear Approximating<br />

140


Eliminating Search Algorithm, voir [Rico-Juan2003]) consiste à éviter un trop grand nombre <strong>de</strong> calculs <strong>de</strong><br />

distances <strong>en</strong> se servant <strong>de</strong> distances déjà calculées <strong>en</strong>tre les élém<strong>en</strong>ts <strong>de</strong> E et un sous-<strong>en</strong>semble B inclus<br />

dans E cont<strong>en</strong>ant <strong>de</strong>s "pivots". La sélection <strong>de</strong>s pivots peut être aléatoire ou plus élaborée comme celle<br />

eectuée par l'algorithme qui suit, décrit dans l'article [Mor<strong>en</strong>o2003].<br />

Algorithme 12.6 : LAESA : sélection <strong>de</strong>s pivots<br />

Soit E = {y 1 , . . . , y N } un <strong>en</strong>semble <strong>de</strong> points, on cherche à déterminer l'<strong>en</strong>semble B =<br />

{p 1 , . . . , p P } ⊂ E utilisé par l'algorithme ??.<br />

Etape A : initialisation<br />

B ←− y ∈ E choisi arbitrairem<strong>en</strong>t.<br />

Etape B : calcul <strong>de</strong> la fonction g<br />

pour chaque y ∈ E − B faire<br />

g (y) ←− 0<br />

pour chaque p ∈ B faire<br />

g (y) ←− g (y) + d (y, p)<br />

n pour<br />

n pour<br />

Etape C : mise à jour <strong>de</strong> B<br />

Trouver p ∗ ∈ arg max {g (p) | p ∈ E − B}<br />

B ←− B ∪ {p ∗ }<br />

Si card (B) < P , retour à l'étape B sinon n.<br />

L'algorithme LAESA utilise les pivots pour diminuer le nombre <strong>de</strong> calculs <strong>en</strong> utilisant l'inégalité triangulaire.<br />

Par exemple, soit x un élém<strong>en</strong>t à classer, p j un pivot, y i un point du nuage. On suppose qu'on<br />

connaît d (x, p j ), d (p j , y i ) et d ∗ la distance du point x à un autre point du nuage. L'inégalité triangulaire<br />

permet d'armer que si : d (x, y i ) |d (x, p j ) − d (p j , y i )| > d ∗ , alors il n'est pas nécessaire <strong>de</strong> calculer la<br />

141


distance d (x, y i ) pour armer que d (x, y i ) > d ∗ . L'élém<strong>en</strong>t y i ne peut être l'élém<strong>en</strong>t le plus proche.<br />

Algorithme 12.7 : LAESA<br />

Soit E = {y 1 , . . . , y N } un <strong>en</strong>semble <strong>de</strong> points, B = {p 1 , . . . , p P } ⊂ E un <strong>en</strong>semble <strong>de</strong> pivots<br />

inclus dans E. On cherche à déterminer le voisinage V (x) <strong>de</strong> x inclus dans E vériant :<br />

On suppose que la matrice M = (m ij ) iP<br />

1jN<br />

∀y ∈ V (x) , d (x, y) ρ<br />

a été calculée préalablem<strong>en</strong>t comme suit :<br />

∀ (i, j) , m ij = d (p i , y j )<br />

Etape A : initialisation<br />

pour i = 1 à P faire<br />

d i ←− d (x, p i )<br />

n pour<br />

d ∗ ←− min {d i | 1 i P }.<br />

d ∗ est la distance du point x au pivot le plus proche.<br />

Etape B : recherche du plus proche élém<strong>en</strong>t<br />

S ←− ∅<br />

pour i = 1 à N faire<br />

d ′ ←− max {|d j − m ji |}<br />

si d ′ < d ∗<br />

d ←− d (x, y i )<br />

si d ′ d ∗<br />

d ∗ ←− d ′<br />

S ←− {y i }<br />

n si<br />

n si<br />

n pour<br />

12.1.5 Résultats théoriques<br />

L'article [Faragó1993] démontre égalem<strong>en</strong>t qu'il existe une majoration du nombre moy<strong>en</strong> <strong>de</strong> calcul <strong>de</strong><br />

distances pour peu que la mesure <strong>de</strong> l'espace cont<strong>en</strong>ant l'<strong>en</strong>semble E et l'élém<strong>en</strong>t x soit connue et que<br />

l'<strong>en</strong>semble B = {p 1 , . . . , p P } <strong>de</strong>s pivots vérie :<br />

∃ (α, β) ∈ R + ∗ tels que<br />

∀ (x, y) ∈ E 2 , ∀i α d (x, y) |d (x, p i ) − d (p i , y)| (12.3)<br />

∀ (x, y) ∈ E 2 , max<br />

i<br />

|d (x, p i ) − d (p i , y)| β d (x, y) (12.4)<br />

L'algorithme développé dans [Faragó1993] permet <strong>de</strong> trouver le point <strong>de</strong> plus proche d'un élém<strong>en</strong>t x dans<br />

142


un <strong>en</strong>semble E = {x 1 , . . . , x N } selon l'algorithme suivant :<br />

Algorithme 12.8 : plus proche voisin d'après [Faragó1993]<br />

Soit E = {x 1 , . . . , x N } et B = {p 1 , . . . , p P } ⊂ E ⊂ X. Soit x ∈ X un élém<strong>en</strong>t quelconque. On<br />

suppose que les valeurs m ij = d (x i , p j ) ont été préalablem<strong>en</strong>t calculées.<br />

Etape A : initialisation<br />

On calcule préalablem<strong>en</strong>t les coeci<strong>en</strong>ts γ (x i ) :<br />

∀i ∈ {1, . . . , N} , γ (x i ) ←−<br />

max |m ij − d (x, p j )|<br />

j∈{1,...,P }<br />

Etape B : élaguage<br />

On dénit t 0 ←− min<br />

i<br />

γ (x i ).<br />

Puis on construit l'<strong>en</strong>semble F (x) = {x i ∈ E | γ (x i )} α β t 0.<br />

Etape C : plus proche voisin<br />

Le plus proche x ∗ voisin est déni par : x ∗ ∈ arg min {d (x, y) | y ∈ F (x)}.<br />

Théorème 12.9 : [Faragó1993] 1<br />

Les notations sont celles <strong>de</strong> l'algorithme 12.8. L'algorithme 12.8 retourne le plus proche voisin<br />

x ∗ <strong>de</strong> x inclus dans E. Autrem<strong>en</strong>t dit, ∀x ∈ X, x ∗ ∈ F (x).<br />

Théorème 12.10 : [Faragó1993] 2<br />

Les notations sont celles <strong>de</strong> l'algorithme 12.8. On dénit une mesure sur l'<strong>en</strong>semble X, B (x, r)<br />

désigne la boule <strong>de</strong> c<strong>en</strong>tre x et <strong>de</strong> rayon r, Z ∈ X une variable aléatoire, <strong>de</strong> plus :<br />

p (x, r) = P X (B (x, r)) = P (Z ∈ B (x, r))<br />

On suppose qu'il existe d > 0 et une fonction f : X −→ R tels que :<br />

p (x, r)<br />

lim<br />

r→0 r d = f (x) > 0<br />

La converg<strong>en</strong>ce doit être uniforme et presque sûre. On note égalem<strong>en</strong>t F N le nombre <strong>de</strong> calculs<br />

<strong>de</strong> dissimilarité eectués par l'algorithme 12.8 où N est le nombre d'élém<strong>en</strong>t <strong>de</strong> E, P désigne<br />

toujours le nombre <strong>de</strong> pivots, alors :<br />

lim sup<br />

n→∞<br />

( ) α 2d<br />

E (F N ) k +<br />

β<br />

143


12.2 Programme, explications <strong>de</strong> l'exemple E12<br />

12.2.1 Programmes annexes<br />

Ces <strong>de</strong>ux <strong>programmes</strong> permett<strong>en</strong>t d'obt<strong>en</strong>ir un jeu <strong>de</strong> données permettant d'utiliser la métho<strong>de</strong> <strong>de</strong>s plus<br />

proches voisins an <strong>de</strong> reconnaître <strong>de</strong> chires manuscrits. Le premier programme html_file.py permet<br />

d'écrire un chier au format HTML, il est utilisé an <strong>de</strong> générer une page internet permettant <strong>de</strong> pr<strong>en</strong>dre<br />

connaissance <strong>de</strong>s résultats <strong>de</strong> classication (ou <strong>de</strong> reconnaissance).<br />

1 # -*- coding: cp1252 -*-<br />

2 import os<br />

3 import os.path<br />

4 import PIL.Image<br />

5<br />

6 class html_file (object) :<br />

7 """cette classe propose quelques métho<strong>de</strong>s pour écrire un fichier HTML simplem<strong>en</strong>t"""<br />

8<br />

9 hea<strong>de</strong>r = """<br />

10 <br />

11 <br />

12 <br />

13 """<br />

14<br />

15 foot = """<br />

16 <br />

17 """<br />

18<br />

19 <strong>de</strong>f __init__ (self, file) :<br />

20 """construction d'un fichier HTML"""<br />

21 self.file = file<br />

22<br />

23 <strong>de</strong>f op<strong>en</strong> (self) :<br />

24 """ouverture du fichier"""<br />

25 self.f = op<strong>en</strong> (self.file, "w")<br />

26 self.f.write (self.hea<strong>de</strong>r)<br />

27<br />

28 <strong>de</strong>f close (self) :<br />

29 """fermeture du fichier"""<br />

30 self.f.close ()<br />

31<br />

32 <strong>de</strong>f text (self, s) :<br />

33 """écrit du texte dans un fichier HTML"""<br />

34 s = s.replace ("\n", "")<br />

35 self.f.write (s)<br />

36<br />

37 <strong>de</strong>f text_line (self) :<br />

38 """passe une ligne dans un fichier HTML"""<br />

39 self.f.write ("")<br />

40<br />

144


41 <strong>de</strong>f table_begin (self, nc) :<br />

42 """comm<strong>en</strong>ce une table, nc est le nombre <strong>de</strong> colonnes"""<br />

43 self.f.write ("""\n \n """)<br />

45<br />

46 <strong>de</strong>f table_<strong>en</strong>d (self) :<br />

47 """termine une table"""<br />

48 self.f.write (""" \n \n\n""")<br />

49<br />

50 <strong>de</strong>f table_next (self) :<br />

51 """passe à la colonne suivante"""<br />

52 self.f.write (""" \n """)<br />

53<br />

54 <strong>de</strong>f table_next_line (self) :<br />

55 """passe à la ligne suivante"""<br />

56 self.f.write (""" \n \n \n """)<br />

57<br />

58 <strong>de</strong>f relative_path (self, url) :<br />

59 """trouve le chemin relatif par rapport au fichier"""<br />

60 pr = os.path.commonprefix ( [ self.file, url ] )<br />

61 if pr != None :<br />

62 d = os.path.dirname (self.file)<br />

63 if pr.count (d) == 1 :<br />

64 pr = d + "\\"<br />

65 url = url [ l<strong>en</strong> (pr) : l<strong>en</strong> (url) ]<br />

66 return url<br />

67<br />

68 <strong>de</strong>f clean_url (self, url) :<br />

69 """<strong>en</strong>lève les espaces et redresse les barres"""<br />

70 url = url.replace (" ", "\%20")<br />

71 url = url.replace ("\\", "/")<br />

72 return url<br />

73<br />

74 <strong>de</strong>f add_link (self, url, caption, isfile) :<br />

75 """ajoute un li<strong>en</strong> dans le fichier html,<br />

76 url est l'adresse, caption est l'intitulé du li<strong>en</strong>,<br />

77 si isfile vaut True, cherche un chemin relatif par rapport à self.file"""<br />

78 if isfile : url = self.relative_path (url)<br />

79 url = self.clean_url (url)<br />

80 s = """%s""" % (url, caption)<br />

81 self.f.write (s)<br />

82<br />

83 <strong>de</strong>f add_image (self, image, size = None, zoom = None) :<br />

84 """ajoute une image dans le fichier html,<br />

85 celle-ci est <strong>en</strong>registrée avec un numéro, of<strong>fr</strong>e la possibilité <strong>de</strong> zoomer<br />

86 ou <strong>de</strong> modifier la taille"""<br />

87 if not self.__dict__.has_key ("num_image") : self.num_image = 0<br />

88 name, ext = os.path.splitext (self.file)<br />

89 name = name + str (self.num_image) + ".png"<br />

90 image.save (name)<br />

145


91 self.add_image_link (name, size, zoom)<br />

92 self.num_image += 1<br />

93<br />

94 <strong>de</strong>f add_image_link (self, url, size = None, zoom = None) :<br />

95 """ajoute une image dans le fichier html,<br />

96 cette image n'est pas recopiée, il est possible <strong>de</strong> spécifier une taille,<br />

97 ou <strong>de</strong> multiplier cette taille"""<br />

98<br />

99 if size == None :<br />

100 im = PIL.Image.op<strong>en</strong> (url)<br />

101 size = im.size<br />

102 if zoom != None :<br />

103 size = (int (size [0] * zoom), int (size [1] * zoom))<br />

104<br />

105 url = self.relative_path (url)<br />

106 url = self.clean_url (url)<br />

107<br />

108 s = """""" \<br />

109 % (size [0], size [1], url, url)<br />

110 self.f.write (s)<br />

111<br />

112<br />

113 if __name__ == "__main__" :<br />

114<br />

115 print "écriture du fichier ",file<br />

116 file = "c:\\temp\\essai.html"<br />

117<br />

118 html = html_file (file)<br />

119 html.op<strong>en</strong> ()<br />

120 html.text ("""première ligne d'un fichier HTML,<br />

121 secon<strong>de</strong> ligne""")<br />

122 html.table_begin (2)<br />

123 html.text ("1")<br />

124 html.table_next ()<br />

125 html.text ("2")<br />

126 html.table_next_line ()<br />

127 html.text("3")<br />

128 html.table_next ()<br />

129 html.text ("4")<br />

130 html.table_<strong>en</strong>d ()<br />

131 html.add_link ("c:\\temp\\cours.py", "cours.py", True)<br />

132 html.text_line ()<br />

133 im = PIL.Image.new ("RGB", (100,100), color = (100,100,100))<br />

134 html.add_image (im)<br />

135 html.add_image (im, zoom = 2)<br />

136 html.close ()<br />

137<br />

138 print "fin"<br />

139<br />

146


Le second chier permet <strong>de</strong> lire la base <strong>de</strong> données (MNIST) regroupant les images <strong>de</strong> caractères. Celle-ci<br />

est disponible à l'adresse http ://yann.lecun.com/exdb/mnist/. La base MNIST est scindée <strong>en</strong> <strong>de</strong>ux sousbases<br />

d'appr<strong>en</strong>tissage et <strong>de</strong> tests. La première sert à estimer les paramètres d'un système <strong>de</strong> classication,<br />

la secon<strong>de</strong> sert à évaluer les performances <strong>de</strong> ce système sur <strong>de</strong>s données non apprises. Ce site internet<br />

référ<strong>en</strong>ce <strong>de</strong>s performances obt<strong>en</strong>ues avec <strong>de</strong>s métho<strong>de</strong>s classiques <strong>de</strong> classication telles que les réseaux <strong>de</strong><br />

neurones, les Support Vector Machines (SVM), les plus proches voisins... Le programme mnist.py permet<br />

d'extraire les images <strong>de</strong> chires <strong>de</strong>s chiers prés<strong>en</strong>ts sur ce site.<br />

1 # -*- coding: cp1252 -*-<br />

2 """décomposition <strong>de</strong> la base d'image MNIST"""<br />

3<br />

4 import os<br />

5 import struct<br />

6 import PIL # ligne à supprimer si on n'a pas besoin <strong>de</strong>s images<br />

7 import PIL.Image as PIM # ligne à supprimer si on n'a pas besoin <strong>de</strong>s images<br />

8<br />

9 <strong>de</strong>f transcription_<strong>en</strong>tier (str) :<br />

10 """convertit un <strong>en</strong>tier lu <strong>en</strong> format binaire <strong>en</strong> <strong>en</strong>tier"""<br />

11 t = struct.unpack ("BBBB", str)<br />

12 i = t [3] + t [2] * 256 + t [1] * 256 * 256 + t [0] * 256 * 256 * 256<br />

13 return i<br />

14<br />

15 <strong>de</strong>f lire_image (fim, nl, nc) :<br />

16 """lit une image dans le fichier fim, cette image a nl lignes et nc colonnes,<br />

17 retourne l'image sous la forme d'un t-uple"""<br />

18 nb = nl * nc<br />

19 str = fim.read (nb)<br />

20 t = struct.unpack ("B" * nb, str)<br />

21 return t<br />

22<br />

23 <strong>de</strong>f lire_image (fim, nl, nc) :<br />

24 """lit une image dans le fichier fim, cette image a nl lignes et nc colonnes,<br />

25 retourne l'image sous la forme d'un t-uple"""<br />

26 nb = nl * nc<br />

27 str = fim.read (nb)<br />

28 t = struct.unpack ("B" * nb, str)<br />

29 return t<br />

30<br />

31 <strong>de</strong>f lire_label (fla) :<br />

32 """lit un label (un chif<strong>fr</strong>e) dans le fichier fla, retourne un <strong>en</strong>tier"""<br />

33 str = fla.read (1)<br />

34 t = struct.unpack ("B", str)<br />

35 return t [0]<br />

36<br />

37 <strong>de</strong>f binarisation (image, binar) :<br />

38 """binarisation d'une image"""<br />

39<br />

40 res = []<br />

41 for i in image :<br />

42 if i < 255 - binar : res.app<strong>en</strong>d (255)<br />

147


43 else : res.app<strong>en</strong>d (0)<br />

44 return res<br />

45<br />

46<br />

47 <strong>de</strong>f write_image_matlab (fmat, image) :<br />

48 """écrit une image au format matlab, chaque nombre indique<br />

49 la position du prochain pixel noir,<br />

50 la suite est aussi longue qu'il y a <strong>de</strong> pixels dans l'image<br />

51 et se termine par une suite <strong>de</strong> 0 qui indique qu'il n'y a plus <strong>de</strong> pixels noirs"""<br />

52 nb = 0<br />

53 for i in xrange (0, l<strong>en</strong>(image)) :<br />

54 if image [i] == 0 :<br />

55 fmat.write ("," + str (i+1))<br />

56 nb += 1<br />

57 for i in xrange (0, l<strong>en</strong> (image) - nb) :<br />

58 fmat.write (",0")<br />

59<br />

60 <strong>de</strong>f <strong>de</strong>compose_mnist (file_image, file_label, file_matlab, binar, \<br />

61 nbcl = -1, dir_image1 = None, dir_image2 = None) :<br />

62 """décompose la base MNIST <strong>en</strong> un format utilisable sous MATLAB,<br />

63 comm<strong>en</strong>ce par [, termine par ], chaque ligne est composée comme suit :<br />

64 premier nombre = classe, voir fonction write_image_matlab ;,<br />

65 nbcl est le nombre d'images à extraire par classe,<br />

66 si nbcl == -1, traite toute la base,<br />

67 binar est le seuil <strong>de</strong> binarisation, voir fonction binarisation,<br />

68 si dir_image1 != None, les images <strong>de</strong>s nombres sont écrites,<br />

69 si dir_image2 != None, on continue d'explorer le fichier <strong>de</strong>s images<br />

70 mais les images <strong>de</strong>s nombres sont écrites dans le répertoire dir_image2"""<br />

71<br />

72 fim = op<strong>en</strong> (file_image, "rb")<br />

73 fla = op<strong>en</strong> (file_label, "rb")<br />

74<br />

75 s = fla.read (4) # , (Hal_byte*) &umagic_an) ;<br />

76 magic_an = transcription_<strong>en</strong>tier (s)<br />

77 s = fla.read (4) # , (Hal_byte*) &unb_an) ;<br />

78 nb_an = transcription_<strong>en</strong>tier (s)<br />

79<br />

80 s = fim.read (4) # , (Hal_byte*) &umagic_im) ;<br />

81 magic_im = transcription_<strong>en</strong>tier (s)<br />

82 s = fim.read (4) # , (Hal_byte*) &unb_im) ;<br />

83 nb_im = transcription_<strong>en</strong>tier (s)<br />

84 s = fim.read (4) #, (Hal_byte*) &unl_im) ;<br />

85 nl_im = transcription_<strong>en</strong>tier (s)<br />

86 s = fim.read (4) #, (Hal_byte*) &unc_im) ;<br />

87 nc_im = transcription_<strong>en</strong>tier (s)<br />

88<br />

89 if nbcl == -1 : nbcl = nb_im<br />

90<br />

91 print "nombre <strong>de</strong> labels ", nb_an<br />

92 print "nombre magique label , vérification (2049) ", magic_an<br />

148


93 print "nombre d'images ", nb_im<br />

94 print "nombre magique image, vérification (2051) ", magic_im<br />

95 print "nombre <strong>de</strong> lignes ", nl_im<br />

96 print "nombre <strong>de</strong> colonnes ", nc_im<br />

97<br />

98 if file_matlab != None :<br />

99 fmat = op<strong>en</strong> (file_matlab, "wt")<br />

100 fmat.write ("[")<br />

101<br />

102 label_count = { }<br />

103<br />

104 nb = 0<br />

105 c<strong>en</strong>t = int (nb_im / 100)<br />

106 while nb < nb_im :<br />

107<br />

108 if nb % c<strong>en</strong>t == 0 : print "avancem<strong>en</strong>t ", nb / c<strong>en</strong>t, " %"<br />

109 nb += 1<br />

110<br />

111 image = lire_image (fim, nl_im, nc_im)<br />

112 label = lire_label (fla)<br />

113<br />

114 image = binarisation (image, binar)<br />

115<br />

116 if not label_count.has_key (label) : label_count [label] = 0<br />

117 if 0


143<br />

144 if file_matlab != None :<br />

145 fmat.write ("]")<br />

146 fmat.close ()<br />

147<br />

148 fim.close ()<br />

149 fla.close ()<br />

150<br />

151 key = label_count.keys ()<br />

152 key.sort ()<br />

153 for i in key :<br />

154 print "label ", i , " \t : ", label_count [i]<br />

155<br />

156 return label_count<br />

157<br />

158<br />

159 if __name__ == "__main__" :<br />

160 file_image = "C:\\Downloads\\data_image\\mnist\\t10k-images.idx3-ubyte"<br />

161 file_label = "C:\\Downloads\\data_image\\mnist\\t10k-labels.idx1-ubyte"<br />

162 file_matlab = "c:\\temp\\mnist2\\matlab.txt"<br />

163 dir_image = None # "c:\\temp\\mnist2"<br />

164 nb = 20<br />

165 binar = 190<br />

166<br />

167 <strong>de</strong>compose_mnist (file_image, file_label, file_matlab, \<br />

168 binar, nb, dir_image1, dir_image2)<br />

12.2.2 Programme <strong>de</strong> classication<br />

Les <strong>de</strong>ux premiers chiers implém<strong>en</strong>t<strong>en</strong>t la métho<strong>de</strong> <strong>de</strong> classication <strong>de</strong>s plus proches voisins dans le<br />

cas d'une reconnaissance <strong>de</strong> caractères. Aucune optimisation n'est eectuée. Le premier chier kppv.py<br />

concrétise l'algorithme <strong>de</strong> classication, le second chier kppv_image.py s'occup<strong>en</strong>t <strong>de</strong> charger les images<br />

<strong>de</strong> caractères et d'utiliser le chier précé<strong>de</strong>nt pour les classer, ce qui équivaut à les reconnaître.<br />

Le premier chier conti<strong>en</strong>t la classe nuage_points. La métho<strong>de</strong> ppv recherche le plus proches voisins dans<br />

le nuage nuage. La métho<strong>de</strong> ppv_nuage l'utilise pour classer tous les élém<strong>en</strong>ts d'un nuage qu'elle reçoit<br />

<strong>en</strong> paramètre. Pour utiliser, il faut créer une classe qui dérive <strong>de</strong> celle-ci an <strong>de</strong> surcharger les métho<strong>de</strong>s<br />

distance et label dont le co<strong>de</strong> dép<strong>en</strong>d <strong>de</strong>s élém<strong>en</strong>ts à classer.<br />

1 # -*- coding: cp1252 -*-<br />

2 import math<br />

3 import random<br />

4<br />

5<br />

6 class nuage_points (object) :<br />

7 """définit une classe <strong>de</strong> nuage <strong>de</strong> points"""<br />

8<br />

9 <strong>de</strong>f __init__ (self) :<br />

10 """constructeur"""<br />

11 self.nb = 0 # aucun élém<strong>en</strong>t<br />

150


12 self.nuage = [] # aucun élém<strong>en</strong>t<br />

13<br />

14 <strong>de</strong>f __iter__(self) :<br />

15 """retourne un itérateur sur l'<strong>en</strong>semble <strong>de</strong> points"""<br />

16 return self.nuage.__iter__ ()<br />

17<br />

18 <strong>de</strong>f __l<strong>en</strong>__ (self) :<br />

19 """retourne le nombre d'élém<strong>en</strong>t"""<br />

20 return l<strong>en</strong> (self.nuage)<br />

21<br />

22 <strong>de</strong>f distance (self, obj1, obj2) :<br />

23 """retourne une distance <strong>en</strong>tre <strong>de</strong>ux élém<strong>en</strong>ts obj1 et obj2"""<br />

24 return 0<br />

25<br />

26 <strong>de</strong>f label (self, obj) :<br />

27 """retourne le label d'un objet"""<br />

28 return None<br />

29<br />

30 <strong>de</strong>f ppv (self, obj) :<br />

31 """retourne l'élém<strong>en</strong>t le plus proche <strong>de</strong> obj et sa distance avec obj"""<br />

32 mn = None<br />

33 dm = None<br />

34 for l in self.nuage :<br />

35 d = self.distance (l, obj)<br />

36 if dm == None or d < dm :<br />

37 dm = d<br />

38 mn = l<br />

39 return mn,dm<br />

40<br />

41 <strong>de</strong>f ppv_nuage (self, nuage, bi<strong>en</strong>classe = None, erreur = None) :<br />

42 """calcule un taux <strong>de</strong> classification pour un nuage,<br />

43 utilise la fonction ppv pour chaque élém<strong>en</strong>t <strong>de</strong> nuage,<br />

44 retourne le nombre d'élém<strong>en</strong>t bi<strong>en</strong> classés, et mal classés,<br />

45 si bi<strong>en</strong>classe est une liste vi<strong>de</strong> (!= None), cette liste conti<strong>en</strong>dra<br />

46 la liste <strong>de</strong>s couples d'élém<strong>en</strong>ts (x,y) in (nuage, self) pour lesquels<br />

47 la classification est bonne,<br />

48 si erreur est une liste vi<strong>de</strong> (!= None), cette liste conti<strong>en</strong>dra<br />

49 la liste <strong>de</strong>s couples d'élém<strong>en</strong>ts (x,y) in (nuage,self)<br />

50 pour lesquels la classification est mauvaise"""<br />

51 disp = l<strong>en</strong> (nuage) / 10 ;<br />

52 good = 0<br />

53 bad = 0<br />

54 n = 0<br />

55 for x in nuage :<br />

56 obj,d = self.ppv (x)<br />

57 if self.label (obj) == self.label (x) :<br />

58 good += 1<br />

59 if bi<strong>en</strong>classe != None : bi<strong>en</strong>classe.app<strong>en</strong>d ((obj,x))<br />

60 else :<br />

61 bad += 1<br />

151


62 if erreur != None : erreur.app<strong>en</strong>d ((obj, x))<br />

63 if n % disp == 0 : print "ppv_nuage ", n * 100 / l<strong>en</strong> (nuage), "%"<br />

64 n += 1<br />

65 return good, bad<br />

66<br />

Le second chier conti<strong>en</strong>t la classe nuage_point_distance_label qui dérive <strong>de</strong> nuage_points pour<br />

redénir les métho<strong>de</strong>s distance et pour <strong>de</strong>s images <strong>de</strong> taille xe 32x32. La distance <strong>en</strong>tre <strong>de</strong>ux images est<br />

le nombre <strong>de</strong> pixels qui diér<strong>en</strong>t <strong>en</strong>tre elles. La secon<strong>de</strong> classe se cont<strong>en</strong>te <strong>de</strong> charger les images aux travers<br />

<strong>de</strong>s métho<strong>de</strong>s image et __init__. La métho<strong>de</strong> html_couple construit un chier HTML permettant <strong>de</strong><br />

visionner les erreurs <strong>de</strong> reconnaissance.<br />

Enn, la métho<strong>de</strong> ppv_nuage appelle la classication d'un <strong>en</strong>semble d'images test à l'ai<strong>de</strong> d'un <strong>en</strong>semble<br />

d'image appr<strong>en</strong>tissage pour lesquelles la classe est supposée connue.<br />

1 # -*- coding: cp1252 -*-<br />

2 import os<br />

3 import os.path<br />

4 import mnist<br />

5 import string<br />

6 import PIL<br />

7 import PIL.Image<br />

8 import html_file as html<br />

9 import psyco<br />

10 import random<br />

11 import kppv<br />

12 import time<br />

13<br />

14 # définit une classe héritant <strong>de</strong> kppv.nuage_points qui surcharge les<br />

15 # fonctions distance et label<br />

16 class nuage_point_distance_label (kppv.nuage_points) :<br />

17 """hérite <strong>de</strong> kppv.nuage_point et surcharge les métho<strong>de</strong>s :<br />

18 - distance<br />

19 - label<br />

20 """<br />

21<br />

22 <strong>de</strong>f distance (self, obj1, obj2) :<br />

23 """surcharge <strong>de</strong> la fonction distance, comme les images sont toutes<br />

24 <strong>de</strong> dim<strong>en</strong>sions i<strong>de</strong>ntiques, on peut compter les pixels <strong>de</strong> couleurs différ<strong>en</strong>tes,<br />

25 le résultat est la distance <strong>en</strong>tre <strong>de</strong>ux images"""<br />

26 if l<strong>en</strong> (obj1) != l<strong>en</strong> (obj2) :<br />

27 print "erreur, l<strong>en</strong> (obj1) != l<strong>en</strong> (obj2)"<br />

28 d = 0<br />

29 for i in xrange (2, l<strong>en</strong> (obj1)) :<br />

30 if obj1 [i] != obj2 [i] : d += 1<br />

31 return d<br />

32<br />

33 <strong>de</strong>f label (self, obj) :<br />

34 """retourne le label d'un objet"""<br />

35 return obj [0]<br />

36<br />

152


37<br />

38 class nuage_image (object) :<br />

39 """nuage <strong>de</strong> points, chaque élém<strong>en</strong>t est une image,<br />

40 les images sont <strong>en</strong> noir et blanc, liste <strong>de</strong> 0 ou 1"""<br />

41<br />

42 <strong>de</strong>f __init__ (self, nuage_ppv, rep, nb, ext<strong>en</strong>sion = "tif") :<br />

43 """initialise le nuage d'images avec un répertoire<br />

44 qui est l'emplacem<strong>en</strong>t <strong>de</strong>s images pour ce nuage,<br />

45 le nom <strong>de</strong>s images conti<strong>en</strong>t une chaîne <strong>de</strong> caractères suivi<br />

46 d'un label séparés par un blanc soulignés,<br />

47 ext<strong>en</strong>sion est le type <strong>de</strong> images à charger,<br />

48 on ne considère que les nb premières images,<br />

49 nuage est une instance <strong>de</strong> la classe nuage_point ou nuage_point_laesa"""<br />

50<br />

51 <strong>de</strong>f binarise (p) :<br />

52 if p [0] == 0 : return 1<br />

53 else : return 0<br />

54<br />

55 self.nb = 0<br />

56 self.nuage = []<br />

57<br />

58 file = os.listdir (rep)<br />

59 nb = min (nb, l<strong>en</strong> (file))<br />

60 step = nb / 10<br />

61 n = 0<br />

62 for f in file :<br />

63 if n >= nb : break<br />

64 if n % step == 0 : print "nuage_image, avancem<strong>en</strong>t ", n * 100 / nb, "%"<br />

65 n += 1<br />

66 ext = os.path.splitext (f)<br />

67 ext = ext [l<strong>en</strong> (ext) - 1].lower ()<br />

68 ext = ext [1 : l<strong>en</strong> (ext)]<br />

69 if ext != "tif" : continue<br />

70 s = f.split ("_")<br />

71 label = s [1]<br />

72 im = PIL.Image.op<strong>en</strong> (rep + "\\" + f)<br />

73 size = im.size<br />

74 data = [ binarise (im.getpixel ((x,y))) for y in xrange (0, size [1]) \<br />

75 for x in xrange (0, size [0]) ]<br />

76 data.insert (0, size)<br />

77 data.insert (0, label)<br />

78 self.nuage.app<strong>en</strong>d ( tuple (data))<br />

79<br />

80 self.ppv = nuage_ppv<br />

81 self.ppv.nb = self.nb<br />

82 self.ppv.nuage = self.nuage<br />

83<br />

84<br />

85 <strong>de</strong>f image (self, obj) :<br />

86 """reconstruit une image à partir d'un élém<strong>en</strong>t"""<br />

153


87 size = obj [1]<br />

88 im = PIL.Image.new ("RGB", size)<br />

89 nb = l<strong>en</strong> (obj) - 2<br />

90 for i in xrange (0, nb) :<br />

91 if obj [i] == 0 : im.putpixel ( (i % size [0], i // size [1]), (255,255,255))<br />

92 else : im.putpixel ((i % size [0], i // size [1]), (0,0,0))<br />

93 return im<br />

94<br />

95 <strong>de</strong>f html_couple (self, fichier, l, zoom = None) :<br />

96 """écrit un fichier html cont<strong>en</strong>ant toutes les images mal classées,<br />

97 à partir <strong>de</strong> la liste erreur construite par la métho<strong>de</strong> nuage_points.ppv_nuage"""<br />

98<br />

99 # nombre <strong>de</strong> colonnes maximales<br />

100 maxc = 0<br />

101 for x in l : maxc = max (maxc, l<strong>en</strong> (x))<br />

102<br />

103 f = html.html_file (fichier)<br />

104 f.op<strong>en</strong> ()<br />

105 f.table_begin (maxc*2+1)<br />

106<br />

107 f.text ("indice")<br />

108 f.table_next ()<br />

109 f.text ("label")<br />

110 f.table_next ()<br />

111 f.text ("à classer")<br />

112 f.table_next ()<br />

113 for n in xrange (1, maxc) :<br />

114 f.text ("label")<br />

115 f.table_next ()<br />

116 f.text ("voisin " + str (n))<br />

117 f.table_next ()<br />

118 f.table_next_line ()<br />

119<br />

120 n = 0<br />

121 for x in l :<br />

122 f.text (str (n))<br />

123 f.table_next ()<br />

124 n += 1<br />

125 for el in x :<br />

126 im = self.image (el)<br />

127 f.text (self.ppv.label (el))<br />

128 f.table_next ()<br />

129 f.add_image (im, zoom = zoom)<br />

130 f.table_next ()<br />

131 f.table_next_line ()<br />

132<br />

133 f.table_<strong>en</strong>d ()<br />

134 f.close ()<br />

135<br />

136 <strong>de</strong>f ppv_nuage (self, nuage, bi<strong>en</strong>classe = None, erreur = None) :<br />

154


137 """appelle self.nuage.ppv_nuage"""<br />

138 return self.ppv.ppv_nuage (nuage, bi<strong>en</strong>classe, erreur)<br />

139<br />

140 <strong>de</strong>f __l<strong>en</strong>__ (self) :<br />

141 """retourne l<strong>en</strong> (self.nuage)"""<br />

142 return l<strong>en</strong> (self.ppv)<br />

143<br />

144 <strong>de</strong>f __iter__(self) :<br />

145 """retourne iter (self.nuage)"""<br />

146 return iter (self.ppv)<br />

147<br />

148<br />

149<br />

150 if __name__ == "__main__" :<br />

151<br />

152 psyco.full ()<br />

153<br />

154 rep_app = "c:\\temp\\mnist\\app"<br />

155 rep_test = "c:\\temp\\mnist\\test"<br />

156 nb_app = 400<br />

157 nb_test = 400<br />

158 html_file1 = "c:\\temp\\mnist\\bi<strong>en</strong>classe.html"<br />

159 html_file2 = "c:\\temp\\mnist\\erreur.html"<br />

160<br />

161 # si les images n'exist<strong>en</strong>t pas<br />

162 if not os.path.exists (rep_app) or not os.path.exists (rep_test) :<br />

163 file_image = "C:\\Downloads\\data_image\\mnist\\t10k-images.idx3-ubyte"<br />

164 file_label = "C:\\Downloads\\data_image\\mnist\\t10k-labels.idx1-ubyte"<br />

165 os.makedirs (rep_app)<br />

166 os.makedirs (rep_test)<br />

167 binar = 220<br />

168 nbcl = 40<br />

169 mnist.<strong>de</strong>compose_mnist (file_image, file_label, None, binar, nbcl, \<br />

170 rep_app, rep_test)<br />

171<br />

172 ppv1 = nuage_point_distance_label ()<br />

173 ppv2 = nuage_point_distance_label ()<br />

174<br />

175 print "construction du nuage d'appr<strong>en</strong>tissage"<br />

176 nuage_app = nuage_image (ppv1, rep_app, nb_app)<br />

177 print "nombre <strong>de</strong> points : ", l<strong>en</strong> (nuage_app)<br />

178<br />

179 print "construction du nuage <strong>de</strong> test"<br />

180 nuage_test = nuage_image (ppv2, rep_test, nb_test)<br />

181 print "nombre <strong>de</strong> points : ", l<strong>en</strong> (nuage_test)<br />

182<br />

183 print "résultat <strong>de</strong> la classification"<br />

184 erreur = []<br />

185 bi<strong>en</strong>classe = []<br />

186 temps1 = time.clock ()<br />

155


187 good,bad = nuage_app.ppv_nuage (nuage_test, bi<strong>en</strong>classe, erreur)<br />

188 temps2 = time.clock ()<br />

189 print "---------------------------------------------------------------------------"<br />

190 print "temps <strong>de</strong> traitem<strong>en</strong>t <strong>en</strong> secon<strong>de</strong>s ", temps2 - temps1<br />

191 print "good, bad = ", good, bad<br />

192 print "taux <strong>de</strong> classification : ", float (good) / (good + bad)<br />

193 print "écriture du fichier ", html_file1<br />

194 nuage_app.html_couple (html_file1, bi<strong>en</strong>classe, zoom = 4)<br />

195 print "écriture du fichier ", html_file2<br />

196 nuage_app.html_couple (html_file2, erreur, zoom = 4)<br />

197<br />

198<br />

12.2.3 Optimisation<br />

Encore <strong>de</strong>ux chiers pour réaliser la métho<strong>de</strong> <strong>de</strong>s plus proches voisins à l'ai<strong>de</strong> <strong>de</strong> l'algorithme LAESA.<br />

Ils sont construits selon le même schéma que les <strong>de</strong>ux chiers précé<strong>de</strong>nts. Le premier conti<strong>en</strong>t la classe<br />

nuage_point_laesa qui hérite <strong>de</strong> nuage_points. Elle redénit la métho<strong>de</strong> ppv pour déterminer plus<br />

rapi<strong>de</strong>m<strong>en</strong>t le plus proche voisins. La métho<strong>de</strong> selection_pivots sélectionne aléatoirem<strong>en</strong>t les pivots.<br />

1 # -*- coding: cp1252 -*-<br />

2 import math<br />

3 import random<br />

4 import kppv<br />

5<br />

6<br />

7 class nuage_point_laesa (kppv.nuage_points) :<br />

8 """implém<strong>en</strong>te l'algorithme <strong>de</strong>s plus proches voisins,<br />

9 version LAESA"""<br />

10<br />

11 <strong>de</strong>f __init__ (self) :<br />

12 """construit la classe"""<br />

13 kppv.nuage_points.__init__ (self)<br />

14<br />

15 <strong>de</strong>f selection_pivots (self,nb) :<br />

16 """sélectionne nb pivots aléatoirem<strong>en</strong>ts"""<br />

17 nb = min (nb, l<strong>en</strong> (self.nuage))<br />

18 self.pivots = []<br />

19 while l<strong>en</strong> (self.pivots) < nb :<br />

20 i = random.randint (0,l<strong>en</strong> (self.nuage))<br />

21 if not self.nuage [i] in self.pivots :<br />

22 self.pivots.app<strong>en</strong>d (self.nuage [i])<br />

23<br />

24 # on calcule aussi la distance <strong>de</strong> chaque élém<strong>en</strong>ts au pivots<br />

25 self.dist = []<br />

26 for el in self.nuage :<br />

27 dl = []<br />

28 for p in self.pivots :<br />

29 d = self.distance (el, p)<br />

30 dl.app<strong>en</strong>d (d)<br />

156


31 self.dist.app<strong>en</strong>d (dl)<br />

32<br />

33 <strong>de</strong>f ppv (self, obj) :<br />

34 """retourne l'élém<strong>en</strong>t le plus proche <strong>de</strong> obj et sa distance avec obj,<br />

35 utilise la sélection à l'ai<strong>de</strong> pivots"""<br />

36<br />

37 # initialisation<br />

38 dp = [ self.distance (obj, p) for p in self.pivots ]<br />

39<br />

40 # pivots le plus proche<br />

41 dm = dp [0]<br />

42 im = self.pivots [0]<br />

43 for i in xrange (0, l<strong>en</strong>(self.pivots)) :<br />

44 d = dp [i]<br />

45 if d < dm :<br />

46 dm = d<br />

47 im = self.pivots [i]<br />

48<br />

49 # améliorations<br />

50 for i in xrange (0, l<strong>en</strong> (self.nuage)) :<br />

51<br />

52 # on regar<strong>de</strong> si un pivots permet d'éliminer l'élém<strong>en</strong>t i<br />

53 calcul = True<br />

54 for j in xrange (0, l<strong>en</strong> (self.pivots)) :<br />

55 d = abs (dp [j] - self.dist[i][j])<br />

56 if d > dm :<br />

57 calcul = False<br />

58 break<br />

59<br />

60<br />

61 # dans le cas contraire on calcule la distance<br />

62 if calcul :<br />

63 d = self.distance (obj, self.nuage [i])<br />

64 if d < dm :<br />

65 dm = d<br />

66 im = self.nuage [i]<br />

67<br />

68 return im,dm<br />

69<br />

70<br />

Le <strong>de</strong>rnier chier est construit exactem<strong>en</strong>t selon le même principe que kppv_image.py. La classe<br />

nuage_image_leasa. Elle redénit son constructeur pour appeler la sélection ds pivots.<br />

1 # -*- coding: cp1252 -*-<br />

2 import os<br />

3 import os.path<br />

4 import mnist<br />

5 import string<br />

6 import PIL<br />

157


7 import PIL.Image<br />

8 import html_file as html<br />

9 import psyco<br />

10 import random<br />

11 import kppv_laesa<br />

12 import time<br />

13 import kppv_image<br />

14<br />

15 # définit une classe héritant <strong>de</strong> kppv.nuage_points qui surcharge les<br />

16 # fonctions distance et label<br />

17 class nuage_point_leasa_distance_label (kppv_laesa.nuage_point_laesa) :<br />

18 """hérite <strong>de</strong> kppv.nuage_point et surcharge les métho<strong>de</strong>s :<br />

19 - distance<br />

20 - label<br />

21 """<br />

22<br />

23 <strong>de</strong>f distance (self, obj1, obj2) :<br />

24 """surcharge <strong>de</strong> la fonction distance, comme les images sont toutes<br />

25 <strong>de</strong> dim<strong>en</strong>sions i<strong>de</strong>ntiques, on peut compter les pixels <strong>de</strong> couleurs différ<strong>en</strong>tes,<br />

26 le résultat est la distance <strong>en</strong>tre <strong>de</strong>ux images"""<br />

27 if l<strong>en</strong> (obj1) != l<strong>en</strong> (obj2) :<br />

28 print "erreur, l<strong>en</strong> (obj1) != l<strong>en</strong> (obj2)"<br />

29 d = 0<br />

30 for i in xrange (2, l<strong>en</strong> (obj1)) :<br />

31 if obj1 [i] != obj2 [i] : d += 1<br />

32 return d<br />

33<br />

34 <strong>de</strong>f label (self, obj) :<br />

35 """retourne le label d'un objet"""<br />

36 return obj [0]<br />

37<br />

38<br />

39 class nuage_image_leasa (kppv_image.nuage_image) :<br />

40 """nuage <strong>de</strong> points, chaque élém<strong>en</strong>t est une image,<br />

41 les images sont <strong>en</strong> noir et blanc, liste <strong>de</strong> 0 ou 1"""<br />

42<br />

43 <strong>de</strong>f __init__ (self, nuage_ppv, rep, nb, ext<strong>en</strong>sion = "tif") :<br />

44 """initialise le nuage d'images avec un répertoire<br />

45 qui est l'emplacem<strong>en</strong>t <strong>de</strong>s images pour ce nuage,<br />

46 le nom <strong>de</strong>s images conti<strong>en</strong>t une chaîne <strong>de</strong> caractères suivi<br />

47 d'un label séparés par un blanc soulignés,<br />

48 ext<strong>en</strong>sion est le type <strong>de</strong> images à charger,<br />

49 on ne considère que les nb premières images,<br />

50 nuage est une instance <strong>de</strong> la classe nuage_point ou nuage_point_laesa"""<br />

51<br />

52 kppv_image.nuage_image.__init__(self, nuage_ppv, rep, nb, ext<strong>en</strong>sion)<br />

53 self.ppv.selection_pivots (20)<br />

54<br />

55<br />

56<br />

158


57<br />

58<br />

59 if __name__ == "__main__" :<br />

60<br />

61 psyco.full ()<br />

62<br />

63 rep_app = "c:\\temp\\mnist\\app"<br />

64 rep_test = "c:\\temp\\mnist\\test"<br />

65 nb_app = 400<br />

66 nb_test = 400<br />

67 html_file1 = "c:\\temp\\mnist\\bi<strong>en</strong>classe.html"<br />

68 html_file2 = "c:\\temp\\mnist\\erreur.html"<br />

69<br />

70 # si les images n'exist<strong>en</strong>t pas<br />

71 if not os.path.exists (rep_app) or not os.path.exists (rep_test) :<br />

72 file_image = "C:\\Downloads\\data_image\\mnist\\t10k-images.idx3-ubyte"<br />

73 file_label = "C:\\Downloads\\data_image\\mnist\\t10k-labels.idx1-ubyte"<br />

74 os.makedirs (rep_app)<br />

75 os.makedirs (rep_test)<br />

76 binar = 220<br />

77 nbcl = 40<br />

78 mnist.<strong>de</strong>compose_mnist (file_image, file_label, None, binar, nbcl, \<br />

79 rep_app, rep_test)<br />

80<br />

81 ppv1 = nuage_point_leasa_distance_label ()<br />

82 ppv2 = nuage_point_leasa_distance_label ()<br />

83<br />

84 print "construction du nuage d'appr<strong>en</strong>tissage"<br />

85 nuage_app = nuage_image_leasa (ppv1, rep_app, nb_app)<br />

86 print "nombre <strong>de</strong> points : ", l<strong>en</strong> (nuage_app)<br />

87<br />

88 print "construction du nuage <strong>de</strong> test"<br />

89 nuage_test = nuage_image_leasa (ppv2, rep_test, nb_test)<br />

90 print "nombre <strong>de</strong> points : ", l<strong>en</strong> (nuage_test)<br />

91<br />

92 print "résultat <strong>de</strong> la classification"<br />

93 erreur = []<br />

94 bi<strong>en</strong>classe = []<br />

95 temps1 = time.clock ()<br />

96 good,bad = nuage_app.ppv_nuage (nuage_test, bi<strong>en</strong>classe, erreur)<br />

97 temps2 = time.clock ()<br />

98 print "---------------------------------------------------------------------------"<br />

99 print "temps <strong>de</strong> traitem<strong>en</strong>t <strong>en</strong> secon<strong>de</strong>s ", temps2 - temps1<br />

100 print "good, bad = ", good, bad<br />

101 print "taux <strong>de</strong> classification : ", float (good) / (good + bad)<br />

102 print "écriture du fichier ", html_file1<br />

103 nuage_app.html_couple (html_file1, bi<strong>en</strong>classe, zoom = 4)<br />

104 print "écriture du fichier ", html_file2<br />

105 nuage_app.html_couple (html_file2, erreur, zoom = 4)<br />

106<br />

159


107<br />

n correction exemple E12<br />

⊓⊔<br />

160


13 Optimisation <strong>de</strong> Newton, métho<strong>de</strong>s du gradi<strong>en</strong>t<br />

13.1 Enoncé E13<br />

Lorsqu'on doit trouver le minimum d'une fonction dénie sur un espace vectoriel, le premier réexe consiste<br />

à dériver la fonction puis à l'annuler <strong>de</strong> manière à obt<strong>en</strong>ir un système d'équation. Il reste <strong>en</strong>suite à<br />

déterminer si les zéros <strong>de</strong> la dérivée correspon<strong>de</strong>nt à un minimum ou à un maximum. Cep<strong>en</strong>dant, il n'est<br />

pas toujours possible <strong>de</strong> résoudre le système d'équation obt<strong>en</strong>u. Par exemple, la fonction f(x) = cos(x)+e x<br />

a pour dérivée f ′ (x) = − sin(x) = e x . Résoudre l'équation f ′ (x) = 0 est impossible.<br />

C'est pourquoi il existe <strong>de</strong>s métho<strong>de</strong>s <strong>de</strong> résolution approchées qui détermin<strong>en</strong>t numériquem<strong>en</strong>t le minimum<br />

<strong>de</strong> la fonction. Il existe <strong>de</strong>s algorithmes permettant <strong>de</strong> trouver une solution approchée à condition toutefois<br />

que la fonction à maximiser ou minimiser soit dérivable. Plusieurs variantes sont proposées regroupées<br />

sous le terme <strong>de</strong> <strong>de</strong>sc<strong>en</strong>te <strong>de</strong> gradi<strong>en</strong>t ou métho<strong>de</strong> <strong>de</strong> Newton. Par la suite, on cherchera à minimiser une<br />

fonction g. Maximiser cette même fonction revi<strong>en</strong>t à minimiser la fonction −g.<br />

13.1.1 Algorithme et converg<strong>en</strong>ce<br />

Soit g : R d −→ R une fonction dérivable dont il faut trouver x ∗ = arg min g (x). L'algorithme suivant<br />

x∈R<br />

propose une métho<strong>de</strong> permettant à partir d'un x 0 ∈ R d quelconque <strong>de</strong> se rapprocher petit à petit du<br />

minimum x ∗ comme le montre le schéma 13.1 dans le cas où g (x) = x 2 .<br />

Algorithme 13.1 : <strong>de</strong>sc<strong>en</strong>te <strong>de</strong> gradi<strong>en</strong>t <strong>de</strong> Newton<br />

Soit g : R d −→ R une fonction dérivable et minorée. On cherche à déterminer le minimum <strong>de</strong> la<br />

fonction g. Soit x 0 ∈ R d . Soit (ɛ t ) t0<br />

une suite réelle positive vériant :<br />

Etape A : initialisation<br />

t ←− 0<br />

∞∑<br />

ɛ t = ∞ et<br />

t=0<br />

∞∑<br />

ɛ 2 t < ∞<br />

Etape B : mise à jour<br />

t ←− t + 1<br />

x t ←−<br />

∂g<br />

x t−1 − ɛ t ∂x (x t)<br />

Etape C : condition d'arrêt<br />

Si f(x t ) ≈ f(x t−1 ), l'algorithme s'arrête, sinon on retourne à l'étape B.<br />

t=0<br />

L'étape C détermine si l'algorithme doit continuer à chercher le minimum <strong>de</strong> la fonction g ou si la valeur<br />

approchée est satisfaisante puisque, aux al<strong>en</strong>tours <strong>de</strong> ce minimum, le gradi<strong>en</strong>t est presque nul et la suite (x t )<br />

presque constante. La condition f(x t ) ≈ f(x t−1 ) peut par exemple être interprétée comme une diér<strong>en</strong>ce<br />

relative d'une itération à la suivante : ∣ f(xt)−f(x t−1)<br />

f(x t−1 )<br />

∣ < A où A est une constante positive petite. Plus elle<br />

est petite, plus la précision sera gran<strong>de</strong>. Il est aussi possible d'arrêter l'algorithme dès que f(x t ) > f(x t−1 )<br />

mais la suite (f(x t )) t<br />

n'est pas toujours décroissante, c'est pourquoi la condition d'arrêt précé<strong>de</strong>nte est<br />

préférable.<br />

La suite (ɛ t ) t<br />

doit vérier quelques contraintes comme la suite ɛ t = ɛ 0<br />

1+t<br />

. Ces contraintes assur<strong>en</strong>t la<br />

161


On note x t l'abscisse à l'itération t.<br />

On note ∂g (x t)<br />

le gradi<strong>en</strong>t <strong>de</strong> g (x) = x 2 .<br />

∂x<br />

L'abscisse à l'itération<br />

[ ]<br />

t + 1 sera :<br />

∂g (xt )<br />

x t+1 = x t − ε t<br />

∂x<br />

ε t est le pas <strong>de</strong> gradi<strong>en</strong>t à l'itération t.<br />

Fig. 13.1 Minimisation par <strong>de</strong>sc<strong>en</strong>te <strong>de</strong> gradi<strong>en</strong>t. A chaque itération, on se déplace dans le s<strong>en</strong>s opposé<br />

à celui du gradi<strong>en</strong>t, direction le minimum est susceptible <strong>de</strong> se trouver.<br />

converg<strong>en</strong>ce <strong>de</strong> l'algorithme vers un minimum <strong>de</strong> la fonction comme le montre le théorème suivant.<br />

Théorème 13.2 : converg<strong>en</strong>ce <strong>de</strong> la métho<strong>de</strong> <strong>de</strong> Newton (Bottou1991)<br />

Soit une fonction continue g : W ∈ R M −→ R, <strong>de</strong> classe C 1 . On suppose les hypothèses suivantes<br />

vériées :<br />

H1 arg min g (W ) = {W ∗ } est un singleton<br />

W ∈R q<br />

[<br />

H2 ∀ε > 0, inf (W − W ∗ ) ′ .∇g (W ) ] > 0<br />

|W −W ∗ |>ε<br />

H3 ∃ (A, B) ∈ R 2 tels que ∀W ∈ R p , ‖∇g (W )‖ 2 A 2 + B 2 ‖W − W ∗ ‖ 2<br />

H4 la suite (ε t ) t0<br />

vérie, ∀t > 0, ε t ∈ R ∗ + et<br />

∑<br />

ε t = +∞,<br />

t0<br />

Alors la suite (W t ) t0<br />

construite <strong>de</strong> la manière suivante :<br />

vérie<br />

lim W t = W ∗<br />

t−→+∞<br />

∑<br />

ε 2 t < +∞<br />

t0<br />

W 0 ∈ R M et ∀t 0, W t+1 = W t − ε t ∇g (W t )<br />

L'hypothèse H1 implique que le minimum <strong>de</strong> la fonction g est unique et l'hypothèse H2 implique que le<br />

<strong>de</strong>mi-espace déni par l'opposé du gradi<strong>en</strong>t conti<strong>en</strong>ne toujours le minimum <strong>de</strong> la fonction g.<br />

Démonstration (théorème 13.2) :<br />

Partie A (démonstration <strong>de</strong> 13.2)<br />

Soit la suite u t = ln ( 1 + ε 2 t x 2) avec x ∈ R, comme ∑ ε 2 t < +∞, u t ∼ ε 2 t x 2 , on a ∑ t < +∞<br />

t0<br />

t0u<br />

Par conséqu<strong>en</strong>t, si v t = e ut<br />

alors T ∏<br />

T →∞<br />

v t<br />

t=1<br />

Partie B (démonstration <strong>de</strong> 13.2)<br />

On pose h t = ‖W t − W ∗ ‖ 2<br />

Donc :<br />

−→ D ∈ R<br />

162


h t+1 − h t = ‖W t − ε t ∇g (W t ) − W ∗ ‖ 2 − ‖W t − W ∗ ‖ 2 (13.1)<br />

Par conséqu<strong>en</strong>t :<br />

D'où :<br />

h t+1 − h t = −2ε t (W t − W ∗ ) ′ ∇g (W t ) + ε 2 t ‖ ∇C (W<br />

} {{ }<br />

t )‖ 2 ε 2 t ‖ ∇g (W t )‖ 2 ε 2 t<br />

>0<br />

(<br />

A 2 + B 2 h t<br />

)<br />

(<br />

h t+1 − h t 1 + ε<br />

2<br />

t B 2) ε 2 t A 2<br />

t∏ (<br />

On pose π t = 1 + ε<br />

2<br />

k<br />

B 2) −1 alors, <strong>en</strong> multipliant <strong>de</strong>s <strong>de</strong>ux côtés par πt+1 , on obti<strong>en</strong>t :<br />

k=1<br />

π t+1 h t+1 − π t h t ε 2 t A 2 π t+1<br />

q∑<br />

d'où π q+1 h q+1 − π p h p ε 2 t A 2 π t+1 <br />

t=p<br />

q∑<br />

ε 2 t A 2 Π <br />

t=p<br />

q∑<br />

t=p<br />

ε 2 t A 2 Π −→<br />

t−→∞<br />

0<br />

Comme la série ∑ t<br />

(π t+1 h t+1 − π t h t ) vérie le critère <strong>de</strong> Cauchy, elle est converg<strong>en</strong>te. Par conséqu<strong>en</strong>t :<br />

lim π q+1h q+1 = 0 = lim Πh q+1<br />

q→∞ q→∞<br />

D'où :<br />

lim h q = 0 (13.2)<br />

q→∞<br />

Partie C (démonstration <strong>de</strong> 13.2)<br />

La série ∑ (h t+1 − h t ) est converg<strong>en</strong>te car Πh t ∼ π t h t .<br />

t<br />

∑<br />

ε 2 t ‖ ∇g (W t )‖ 2 l'est aussi (d'après H3).<br />

t0<br />

D'après (13.1), la série<br />

t0ε ∑ t (W t − W ∗ ) ′ ∇g (W t ) est donc converg<strong>en</strong>te. Or d'après les hypothèses (H2,<br />

H4), elle ne peut l'être que si :<br />

lim W t = W ∗ (13.3)<br />

t→∞<br />

(13.2) ⊓⊔<br />

Ce théorème peut être ét<strong>en</strong>du dans le cas où la fonction g n'a plus un seul minimum global mais plusieurs<br />

minima locaux (voir [Bottou1991]), dans ce cas, la suite (W t ) converge vers un mimimum local. Une<br />

généralisation <strong>de</strong> ce théorème est prés<strong>en</strong>tée dans [Driancourt1996].<br />

Si ce théorème prouve la converg<strong>en</strong>ce <strong>de</strong> la métho<strong>de</strong> <strong>de</strong> Newton, il ne précise pas à quelle vitesse cette<br />

converg<strong>en</strong>ce s'eectue et celle-ci peut parfois être très l<strong>en</strong>te. Plusieurs variantes ont été développées regroupées<br />

sous le terme <strong>de</strong> métho<strong>de</strong>s <strong>de</strong> quasi-Newton, ou métho<strong>de</strong>s du second ordre, dans le but d'améliorer<br />

la vitesse <strong>de</strong> converg<strong>en</strong>ce.<br />

163


13.1.2 Métho<strong>de</strong> du second ordre<br />

L'algorithme 13.1 fournit le canevas <strong>de</strong>s métho<strong>de</strong>s d'optimisation du second ordre. Seule la mise à jour<br />

<strong>de</strong>s coeci<strong>en</strong>ts (étape B) est diér<strong>en</strong>te : elle pr<strong>en</strong>d <strong>en</strong> compte les <strong>de</strong>rnières valeurs <strong>de</strong>s coeci<strong>en</strong>ts ainsi<br />

que les <strong>de</strong>rniers gradi<strong>en</strong>ts calculés. Ce passé va être utilisé pour estimer une direction <strong>de</strong> recherche pour le<br />

minimum diér<strong>en</strong>te <strong>de</strong> celle du gradi<strong>en</strong>t, cette direction est appelée gradi<strong>en</strong>t conjugué (voir [Moré1977]).<br />

La gure 13.2 est couramm<strong>en</strong>t employée pour illustrer l'intérêt <strong>de</strong>s métho<strong>de</strong>s d'optimisation du second<br />

ordre ou métho<strong>de</strong> <strong>de</strong> gradi<strong>en</strong>t conjugué. Le problème consiste à trouver le minimum d'une fonction quadratique,<br />

par exemple, G (x, y) = 3x 2 + y 2 . Tandis que le gradi<strong>en</strong>t est orthogonal aux lignes <strong>de</strong> niveaux <strong>de</strong><br />

la fonction G, le gradi<strong>en</strong>t conjugué se dirige plus sûrem<strong>en</strong>t vers le minimum global.<br />

Ces techniques sont basées sur une approximation du second <strong>de</strong>gré <strong>de</strong> la fonction à minimiser. On note<br />

toujours g : R d −→ R la fonction à minimiser. Au voisinage <strong>de</strong> x 0 , un développem<strong>en</strong>t limité donne :<br />

g (x) = g (x 0 ) + ∂g (x 0)<br />

∂x<br />

(x − x 0 ) + (x − x 0 ) ′ ∂ 2 g (x 0 )<br />

∂x 2 (x − x 0 ) + o ‖x − x 0 ‖ 2<br />

Par conséqu<strong>en</strong>t, sur un voisinage <strong>de</strong> x 0 , la fonction g (x) admet un minimum local si ∂2 g(x 0 )<br />

est dénie<br />

∂x 2<br />

positive strictem<strong>en</strong>t 7 . Une matrice symétrique dénie strictem<strong>en</strong>t positive est inversible, et le minimum<br />

est atteint pour la valeur :<br />

x min = x 0 + 1 2<br />

[ ∂ 2 ] −1 [ ]<br />

g (x 0 ) ∂g (x0 )<br />

∂x 2 ∂x<br />

(13.4)<br />

Néanmoins, pour un réseau <strong>de</strong> neurones, le calcul <strong>de</strong> la dérivée secon<strong>de</strong> est coûteux, son inversion égalem<strong>en</strong>t.<br />

C'est pourquoi les <strong>de</strong>rnières valeurs <strong>de</strong>s coeci<strong>en</strong>ts et du gradi<strong>en</strong>t sont utilisées an d'approcher cette<br />

dérivée secon<strong>de</strong> ou directem<strong>en</strong>t son inverse. Une métho<strong>de</strong> couramm<strong>en</strong>t employée est l'algorithme BFGS<br />

(Broy<strong>de</strong>n-Fletcher-Goldfarb-Shano, voir [Broy<strong>de</strong>n1967], [Fletcher1993]).<br />

Ces métho<strong>de</strong>s propos<strong>en</strong>t une estimation <strong>de</strong> la dérivée secon<strong>de</strong> (ou <strong>de</strong> son inverse) utilisée <strong>en</strong> (13.4). Dans<br />

les métho<strong>de</strong>s du premier ordre, une itération permet <strong>de</strong> calculer les poids x t+1 à partir <strong>de</strong>s poids x t et du<br />

gradi<strong>en</strong>t g t . Si ce gradi<strong>en</strong>t est petit, on peut supposer que g t+1 est presque égal au produit <strong>de</strong> la dérivée<br />

secon<strong>de</strong> par g t . Cette relation est mise à prot pour construire une estimation <strong>de</strong> la dérivée secon<strong>de</strong>. Cette<br />

matrice notée B t dans l'algorithme 13.3 est d'abord supposée égale à l'i<strong>de</strong>ntité puis actualisée à chaque<br />

7 Rappel : ∂2 g (x 0)<br />

∂x 2 est dénie positive strictem<strong>en</strong>t ⇐⇒ ∀Z ∈ R N , Z ≠ 0 =⇒ Z ′ ∂ 2 g (x 0)<br />

∂x 2 Z > 0<br />

164


itération <strong>en</strong> t<strong>en</strong>ant <strong>de</strong> l'information apportée par chaque déplacem<strong>en</strong>t.<br />

Algorithme 13.3 : algorithme BFGS<br />

Soit g : R d −→ R une fonction dérivable et minorée. On cherche à déterminer le minimum <strong>de</strong><br />

la fonction g. Soit x 0 ∈ R d . Le nombre <strong>de</strong> paramètres <strong>de</strong> la fonction f est d. n est un <strong>en</strong>tier<br />

strictem<strong>en</strong>t positif.<br />

Etape A : initialisation<br />

Le premier jeu <strong>de</strong> coeci<strong>en</strong>ts x 0 du réseau <strong>de</strong> neurones est choisi aléatoirem<strong>en</strong>t.<br />

t ←− 0<br />

E 0 ←− f(x 0 )<br />

B 0 ←− I d<br />

i ←− 0<br />

Etape B : calcul du gradi<strong>en</strong>t<br />

g t ←− ∂f<br />

∂x (x t)<br />

c t ←− B t g t<br />

Etape C : recherche <strong>de</strong> ɛ ∗<br />

ɛ ∗ ←− ɛ 0<br />

faire<br />

ɛ<br />

∗<br />

←− ɛ∗ 2<br />

x t+1 ←− x t − ɛ ∗ c t<br />

tant que (f xt+1 E t et ɛ ∗ ≫ 0)<br />

si ɛ ∗ ≈ 0 et B t ≠ I d<br />

B t ←− I d<br />

i ←− t<br />

retour à l'étape B.<br />

n si<br />

Etape D : mise à jour <strong>de</strong>s coeci<strong>en</strong>ts<br />

x t+1 ←− x t − ɛ ∗ c t<br />

E t+1 ←− f(x t+1 )<br />

t ←− t + 1<br />

Etape E : mise à jour <strong>de</strong> la matrice B t<br />

si t − i nd ou g ′ t−1 B t−1g t−1 0 ou g ′ t−1 B t−1 (g t − g t−1 ) 0<br />

B t ←− I d<br />

i ←− t<br />

sinon<br />

st ←− x t − x t−1<br />

d t ←− g t − g t−1 (<br />

)<br />

B t ←− B t−1 + 1 + d′ tB t−1 d t st s ′ t<br />

d ′ t s t s ′ t d t<br />

n si<br />

Etape F : terminaison<br />

si<br />

− s td ′ tB t−1 + B t−1 d t s ′ t<br />

d ′ t s t<br />

E t<br />

E t−1<br />

≈ 1 alors l'appr<strong>en</strong>tissage a convergé sinon retour à l'étape B.<br />

Lorsque la matrice B t est égale à l'i<strong>de</strong>ntité (I d ), le gradi<strong>en</strong>t conjugué est égal au gradi<strong>en</strong>t. Au fur et à mesure<br />

<strong>de</strong>s itérations, cette matrice toujours symétrique évolue <strong>en</strong> améliorant la converg<strong>en</strong>ce <strong>de</strong> l'optimisation.<br />

Néanmoins, la matrice B t doit être "nettoyée" (égale à l'i<strong>de</strong>ntité) <strong>fr</strong>équemm<strong>en</strong>t an d'éviter qu'elle n'agrège<br />

165


un passé trop lointain. Elle est aussi nettoyée lorsque le gradi<strong>en</strong>t conjugué semble trop s'éloigner du<br />

véritable gradi<strong>en</strong>t et <strong>de</strong>vi<strong>en</strong>t plus proche d'une direction perp<strong>en</strong>diculaire.<br />

L'algorithme DFP (Davidon-Fletcher-Powell, voir [Davidon1959], [Fletcher1963]) est aussi un algorithme<br />

<strong>de</strong> gradi<strong>en</strong>t conjugué qui propose une approximation diér<strong>en</strong>te <strong>de</strong> l'inverse <strong>de</strong> la dérivée secon<strong>de</strong>. Pour<br />

appliquer cette métho<strong>de</strong>, il sut <strong>de</strong> remplacer la mise à jour <strong>de</strong> la matrice B t dans l'étape E <strong>de</strong> l'algorithme<br />

13.3 par la suivante :<br />

13.1.3 Minimisation avec gradi<strong>en</strong>t stochastique<br />

B t ←− B t−1 + d td ′ t<br />

d ′ t s − B t−1s t s ′ tB t−1<br />

t s ′ t B (13.5)<br />

t−1s t<br />

La métho<strong>de</strong> du gradi<strong>en</strong>t stochastique s'applique lorsque la fonction à minimiser est une somme <strong>de</strong> fonctions<br />

dérivables et minorées.<br />

g(x) =<br />

N∑<br />

e i (x) (13.6)<br />

i=1<br />

Compte t<strong>en</strong>u <strong>de</strong>s courbes d'erreurs très "acci<strong>de</strong>ntées" (gure 13.3) <strong>de</strong>ssinées par ces fonctions, il existe une<br />

multitu<strong>de</strong> <strong>de</strong> minima locaux. De ce fait, l'appr<strong>en</strong>tissage global converge rarem<strong>en</strong>t vers le minimum global<br />

<strong>de</strong> la fonction d'erreur lorsqu'on applique les algorithmes basés sur le gradi<strong>en</strong>t global. L'optimisation avec<br />

gradi<strong>en</strong>t stochastique est une solution permettant <strong>de</strong> mieux explorer ces courbes d'erreurs.<br />

De plus, les métho<strong>de</strong>s <strong>de</strong> gradi<strong>en</strong>t conjugué nécessite le stockage d'une matrice trop gran<strong>de</strong> parfois pour<br />

<strong>de</strong>s fonctions ayant quelques milliers <strong>de</strong> paramètres (g : R 1000 −→ R). C'est pourquoi l'appr<strong>en</strong>tissage avec<br />

gradi<strong>en</strong>t stochastique est souv<strong>en</strong>t préféré à <strong>de</strong>s métho<strong>de</strong>s du second ordre trop coûteuses <strong>en</strong> calcul et <strong>en</strong><br />

espace mémoire. En contrepartie, la converg<strong>en</strong>ce est plus l<strong>en</strong>te. La démonstration <strong>de</strong> cette converg<strong>en</strong>ce<br />

nécessite l'utilisation <strong>de</strong> quasi-martingales et est une converg<strong>en</strong>ce presque sûre (voir [Bottou1991]).<br />

166


Algorithme 13.4 : optimisation stochastique<br />

∑<br />

Soit g = N e i (x), ∀i, e i : R d −→ R une fonction dérivable et minorée. On cherche à déterminer<br />

i=1<br />

le minimum <strong>de</strong> la fonction g. Soit x 0 ∈ R d . Soit (ɛ t ) t0<br />

une suite réelle positive vériant :<br />

Etape A : initialisation<br />

t ←− 0<br />

∑<br />

E 0 ←− N e i (x 0 )<br />

i=1<br />

Etape B : récurr<strong>en</strong>ce<br />

x t,0 ←− W 0<br />

pour t ′ = 0 à N − 1 faire<br />

∞∑<br />

ɛ t = ∞ et<br />

t=0<br />

∞∑<br />

ɛ 2 t < ∞<br />

t=0<br />

i ←− nombre aléatoire dans {1, . . . , N}<br />

g ←− ∂e (<br />

i xt,t ′)<br />

∂x<br />

x t,t ′ +1 ←− x t,t ′ − ɛ t g<br />

n pour<br />

x t+1 ←− x t,N<br />

E t+1 ←−<br />

∑ N e i (x t+1 )<br />

i=1<br />

t ←− t + 1<br />

Etape C : terminaison<br />

si<br />

E t<br />

E t−1<br />

≈ 1 alors l'appr<strong>en</strong>tissage a convergé sinon retour à l'étape B.<br />

En pratique, il est utile <strong>de</strong> converser le meilleur jeu <strong>de</strong> coeci<strong>en</strong>ts : x ∗ = arg min<br />

u0<br />

n'est pas une suite constamm<strong>en</strong>t décroissante.<br />

13.1.4 Premier exemple : optimisation d'une fonction quadratique<br />

E u car la suite (E u ) u0<br />

La gure 13.2 illustre un exemple pour lequel l'utilisation d'un algorithme d'optimisation du second <strong>de</strong>gré<br />

est pertin<strong>en</strong>te et pour le vérier concrètem<strong>en</strong>t, on choisit la fonction suivante :<br />

f(x, y) = x 4 + x 3 + y2 + y<br />

100<br />

(13.7)<br />

Le miminum est atteint pour (x, y) = ( − 3 4 , − 1 2)<br />

. Toutefois, du fait du coeci<strong>en</strong>t très faible <strong>de</strong>vant les<br />

termes <strong>en</strong> y 2 et y, l'utilisation d'un algorithme <strong>de</strong> <strong>de</strong>sc<strong>en</strong>te <strong>de</strong> gradi<strong>en</strong>t du premier ordre doit converger très<br />

l<strong>en</strong>tem<strong>en</strong>t contrairem<strong>en</strong>t à une métho<strong>de</strong> du second ordre, ce que le programme décrit au paragraphe 13.2.1<br />

montrera numériquem<strong>en</strong>t.<br />

13.1.5 Court rappel sur l'estimateur du maximum <strong>de</strong> vraisemblance<br />

La métho<strong>de</strong> <strong>de</strong> l'estimateur du maximum <strong>de</strong> vraisemblance est une métho<strong>de</strong> permettant d'estimer les<br />

paramètres d'une <strong>de</strong>nsité. Par exemple, on mesure les tailles d'une tr<strong>en</strong>taine <strong>de</strong> personnes pour obt<strong>en</strong>ir un<br />

échantillon (X 1 , . . . , X n ). On calcule pour cet échantillon l'espérance (ou la moy<strong>en</strong>ne) et l'écart-type :<br />

167


µ = E (X) = 1 n<br />

n∑<br />

X i et σ = √ V (X) = √ 1 n<br />

i=1<br />

n∑<br />

(X i − E (X)) 2 (13.8)<br />

Ces valeurs apparaiss<strong>en</strong>t souv<strong>en</strong>t <strong>en</strong> statistiques, elles provi<strong>en</strong>n<strong>en</strong>t d'une hypothèse faite sur la loi que<br />

suiv<strong>en</strong>t les variables <strong>de</strong> l'échantillon (X 1 , . . . , X n ) : une loi normale <strong>de</strong> moy<strong>en</strong>ne µ et d'écart-type σ. On<br />

suppose alors que toutes les variables X i sont indép<strong>en</strong>dantes et suiv<strong>en</strong>t la même loi normale <strong>de</strong> moy<strong>en</strong>ne<br />

µ et d'écart-type σ pour l'instant inconnus. Cela signie aussi que la <strong>de</strong>nsité <strong>de</strong> la variable X i a pour<br />

expression :<br />

i=1<br />

( )<br />

f(x) = √ 1 (x − µ)2<br />

exp − 2πσ 2σ 2<br />

(13.9)<br />

Cette expression permet par exemple <strong>de</strong> calculer la probabilité d'un événem<strong>en</strong>t comme le fait que les tailles<br />

<strong>de</strong>s n personnes soi<strong>en</strong>t inférieures à 1,80 mètres et supérieures à 1,60 mètres. Notons A cet événem<strong>en</strong>t.<br />

P (A) = P (1, 6 X 1 1, 8 et ... et 1, 6 X n 1, 8) (13.10)<br />

Etant donné que les variables <strong>de</strong> l'échantillon sont toutes indép<strong>en</strong>dantes. Il est possible d'écrire que :<br />

P (A) = P (1, 6 X 1 1, 8) × ... × P (1, 6 X n 1, 8) =<br />

On utilise maint<strong>en</strong>ant le fait que X i a pour <strong>de</strong>nsité f :<br />

P (A) =<br />

=<br />

n∏<br />

[∫ 1,8<br />

i=1<br />

1,6<br />

∫ 1,8 ∫ 1,8<br />

1,6<br />

...<br />

]<br />

f(x)dx =<br />

1,6<br />

i=1<br />

1<br />

√<br />

2πσ<br />

exp<br />

1,6<br />

n∏<br />

P (1, 6 X i 1, 8) (13.11)<br />

i=1<br />

[<br />

n∏ ∫ ( ) ]<br />

1,8<br />

1 (x − µ)2<br />

√ exp − 2πσ 2σ 2 dx<br />

(<br />

)<br />

(<br />

− (x 1 − µ) 2 1<br />

2σ 2 ... √ exp 2πσ<br />

(13.12)<br />

− (x n − µ) 2<br />

2σ 2 )<br />

dx 1 ...dx n (13.13)<br />

Cette <strong>de</strong>rnière écriture fait référ<strong>en</strong>ce à la <strong>de</strong>nsité d'un vecteur <strong>de</strong> variables aléatoires et indép<strong>en</strong>dantes<br />

comme par exemple le vecteur (X 1 , . . . , X n ). Puisque la <strong>de</strong>nsité d'une variable X i est f(x) =<br />

√ 1<br />

2πσ<br />

exp<br />

(−<br />

), (x−µ)2 la <strong>de</strong>nsité <strong>de</strong> l'échantillon (X<br />

2σ 2 1 , . . . , X n ) est :<br />

f (x 1 , ..., x n ) =<br />

n∏<br />

f(x i ) (13.14)<br />

i=1<br />

[ ]<br />

1 n<br />

= √<br />

2πσ<br />

n<br />

∏<br />

i=1<br />

exp<br />

(<br />

− (x n − µ) 2<br />

2σ 2 )<br />

(13.15)<br />

On utilise pour cela le fait que la <strong>de</strong>nsité d'un <strong>en</strong>semble <strong>de</strong> variables indép<strong>en</strong>dantes est le produit <strong>de</strong>s<br />

<strong>de</strong>nsités. En fait, cela revi<strong>en</strong>t à construire la probabilité innitésimale dP correspondant au fait que chaque<br />

168


variables X i apparti<strong>en</strong>t à l'intervalle [x i , x i + dx i ]. A partir <strong>de</strong> cette <strong>de</strong>nsité, on construit la vraisemblance 8<br />

L égale à la valeur <strong>de</strong> la <strong>de</strong>nsité f obt<strong>en</strong>ue <strong>en</strong> remplaçant les x i par les valeurs observées <strong>de</strong>s variables X i .<br />

[ ]<br />

1 n<br />

L (X 1 , ..., X n ) = √<br />

2πσ<br />

n<br />

∏<br />

i=1<br />

exp<br />

(<br />

− (X n − µ) 2<br />

2σ 2 )<br />

(13.16)<br />

Plutôt que d'avoir un produit, on préfère la log-vraisemblance (ou log-likelihood) :<br />

[ ]<br />

1<br />

log L (X 1 , ..., X n ) = n log √ + 2πσ<br />

[ ]<br />

1<br />

= n log √<br />

2πσ<br />

= n log<br />

(<br />

)<br />

n∑<br />

− (X n − µ) 2<br />

2σ 2<br />

i=1<br />

− 1<br />

2σ 2<br />

n∑<br />

(X n − µ) 2<br />

i=1<br />

1<br />

√ − n log σ − 1<br />

2π 2σ 2<br />

(13.17)<br />

n∑<br />

(X n − µ) 2 (13.18)<br />

On cherche maint<strong>en</strong>ant à maximiser cette fonction par rapport à µ et σ. En dérivant et <strong>en</strong> cherchant à<br />

annuler les dérivées partielles, on obti<strong>en</strong>t le système d'équations suivant :<br />

i=1<br />

⎧<br />

⎪⎨<br />

⎪⎩<br />

∂L<br />

∂µ = 1 n∑<br />

σ 2 (X i − µ) = 0<br />

i=1<br />

∂L<br />

∂σ = −n σ + 1 n∑<br />

σ 3 (X i − µ) 2 = 0<br />

i=1<br />

(13.19)<br />

On résoud d'abord la première équation puis la secon<strong>de</strong> pour obt<strong>en</strong>ir :<br />

µ ∗ = 1 n<br />

n∑<br />

X i et (σ ∗ ) 2 = 1 n<br />

i=1<br />

n∑<br />

(X i − µ) 2 (13.20)<br />

On retrouve la moy<strong>en</strong>ne et la variance <strong>de</strong> l'échantillon (X 1 , . . . , X n ). La loi normale la plus appropriée pour<br />

modéliser cet échantillon a pour paramètre µ ∗ et σ ∗ . Et la loi la plus appropriée est celle qui maximise<br />

l'expression (13.16). La gure 13.4 suggère que la courbe qui maximise cette expression est celle qui<br />

épouse au mieux la forme <strong>de</strong> l'histogramme <strong>de</strong> l'échantillon 9 . Si cet histogramme est l'<strong>en</strong>semble <strong>de</strong>s couples<br />

(t j , h j ) 1jk<br />

, on peut presque écrire que :<br />

i=1<br />

n∏<br />

f (X i ) ≈<br />

i=1<br />

k∏<br />

[f (t j )] h j<br />

(13.21)<br />

j=1<br />

8 La vraisemblance est souv<strong>en</strong>t noté L pour likelihood <strong>en</strong> anglais.<br />

Un histogramme <strong>de</strong> l'échantillon (X 1, . . . , X n) est une liste <strong>de</strong> couples <strong>de</strong> valeurs (t j, h j) 1jk<br />

où (t j) j est une suite<br />

croissante <strong>de</strong> R et h j est un nombre <strong>en</strong>tier positif égal au nombre <strong>de</strong> variables X i appart<strong>en</strong>ant à l'intervalle [t j, t j+1[.<br />

Autrem<strong>en</strong>t dit :<br />

h j = 1 n∑<br />

1<br />

n {Xi ∈[t j , t j+1 [}<br />

i=1<br />

169


Cette <strong>de</strong>rnière expression montre que la fonction f doit éviter d'être nulle <strong>en</strong> t j lorsque h j n'est pas nulle.<br />

C'est pour cela que la meilleure courbe f est celle qui épouse au mieux la forme <strong>de</strong> l'histogramme. Lorsqu'on<br />

cherche les meilleurs paramètres d'une <strong>de</strong>nsité à l'ai<strong>de</strong> <strong>de</strong> la métho<strong>de</strong> <strong>de</strong> l'estimateur <strong>de</strong> maximum <strong>de</strong><br />

vraisemblance, cela revi<strong>en</strong>t à choisir une forme <strong>de</strong> <strong>de</strong>nsité 10 et à chercher les paramètres qui font que cette<br />

courbe épouse au mieux la vraie distribution <strong>de</strong>s variables observées. L'histogramme est une approximation<br />

<strong>de</strong> cette distribution.<br />

L'estimateur du maximum <strong>de</strong> vraisemblance est ce qu'on appelle une métho<strong>de</strong> paramétrique. Il existe <strong>de</strong>s<br />

métho<strong>de</strong>s non paramétriques comme la métho<strong>de</strong> <strong>de</strong>s noyaux qui ne fait pas d'hypothèse sur la forme <strong>de</strong><br />

la <strong>de</strong>nsité. D'autres métho<strong>de</strong>s <strong>en</strong>core ne s'intéress<strong>en</strong>t qu'à une partie <strong>de</strong> la <strong>de</strong>nsité, plus précisém<strong>en</strong>t les<br />

queues <strong>de</strong> distributions.<br />

Par exemple, lors <strong>de</strong> la construction d'une plateforme pétrolière, il est important <strong>de</strong> connaître la hauteur<br />

<strong>de</strong> la plus gran<strong>de</strong> vague pour placer la plateforme juste au-<strong>de</strong>ssus. Le coût <strong>de</strong> construction est plus élevé<br />

lorsque la plateforme est surélevée mais elle est hors <strong>de</strong> portée <strong>de</strong>s vagues qui ne peuv<strong>en</strong>t plus l'emporter.<br />

A partir <strong>de</strong> séries <strong>de</strong> mesures <strong>de</strong> hauteurs <strong>de</strong> vagues, les statistici<strong>en</strong>s s'intéress<strong>en</strong>t non plus à la distribution<br />

<strong>de</strong> cette hauteur mais à la distribution <strong>de</strong> la hauteur maximale, c'est-à-dire la probabilité d'événem<strong>en</strong>ts<br />

très rares.<br />

La métho<strong>de</strong> <strong>de</strong> l'estimateur du maximum <strong>de</strong> vraisemblance n'est plus applicable lorsqu'on s'intéresse aux<br />

queues <strong>de</strong> distributions. La <strong>de</strong>nsité calculée par ce moy<strong>en</strong> est proche <strong>de</strong> la véritable distribution à proximité<br />

<strong>de</strong> la moy<strong>en</strong>ne, là où il y a beaucoup <strong>de</strong> données. Mais elle est peu précise lorsqu'il y a peu <strong>de</strong> données<br />

car, même si on fait <strong>de</strong>s erreurs, elles sont peu nombreuses.<br />

Les événem<strong>en</strong>ts rares form<strong>en</strong>t une branche à part dans la théorie statistique, elle pr<strong>en</strong>d <strong>de</strong> plus <strong>en</strong> plus<br />

d'importance <strong>en</strong> assurance par exemple car on cherche <strong>de</strong> plus <strong>en</strong> plus à estimer la probabilité d'événem<strong>en</strong>ts<br />

rares comme les catastrophes naturels.<br />

Pour aller plus loin :<br />

L'expression (13.21) équivaut à la suivante :<br />

(13.21) ⇐⇒<br />

n∑<br />

log f (X i ) ≈<br />

i=1<br />

k∑<br />

h i log f (t j ) (13.22)<br />

j=1<br />

Et si on note g la véritable <strong>de</strong>nsité <strong>de</strong> l'échantillon (X 1 , . . . , X n ), l'expression (13.22) converge vers une<br />

limite lorsque n −→ ∞ :<br />

1<br />

n<br />

n∑<br />

i=1<br />

log f (X i ) −→ g(x) log f(x)dx = E g (log f) (13.23)<br />

n→∞<br />

∫R<br />

Or l'expression − ∫ R<br />

g(x) log f(x)dx est aussi appelée distance <strong>de</strong> Kullback-Leiber <strong>en</strong>tre <strong>de</strong>ux <strong>de</strong>nsités. La<br />

10 Une forme <strong>de</strong> <strong>de</strong>nsité ou plus exactem<strong>en</strong>t un modèle, loi normale, loi <strong>de</strong> Poisson, ... une loi dép<strong>en</strong>dant d'un ou plusieurs<br />

paramètres.<br />

170


<strong>de</strong>nsité f cherchée est celle qui minimise cette distance :<br />

Problème 13.5 : distance <strong>de</strong> Kullback-Leiber<br />

Soit l'<strong>en</strong>semble F <strong>de</strong>s fonctions dénies sur l'<strong>en</strong>semble D ⊂ R. Toute fonction f ∈ F vérie<br />

∀x ∈ D, f(x) > 0 et ∫ D f = 1. Soit g ∈ F . On cherche la fonction f ∗ solution du problème :<br />

∫<br />

f ∗ = arg min − g(x) log f(x)dx<br />

f∈F D<br />

Théorème 13.6 : distance <strong>de</strong> Kullback-Leiber<br />

La solution du problème d'optimisation 13.5 est f ∗ = g.<br />

On peut démontrer ce théorème lorsque l'<strong>en</strong>semble D est un sous-<strong>en</strong>semble ni <strong>de</strong> N. La fonction g est<br />

dénie par une liste <strong>de</strong> couple (j, g j ) j∈D<br />

et ∑ j∈D g j = 1. C'est un histogramme. La fonction f est dénie<br />

<strong>de</strong> même : (j, f j ) j∈D<br />

et ∑ j∈D f j = 1. La distance <strong>de</strong> Kullback-Leiber <strong>en</strong>tre f et g s'écrit :<br />

∑<br />

− g j log f j (13.24)<br />

On cherche à démontrer que quelque soit la fonction f choisie, elle vérie :<br />

∑<br />

j∈D<br />

j∈Dg j log f j ∑ j∈D<br />

g j log g j (13.25)<br />

⇐⇒ ∑ j∈Dg j (log f j − log g j ) 0 (13.26)<br />

⇐⇒<br />

∑ g j log f j<br />

0 (13.27)<br />

g j<br />

j∈D<br />

Comme la fonction x −→ log x est concave et que ∑ j∈D g j = 1 :<br />

∑<br />

g j log f j<br />

∑ g j<br />

j∈D<br />

j∈D<br />

( )<br />

f j<br />

log g j = ∑ log f j 0 (13.28)<br />

g j<br />

j∈D<br />

Cette <strong>de</strong>rnière inégalité sut à démontrer que la disbribution qui minimise la distance <strong>de</strong> Kullback-Leiber<br />

à une autre distribution est elle-même. Cette démonstration convi<strong>en</strong>t pour un <strong>en</strong>semble D ni. Il reste à<br />

l'adapter pour un <strong>en</strong>semble dénombrable et plus généralem<strong>en</strong>t pour une <strong>de</strong>nsité dénie sur R, ce qui sort<br />

du cadre <strong>de</strong> ce docum<strong>en</strong>t 11 .<br />

13.1.6 Second exemple : proportion <strong>de</strong> mâles chez les poissons<br />

Un biologiste cherche à étudier la population d'une certaine espèce <strong>de</strong> poissons. Plus exactem<strong>en</strong>t, il voudrait<br />

savoir quelle est la proportion <strong>de</strong> mâles et <strong>de</strong> femelles. Il a réussi à récupérer une base <strong>de</strong> données cont<strong>en</strong>ant<br />

11 Cette démonstration utilise l'inégalité <strong>de</strong> J<strong>en</strong>s<strong>en</strong>. Si f est une fonction convexe sur R et X une variable aléatoire réelle,<br />

alors : E (f(X)) f (E (X)).<br />

171


les tailles <strong>de</strong>s poissons <strong>de</strong> cette espèce capturés par les pêcheurs sur une année <strong>en</strong>tière. Malheureusem<strong>en</strong>t,<br />

les pêcheurs n'ont pas distingué les mâles <strong>de</strong>s femelles. Pour estimer cette proportion, le biologiste dispose<br />

<strong>de</strong> <strong>de</strong>ux informations :<br />

1. La taille moy<strong>en</strong>ne <strong>de</strong>s mâles est diér<strong>en</strong>te <strong>de</strong> la taille moy<strong>en</strong>ne <strong>de</strong>s femelles, elle est même supérieure.<br />

2. La taille <strong>de</strong>s mâles est distribuée selon une loi normale, il <strong>en</strong> est <strong>de</strong> même pour les femelles.<br />

A partir <strong>de</strong> ces <strong>de</strong>ux informations (ou hypothèses), le biologiste suppose que la distribution <strong>de</strong>s tailles <strong>de</strong>s<br />

poissons suit un mélange <strong>de</strong> loi normales :<br />

f(x) =<br />

α m<br />

√<br />

2πσm<br />

exp<br />

(<br />

− (x − µ m) 2<br />

2σ 2 m<br />

)<br />

+ α f<br />

√ exp 2πσf<br />

(<br />

− (x − µ f ) 2<br />

2σ 2 f<br />

)<br />

(13.29)<br />

On sait d'après les hypothèses <strong>de</strong> modélisation que α m + α f = 1, α m > 0, α f > 0 et µ m > µ f . Il reste à<br />

estimer ces paramètres. La métho<strong>de</strong> choisie est celle <strong>de</strong> l'estimateur du maximum <strong>de</strong> vraisemblance. On<br />

note les tailles mesurées par les pêcheurs (X 1 , . . . , X N ). On cherche donc à calculer :<br />

(<br />

α ∗ , µ ∗ m, σm, ∗ µ ∗ f , ) σ∗ f = arg max<br />

α,µ m,σ m,µ f ,σ f<br />

N ∏<br />

i=1<br />

[<br />

(<br />

)<br />

α<br />

√ exp − (X i − µ m ) 2<br />

+<br />

2πσm<br />

On passe au logarithme, il faut donc maximiser la fonction g dénie par :<br />

2σ 2 m<br />

(<br />

) ]<br />

1 − α<br />

√ exp − (X i − µ f ) 2<br />

2πσf 2σf<br />

2 (13.30)<br />

g (α, µ m , σ m , µ f , σ f ) =<br />

[<br />

(<br />

)<br />

N∑ α<br />

log √ exp − (X i − µ m ) 2<br />

2πσm 2σm<br />

2<br />

i=1<br />

(<br />

)]<br />

+ √ 1 − α exp 2πσf<br />

− (X i − µ f ) 2<br />

2σf<br />

2 (13.31)<br />

On note :<br />

[<br />

(<br />

)<br />

α<br />

h (X i , α, µ m , σ m , µ f , σ f ) = log √ exp − (X i − µ m ) 2<br />

2πσm 2σm<br />

2<br />

(<br />

)]<br />

+ √ 1 − α exp − (X i − µ f ) 2<br />

2πσf 2σf<br />

2<br />

An <strong>de</strong> trouver le maximum <strong>de</strong> la fonction h, on calcule les dérivées <strong>de</strong> h par rapport à α, µ m , σ m , µ f , σ f :<br />

172


∂g<br />

∂α (α, µ m, σ m , µ f , σ f ) =<br />

∂g<br />

∂µ m<br />

(α, µ m , σ m , µ f , σ f ) =<br />

∂g<br />

∂µ f<br />

(α, µ m , σ m , µ f , σ f ) =<br />

∂g<br />

∂σ m<br />

(α, µ m , σ m , µ f , σ f ) =<br />

∂g<br />

∂σ f<br />

(α, µ m , σ m , µ f , σ f ) =<br />

N∑<br />

i=1<br />

N∑<br />

i=1<br />

N∑<br />

i=1<br />

N∑<br />

i=1<br />

N∑<br />

i=1<br />

√ 1<br />

2πσm<br />

X i −µ m<br />

σ 2 m<br />

X i −µ f<br />

σ 2 f<br />

)<br />

)<br />

exp<br />

(− (X i−µ m) 2<br />

− 1<br />

2σm<br />

2 √<br />

2πσf<br />

exp<br />

(− (X i−µ f) 2<br />

2σf<br />

2 (13.32)<br />

h (X i , α, µ m , σ m , µ f , σ f )<br />

)<br />

√ α<br />

2πσm<br />

exp<br />

(− (X i−µ m) 2<br />

2σm<br />

2 (13.33)<br />

h (X i , α, µ m , σ m , µ f , σ f )<br />

)<br />

√1−α<br />

2πσf<br />

exp<br />

(− (X i−µ f) 2<br />

2σf<br />

2 (13.34)<br />

h (X i , α, µ m , σ m , µ f , σ f )<br />

)<br />

)<br />

√ α<br />

2πσm<br />

exp<br />

(− (X i−µ m) 2<br />

−<br />

α<br />

2σm<br />

2 √<br />

2πσ 2<br />

exp<br />

(− (X i−µ m) 2<br />

m<br />

2σm<br />

2 (13.35)<br />

h (X i , α, µ m , σ m , µ f , σ f )<br />

)<br />

)<br />

1−α<br />

σf<br />

3 √<br />

2πσf<br />

exp<br />

(− (X i−µ f) 2<br />

−<br />

1−α<br />

2σf<br />

2 √<br />

2πσ 2<br />

exp<br />

(− (X i−µ f) 2<br />

f<br />

2σf<br />

2 (13.36)<br />

h (X i , α, µ m , σ m , µ f , σ f )<br />

(X i −µ m) 2<br />

σ 3 m<br />

(X i −µ f) 2<br />

Annuler ces dérivées est impossible tout comme obt<strong>en</strong>ir la solution exacte correspondant au maximum <strong>de</strong><br />

la fonction f. Il faut utiliser un algorithme approché, par exemple, une métho<strong>de</strong> d'optimisation <strong>de</strong> Newton.<br />

Dans cet exemple, le calcul <strong>de</strong> la proportion <strong>de</strong>s mâles et <strong>de</strong> femelles repos<strong>en</strong>t sur le choix d'un modèle<br />

pour la <strong>de</strong>nsité et d'une métho<strong>de</strong> numérique pour estimer ces paramètres.<br />

Tout d'abord, on vérie que la métho<strong>de</strong> numérique permettant d'estimer les paramètres du modèle (13.29)<br />

permet eectivem<strong>en</strong>t <strong>de</strong> les retrouver dans une situation où ils sont connus. On simule donc un échantillon<br />

<strong>de</strong> tailles <strong>de</strong> poissons générées aléatoirem<strong>en</strong>t selon la <strong>de</strong>nsité (13.29) avec <strong>de</strong>s paramètres α, µ m , σ m , µ f ,<br />

σ f puis on vérie que l'algorithme 13.1 ou 13.3 converg<strong>en</strong>t vers ces paramètres.<br />

Cette première étape, si elle est réussie, vali<strong>de</strong> <strong>en</strong> quelque sorte la méthodologie proposée. Il est alors<br />

<strong>en</strong>visageable <strong>de</strong> considérer que les résultats obt<strong>en</strong>us avec <strong>de</strong>s données réelles seront ables.<br />

Remarque 13.7: Algorithme Expectation-Maximisation<br />

Il existe un autre algorithme permettant d'estimer les paramètres d'une <strong>de</strong>nsité faite d'un mélange<br />

<strong>de</strong> lois normales, c'est l'algorithme Expectation-Maximisation (ou EM) établi par Dempster (voir<br />

[Dempster1977]). Cet algorithme s'applique pour tout modèle cont<strong>en</strong>ant <strong>de</strong>s informations cachées - pour<br />

cet exemple, c'est la proportion <strong>de</strong> mâles et <strong>de</strong> femelles. Il est souv<strong>en</strong>t préféré à un algorithme d'optimisation<br />

classique comme ceux prés<strong>en</strong>tés dans ce chapitre : le paragraphe 13.2.4 montrera que le paramètre<br />

α liée à l'information cachée fait <strong>de</strong> la résistance à l'algorithme 13.1.<br />

13.2 Programme, explications <strong>de</strong> l'exemple E13<br />

13.2.1 Premier exemple, fonction quadratique<br />

On cherche à tester l'algorithme BFGS pour la fonction f(x, y) = x 4 + x 3 + y2 + y<br />

. Pour cela, on crée la<br />

100<br />

classe optimisation_bfgs. Son constructeur pr<strong>en</strong>d <strong>en</strong>tre autres comme paramètres f, <strong>de</strong>rivee, args.<br />

f(x, ...) est la fonction à minimiser, cette fonction retourne la valeur <strong>de</strong> f au point x.<br />

173


<strong>de</strong>rivee(x, ...) retourne un vecteur égal au gradi<strong>en</strong>t <strong>de</strong> la fonction f au point x.<br />

args est une liste d'argum<strong>en</strong>ts supplém<strong>en</strong>taires hormis x dont les fonctions f et <strong>de</strong>rivee ont besoin.<br />

Les <strong>de</strong>ux fonctions f et <strong>de</strong>rivee sont appelées par les métho<strong>de</strong>s erreur et gradi<strong>en</strong>t, elles-mêmes appelées<br />

par la métho<strong>de</strong> optimise qui implém<strong>en</strong>te l'algorithme BFGS 13.3. Toutes ces métho<strong>de</strong>s utilis<strong>en</strong>t l'objet<br />

array du module Numeric qui permet <strong>de</strong> stocker <strong>de</strong>s vecteurs et <strong>de</strong>s matrices. Le module Numeric dénit<br />

égalem<strong>en</strong>t toutes les opérations matricielles dont l'algorithme BFGS a besoin.<br />

Pour appliquer l'algorithme BFGS sur la fonction f(x, y) = x 4 + x 3 + y2 + y<br />

, on dénit les fonctions<br />

100<br />

suivantes :<br />

<strong>de</strong>f fonction (x, a,b,c) :<br />

return x [0]**4 + a * x[0]**3 + b * x[1]**2 + c * x [1]<br />

<strong>de</strong>f <strong>de</strong>rivee (x, a,b,c) :<br />

return Num.array ( [4 * x[0]**3 + 3 * a * x[0]**2, b * 2 * x [1] + c] )<br />

Puis on applique l'algorithme BFGS :<br />

opt = optimisation_bfgs (fonction, <strong>de</strong>rivee, (1,0.01,0.01))<br />

x0 = Num.array ( [ 3, 4] )<br />

x = opt.optimise (x0)<br />

Ces extraits ainsi que l'algorithme BFGS sont prés<strong>en</strong>ts dans le programme qui suit.<br />

1 # -*- coding: cp1252 -*-<br />

2 import numpy as Num # pour les tableaux<br />

3 import psyco<br />

4<br />

5 class optimisation_bfgs (object) :<br />

6 """optimisation à l'ai<strong>de</strong> <strong>de</strong> l'algorithme BFGS"""<br />

7<br />

8 <strong>de</strong>f __init__ (self, f, <strong>de</strong>rivee, args = None, \<br />

9 epsilon = 0.1, erreur = 1e-5, maxiter = 100) :<br />

10 """initialisation<br />

11 @param f fonction à minimiser --> f (x, args)<br />

12 où x est un vecteur (Numeric.array)<br />

13 @param <strong>de</strong>rivee dérivée <strong>de</strong> f --> <strong>de</strong>rivee (x, args)<br />

14 @param args autres paramètres <strong>de</strong> la fonction<br />

15 @param epsilon coeffici<strong>en</strong>t <strong>de</strong>vant le gradi<strong>en</strong>t<br />

16 @param erreur on s'arrête lorsque l'erreur ne décroît plus<br />

17 @param maxiter nombre d'itérations maximum<br />

18 """<br />

19<br />

20 self._f = f<br />

21 self._<strong>de</strong>rivee = <strong>de</strong>rivee<br />

22 self._epsilon = epsilon<br />

23 self._erreur = erreur<br />

24 self._maxiter = maxiter<br />

25 self._args = args<br />

26<br />

174


27 <strong>de</strong>f erreur (self, x) :<br />

28 """calcule l'erreur"""<br />

29 return self._f (x, *self._args)<br />

30<br />

31 <strong>de</strong>f gradi<strong>en</strong>t (self, x) :<br />

32 """calcule le gradi<strong>en</strong>t"""<br />

33 return self._<strong>de</strong>rivee (x, *self._args)<br />

34<br />

35 <strong>de</strong>f i<strong>de</strong>ntite (self, dim) :<br />

36 """retourne la matrice i<strong>de</strong>ntité"""<br />

37 b = Num.array ( [ [ 0 for i in xrange (0, dim) ] for j in xrange (0,dim) ] )<br />

38 for i in xrange (0, dim) :<br />

39 b [(i,i)] = 1<br />

40 return b<br />

41<br />

42 <strong>de</strong>f optimise (self, xO, classic = False, pr = True) :<br />

43 """optimisation selon l'algorithme BFGS,<br />

44 x0 est le vecteur initiale, retourne la meilleure solution,<br />

45 si pr == True, affiche <strong>de</strong>s résultats intermédiaires,<br />

46 classic == True, la matrice bt est mise à jour à chaque itération,<br />

47 équivaut à une <strong>de</strong>sc<strong>en</strong>te <strong>de</strong> gradi<strong>en</strong>t du premier <strong>de</strong>gré"""<br />

48<br />

49 dim = l<strong>en</strong> (self.gradi<strong>en</strong>t (xO))<br />

50 stop = 1e-12<br />

51 err = self.erreur (xO)<br />

52 bt = self.i<strong>de</strong>ntite (dim)<br />

53 id = True<br />

54 t = 0<br />

55 iter = 0<br />

56 old = err * (1.0 + self._erreur * 2) * 2<br />

57 x = xO<br />

58 gt_ = self.gradi<strong>en</strong>t (xO)<br />

59 xt_ = xO<br />

60 nbt = 0<br />

61<br />

62 while (abs ((old - err) / err) > self._erreur or t == 0) and \<br />

63 iter < self._maxiter :<br />

64<br />

65 if pr :<br />

66 print "itération ", iter, " (", \<br />

67 t,nbt, ") \t erreur : ", err, " (", old, ")"<br />

68 iter = iter + 1<br />

69 gt = self.gradi<strong>en</strong>t (x)<br />

70 ct = Num.dot (bt, gt)<br />

71<br />

72 e = abs (err) * 2 + 1<br />

73 eps = self._epsilon * 2<br />

74 while e > err and eps > stop :<br />

75 eps = eps / 2<br />

76 xt = x - ct * eps<br />

175


77 e = self.erreur (xt)<br />

78<br />

79 if eps < 1e-12 and t > 0 :<br />

80 bt = self.i<strong>de</strong>ntite (dim)<br />

81 if t == 0 : nbt += 1<br />

82 else : nbt = 0<br />

83 t = 0<br />

84 else :<br />

85 old = err<br />

86 x = xt<br />

87 err = e<br />

88 if not classic :<br />

89 gt = self.gradi<strong>en</strong>t (x)<br />

90 dt = gt - gt_<br />

91 gbtg = Num.dot (Num.transpose (gt), Num.dot (bt, gt))<br />

92 gbtd = Num.dot (Num.transpose (gt), Num.dot (bt, dt))<br />

93 if (gbtg


127 x0 = Num.array ( [ 3, 4] )<br />

128 print "solution initiale : ", x0<br />

129 x = opt.optimise (x0, classic = False)<br />

130 print "solution finale : ", x<br />

Après exécution, on vérie que la solution trouvée correspond bi<strong>en</strong> à ( − 3 4 , − 1 2)<br />

. Pour comparer avec<br />

l'algorithme d'optimisation du premier <strong>de</strong>gré, on remplace la ligne suivante :<br />

x<br />

= opt.optimise (x0, classic = False)<br />

Par :<br />

x<br />

= opt.optimise (x0, classic = True)<br />

Alors qu'il faut moins d'une c<strong>en</strong>taine d'itérations à l'algorithme BFGS pour converger la solution, il <strong>en</strong><br />

faut plus d'un millier à l'algorithme du premier ordre.<br />

13.2.2 Second exemple, proportion <strong>de</strong> mâles et <strong>de</strong> femelles chez les poissons<br />

Pour vérier la pertin<strong>en</strong>ce d'une métho<strong>de</strong> numérique, on cherche d'abord à vérier qu'elle marche bi<strong>en</strong> sur<br />

un échantillon (X i ) i<br />

dont on sait qu'il vérie les hypothèses du problème à résoudre - la distribution <strong>de</strong>s<br />

X i est un mélange <strong>de</strong> <strong>de</strong>ux lois normales. Une fois cette première étape validée, il est alors utilisé sur <strong>de</strong>s<br />

données réelles ce qui ne sera pas fait ici.<br />

13.2.3 Simulation d'une série (X i ) i<br />

Tout d'abord, on cherche à simuler une série (X i ) 1iN<br />

où chaque X i suit une loi <strong>de</strong> <strong>de</strong>nsité (voir -<br />

gure 13.5) :<br />

(<br />

)<br />

(<br />

)<br />

α<br />

f(x) = √ exp − (x − µ m) 2<br />

2πσm 2σm<br />

2 + √ 1 − α exp − (x − µ f ) 2<br />

2πσf 2σf<br />

2 (13.37)<br />

On choisi par exemple N = 10000, α = 0, 55, µ m = 0, 40, µ f = 0, 35, σ m = 0, 020, σ = 0, 015. Les mâles<br />

ont une taille moy<strong>en</strong>ne <strong>de</strong> 40 cm et un écart-type <strong>de</strong> 2cm. Ceci signie que 95% <strong>de</strong>s mâles ont une taille<br />

comprises <strong>en</strong>tre 36cm et 44cm. Les femelles ont une taille moy<strong>en</strong>ne <strong>de</strong> 40 cm et un écart-type <strong>de</strong> 1, 5 cm.<br />

Ceci signie que 95% <strong>de</strong>s femelles ont une taille comprise <strong>en</strong>tre 32cm et 38cm. Pour cela, on applique<br />

177


l'alogirthme suivant :<br />

Algorithme 13.8 : simulation d'une variable selon un mélange <strong>de</strong> <strong>de</strong>ux lois normales<br />

On cherche à simuler une variable aléatoire X selon un mélange <strong>de</strong> <strong>de</strong>ux lois normales :<br />

(<br />

)<br />

(<br />

)<br />

α<br />

X ∼ √ exp − (x − µ m) 2<br />

2πσm 2σm<br />

2 + √ 1 − α exp − (x − µ f ) 2<br />

2πσf 2σf<br />

2<br />

où α ∈ [0, 1], µ m , µ f ∈ R, σ m , σ f > 0.<br />

Etape A : choix <strong>de</strong> la composante normale<br />

Soit U ∼ U [0, 1], U est une variable aléatoire uniforme sur [0, 1].<br />

Etape B : simulation loi normale<br />

On simule V selon une loi normale <strong>de</strong> moy<strong>en</strong>ne nulle et <strong>de</strong> variance un. Autrem<strong>en</strong>t<br />

dit, soit A, B <strong>de</strong>ux variables générées selon une loi uniforme sur [0, 1]<br />

(voir [Robert1996]) :<br />

V = √ −2 log A cos(2πB)<br />

Etape C : construction <strong>de</strong> X<br />

On calcule :<br />

X est la valeur cherchée.<br />

X = 1 {Uα} (σ m V + µ m ) + 1 {U>α} (σ f V + µ f )<br />

Pour obt<strong>en</strong>ir la série (X i ) 1iN<br />

, il sut <strong>de</strong> calculer N valeurs avec l'algorithme 13.8.<br />

13.2.4 Estimation <strong>de</strong>s paramètres avec une métho<strong>de</strong> du premier <strong>de</strong>gré<br />

On implém<strong>en</strong>te le premier algorithme 13.1 an <strong>de</strong> retrouver les paramètres qui ont permis <strong>de</strong> simuler<br />

l'échantillon (X i ) 1iN<br />

. On cherche donc à maximiser la fonction g dénie par l'expression (13.31) dont<br />

le gradi<strong>en</strong>t est dénie par les expressions (13.32) à (13.36).<br />

Il n'est pas toujours possible <strong>de</strong> choisir <strong>de</strong>s valeurs initiales pour l'algorithme d'optimisation. Toutefois,<br />

dans le cas prés<strong>en</strong>t, les paramètres <strong>de</strong> la fonction g ont un s<strong>en</strong>s presque physique. On peut donc choisir<br />

<strong>de</strong>s valeurs s<strong>en</strong>sées pour la solution initiale <strong>de</strong> manière à accélérer la converg<strong>en</strong>ce <strong>de</strong> l'algorithme :<br />

α = 1 2<br />

µ m = E (X i ) + √ V (X i ) µ f = E (X i ) − √ V (X i ) σ m = σ f =<br />

√<br />

V (Xi )<br />

2<br />

(13.38)<br />

L'algorithme converge alors <strong>en</strong> une dizaine d'itérations vers le résultat <strong>de</strong> la gure 13.6. La logvraisemblance<br />

est très proche <strong>de</strong> celle obt<strong>en</strong>ue avec le modèle initial et on remarque que pour cet exemple,<br />

elle est très peu s<strong>en</strong>sible au paramètre α et beaucoup plus aux autres paramètres puisque le paramètre α<br />

n'a pas varié <strong>de</strong> sa valeur initiale 12 .<br />

Pour implém<strong>en</strong>ter cet algorithme, on crée la classe poisson_loi. Cette classe permet <strong>de</strong> simuler un<br />

échantillon <strong>de</strong> taille (métho<strong>de</strong>s simulation, métho<strong>de</strong>s g<strong>en</strong>erate), <strong>de</strong> calculer la log-vraisemblance pour<br />

un échantillon (métho<strong>de</strong>s <strong>de</strong>nsite, log_<strong>de</strong>nsite, log_<strong>de</strong>nsite_list), <strong>de</strong> calculer le gradi<strong>en</strong>t pour un<br />

12 Le résultat est le même si la valeur initiale pour α est diér<strong>en</strong>te : lors <strong>de</strong> l'optimisation, ce paramètre évolue très peu.<br />

178


point x (métho<strong>de</strong> gradi<strong>en</strong>t) ou pour un échantillon métho<strong>de</strong>s gradi<strong>en</strong>t_total). Enn, cette classe<br />

implémt<strong>en</strong>te l'algorithme d'optimisation 13.1 par le biais <strong>de</strong> la métho<strong>de</strong> optimisation.<br />

La partie principale du programme consiste tout d'abord à créer une instance <strong>de</strong> cette classe an <strong>de</strong> générer<br />

une série <strong>de</strong> 10000 tailles <strong>de</strong> poisson. Une secon<strong>de</strong> instance est <strong>en</strong>suite créée qui estime ses coeci<strong>en</strong>ts à<br />

l'ai<strong>de</strong> <strong>de</strong> la métho<strong>de</strong> optimisation. Le résultat est aché puis les <strong>de</strong>nsités sont comparées graphiquem<strong>en</strong>t<br />

(métho<strong>de</strong> trace_<strong>de</strong>nsite).<br />

1 # -*- coding: cp1252 -*-<br />

2 import random<br />

3 import math<br />

4<br />

5 class poisson_loi (object) :<br />

6 """loi <strong>de</strong> distribution <strong>de</strong> la taille <strong>de</strong>s poissons"""<br />

7<br />

8 <strong>de</strong>f __init__(self, alpha = 0.5, mm = 1, sm = 1, mf = 1, sf = 1) :<br />

9 """initialisation <strong>de</strong>s paramètres <strong>de</strong> la loi"""<br />

10 self.alpha = alpha<br />

11 self.mm = mm<br />

12 self.sm = sm<br />

13 self.mf = mf<br />

14 self.sf = sf<br />

15<br />

16 <strong>de</strong>f init (self, l) :<br />

17 """initialisation <strong>de</strong> la classe avec une liste <strong>de</strong> variables"""<br />

18 m = 0<br />

19 s = 0<br />

20 for x in l :<br />

21 m += x<br />

22 s += x*x<br />

23 m /= l<strong>en</strong> (l)<br />

24 s /= l<strong>en</strong> (l)<br />

25 s -= m*m<br />

26 self.alpha = 0.5<br />

27 self.sm = s / 2<br />

28 self.sf = s / 2<br />

29 self.sm = math.sqrt (self.sm)<br />

30 self.sf = math.sqrt (self.sf)<br />

31 self.mm = m + self.sm * 2<br />

32 self.mf = m - self.sf * 2<br />

33<br />

34 <strong>de</strong>f __str__(self) :<br />

35 """affichage"""<br />

36 s = "classe poisson_loi\n"<br />

37 s += "alpha = " + str (self.alpha) + "\n"<br />

38 s += "moy<strong>en</strong>ne male = " + str (self.mm) + "\n"<br />

39 s += "sigma male = " + str (self.sm) + "\n"<br />

40 s += "moy<strong>en</strong>ne femelle = " + str (self.mf) + "\n"<br />

41 s += "sigma femelle = " + str (self.sf) + "\n"<br />

42 return s<br />

43<br />

179


44 <strong>de</strong>f simulation (self) :<br />

45 """simule une taille <strong>de</strong> poisson"""<br />

46 u = random.uniform (0,1)<br />

47 v = random.gauss (0,1)<br />

48 if u


94 except :<br />

95 print "---------- erreur -------------------------------------------"<br />

96 print x<br />

97 return 0<br />

98<br />

99 <strong>de</strong>f log_<strong>de</strong>nsite_list (self, l) :<br />

100 """calcule la somme <strong>de</strong>s log-<strong>de</strong>nsités au point x <strong>de</strong> l"""<br />

101 s = 0<br />

102 for x in l :<br />

103 s += self.log_<strong>de</strong>nsite (x)<br />

104 s /= l<strong>en</strong> (l)<br />

105 return s<br />

106<br />

107 <strong>de</strong>f gradi<strong>en</strong>t (self, x) :<br />

108 """calcul le gradi<strong>en</strong>t au point x, retourne un t-uple"""<br />

109 unpim = 1.0 / (math.sqrt (2 * math.pi) * self.sm)<br />

110 unpif = 1.0 / (math.sqrt (2 * math.pi) * self.sf)<br />

111 expm = math.exp ( - ( x - self.mm)**2 / (2 * (self.sm**2)))<br />

112 expf = math.exp ( - ( x - self.mf)**2 / (2 * (self.sf**2)))<br />

113 grada = unpim * expm - unpif * expf<br />

114 gradmm = (x - self.mm) / (self.sm** 2) * self.alpha * unpim * expm<br />

115 gradmf = (x - self.mf) / (self.sf** 2) * (1.0 - self.alpha) * unpif * expf<br />

116 gradsm = self.alpha * unpim * (x - self.mm) ** 2 / (self.sm ** 3) - \<br />

117 self.alpha * unpim / self.sm<br />

118 gradsm *= expm<br />

119 gradsf = (1.0 - self.alpha) * unpif * (x - self.mf) ** 2 / (self.sf ** 3) - \<br />

120 self.alpha * unpif / self.sf<br />

121 gradsf *= expf<br />

122 f = self.<strong>de</strong>nsite (x)<br />

123 return (grada / f, gradmm / f, gradsm / f, gradmf / f, gradsf / f)<br />

124<br />

125 <strong>de</strong>f gradi<strong>en</strong>t_total (self, l) :<br />

126 """calcul la somme <strong>de</strong>s gradi<strong>en</strong>ts pour tous les x <strong>de</strong> l"""<br />

127 g = [ 0.0 for i in range (0,5) ]<br />

128 for x in l :<br />

129 (a,b,c,d,e) = self.gradi<strong>en</strong>t (x)<br />

130 g [0] += a<br />

131 g [1] += b<br />

132 g [2] += c<br />

133 g [3] += d<br />

134 g [4] += e<br />

135 for i in range (0,l<strong>en</strong> (g)) :<br />

136 g [i] /= l<strong>en</strong> (l)<br />

137 return ( g [0], g [1], g [2], g [3], g [4] )<br />

138<br />

139 <strong>de</strong>f optimisation (self, l, epsilon = 0.001, erreur = 1e-5) :<br />

140 """recherche du maximum <strong>de</strong> la fonction f"""<br />

141 self.init (l)<br />

142 <strong>de</strong> = self.log_<strong>de</strong>nsite_list (l)<br />

143 print "première <strong>de</strong>nsité : ", <strong>de</strong><br />

181


144 dold = <strong>de</strong> / 2<br />

145 nb = 0<br />

146 while abs (dold - <strong>de</strong>) / <strong>de</strong> > erreur :<br />

147 (a,b,c,d,e) = self.gradi<strong>en</strong>t_total (l)<br />

148 self.alpha += epsilon * a<br />

149 self.mm += epsilon * b<br />

150 self.sm += epsilon * c<br />

151 self.mf += epsilon * d<br />

152 self.sf += epsilon * e<br />

153 dold = <strong>de</strong><br />

154 <strong>de</strong> = self.log_<strong>de</strong>nsite_list (l)<br />

155 nb += 1<br />

156 print "itération ", nb, " <strong>de</strong>nsité ", <strong>de</strong>, " (", dold, ") ", \<br />

157 abs (dold - <strong>de</strong>) / <strong>de</strong>, " epsilon = ", epsilon<br />

158<br />

159<br />

160 if __name__ == "__main__" :<br />

161 # création d'une instance <strong>de</strong> la classe<br />

162 cl = poisson_loi (0.55, 0.40, 0.020, 0.35, 0.015)<br />

163 print cl # affichage<br />

164 #cl.trace_<strong>de</strong>nsite () # courbe <strong>de</strong>nsité<br />

165<br />

166 l = cl.g<strong>en</strong>erate (10000)<br />

167 cl2 = poisson_loi ()<br />

168 print "-----------------------------------------------------------"<br />

169 print "log <strong>de</strong>nsité maximale ", cl.log_<strong>de</strong>nsite_list (l)<br />

170 (a,b,c,d,e) = cl.gradi<strong>en</strong>t_total (l)<br />

171 print "gradi<strong>en</strong>t idéal ", (a,b,c,d,e)<br />

172 print "-----------------------------------------------------------"<br />

173<br />

174 (a,b,c,d,e) = cl2.gradi<strong>en</strong>t_total (l)<br />

175 print "gradi<strong>en</strong>t avant", (a,b,c,d,e)<br />

176 cl2.optimisation (l)<br />

177 (a,b,c,d,e) = cl2.gradi<strong>en</strong>t_total (l)<br />

178 print "gradi<strong>en</strong>t après ", (a,b,c,d,e)<br />

179 print "-----------------------------------------------------------"<br />

180 print cl2<br />

181 cl2.trace_<strong>de</strong>nsite (cl)<br />

182 print "log vraisemblance : ", cl2.log_<strong>de</strong>nsite_list (l)<br />

183<br />

184<br />

13.2.5 Estimation <strong>de</strong>s paramètres avec une métho<strong>de</strong> du second <strong>de</strong>gré<br />

Le second programme utilise l'algorithme BFGS pour déterminer le maximum <strong>de</strong> vraisemblance (voir<br />

gure 13.7). Cette métho<strong>de</strong> du second <strong>de</strong>gré permet <strong>de</strong> corriger le fait que le gradi<strong>en</strong>t par rapport au<br />

paramètre α est plus faible que les dérivées partielles par rapport aux autres coeci<strong>en</strong>ts. On trouve cette<br />

fois-ci <strong>de</strong>s valeurs proches du modèle qui a permis <strong>de</strong> simuler les observations.<br />

Ces métho<strong>de</strong>s sont très calculatoires et pour accélérer la vitesse <strong>de</strong> converg<strong>en</strong>ce <strong>de</strong> l'algorithme, il est<br />

182


possible d'utiliser le module pysco <strong>en</strong> utilisant ces <strong>de</strong>ux lignes.<br />

import psyco<br />

psyco.full ()<br />

La vitesse du programme est multipliée par trois ou quatre lorsque celui-ci passe la plus gran<strong>de</strong> partie <strong>de</strong><br />

son temps à faire <strong>de</strong>s calculs.<br />

1 # -*- coding: cp1252 -*-<br />

2 import poisson<br />

3 #import scipy.optimize.lbfgsb as Opt # optimisation<br />

4 import bfgs # optimisation<br />

5 import numpy as Num # pour les tableaux<br />

6 import psyco<br />

7<br />

8 <strong>de</strong>f fonction_bfgs (x, cl, l) :<br />

9 """fonction retournant l'opposé <strong>de</strong> la log-vraisemblance<br />

10 <strong>de</strong> l'instance cl, fonction à minimiser"""<br />

11 cl.set (x)<br />

12 f = - cl.log_<strong>de</strong>nsite_list (l)<br />

13 #print f, "\t", x<br />

14 return f<br />

15<br />

16 <strong>de</strong>f fonction_<strong>de</strong>rivee_bfgs (x, cl, l) :<br />

17 """fonction retournant l'opposé <strong>de</strong> la log-vraisemblance<br />

18 <strong>de</strong> l'instance cl, fonction à minimiser"""<br />

19 cl.set (x)<br />

20 f = cl.gradi<strong>en</strong>t_total (l)<br />

21 return - Num.array (f)<br />

22<br />

23 class poisson_loi_bfgs (poisson.poisson_loi) :<br />

24 """loi <strong>de</strong> distribution <strong>de</strong> la taille <strong>de</strong>s poissons,<br />

25 optimisation à l'ai<strong>de</strong> <strong>de</strong> l'algorithme BFGS"""<br />

26<br />

27 <strong>de</strong>f __init__(self, alpha = 0.5, mm = 1, sm = 1, mf = 1, sf = 1) :<br />

28 """initialisation <strong>de</strong>s paramètres <strong>de</strong> la loi"""<br />

29 poisson.poisson_loi.__init__(self, alpha, mm, sm, mf, sf)<br />

30<br />

31 <strong>de</strong>f set (self, x) :<br />

32 """initialisation avec un tableau"""<br />

33 self.alpha = x [0]<br />

34 self.mm = x [1]<br />

35 self.sm = abs (x [2])<br />

36 self.mf = x [3]<br />

37 self.sf = abs (x [4])<br />

38<br />

39 <strong>de</strong>f get (self) :<br />

40 """retourne un tableau cont<strong>en</strong>ant les paramètres"""<br />

41 return Num.array ( (self.alpha, self.mm, self.sm, self.mf, self.sf) )<br />

42<br />

183


43 <strong>de</strong>f __str__(self) :<br />

44 """affichage"""<br />

45 s = "classe poisson_loi BFGS\n"<br />

46 s += "alpha = " + str (self.alpha) + "\n"<br />

47 s += "moy<strong>en</strong>ne male = " + str (self.mm) + "\n"<br />

48 s += "sigma male = " + str (self.sm) + "\n"<br />

49 s += "moy<strong>en</strong>ne femelle = " + str (self.mf) + "\n"<br />

50 s += "sigma femelle = " + str (self.sf) + "\n"<br />

51 return s<br />

52<br />

53 <strong>de</strong>f optimisation (self, l, epsilon = 0.001) :<br />

54 """recherche du maximum <strong>de</strong> la fonction f"""<br />

55 self.init (l)<br />

56 x0 = self.get ()<br />

57 print x0<br />

58 opt = bfgs.optimisation_bfgs (fonction_bfgs, \<br />

59 fonction_<strong>de</strong>rivee_bfgs, \<br />

60 args = (self, l))<br />

61 x = opt.optimise (x0)<br />

62 #x = bfgs.Opt.fmin_l_bfgs_b ( fonction_bfgs, \<br />

63 # x0, \<br />

64 # fonction_<strong>de</strong>rivee_bfgs, \<br />

65 # args = (self, l))<br />

66 self.set (x)<br />

67 print "optimisation terminée"<br />

68 print "valeur maximale : ", self.log_<strong>de</strong>nsite_list (l)<br />

69 print "message ", d<br />

70<br />

71<br />

72 if __name__ == "__main__" :<br />

73 psyco.full ()<br />

74 # création d'une instance <strong>de</strong> la classe<br />

75 cl = poisson.poisson_loi (0.55, 0.40, 0.020, 0.35, 0.015)<br />

76 print cl # affichage<br />

77 #cl.trace_<strong>de</strong>nsite () # courbe <strong>de</strong>nsité<br />

78<br />

79 l = cl.g<strong>en</strong>erate (10000)<br />

80 cl2 = poisson_loi_bfgs ()<br />

81 print "-----------------------------------------------------------"<br />

82 print "log <strong>de</strong>nsité maximale ", cl.log_<strong>de</strong>nsite_list (l)<br />

83 (a,b,c,d,e) = cl.gradi<strong>en</strong>t_total (l)<br />

84 print "gradi<strong>en</strong>t idéal ", (a,b,c,d,e)<br />

85 print "-----------------------------------------------------------"<br />

86<br />

87 (a,b,c,d,e) = cl2.gradi<strong>en</strong>t_total (l)<br />

88 print "gradi<strong>en</strong>t avant", (a,b,c,d,e)<br />

89 cl2.optimisation (l)<br />

90 (a,b,c,d,e) = cl2.gradi<strong>en</strong>t_total (l)<br />

91 print "gradi<strong>en</strong>t après ", (a,b,c,d,e)<br />

92 print "-----------------------------------------------------------"<br />

184


93 print cl2<br />

94 cl2.trace_<strong>de</strong>nsite (cl)<br />

95 print "log vraisemblance : ", cl2.log_<strong>de</strong>nsite_list (l)<br />

n correction exemple E13<br />

⊓⊔<br />

185


Référ<strong>en</strong>ces<br />

[Berchtold1996] S. Berchtold, D. A. Keim, H. P. Kriegel, The X-Tree : An in<strong>de</strong>x structure for high dim<strong>en</strong>sion<br />

data, Proceedings of the 22nd Internation Confer<strong>en</strong>ce on Very Large Databases, Bombay, India<br />

(1996), pp 0-<br />

[Beckmann1990] N. Beckmann, H. P. Kriegel, P. Schnei<strong>de</strong>r, B. Seeger, The R ∗ -tree : an eci<strong>en</strong>t and robust<br />

access method for points and rectangles, Proceedings of SIGMOD confer<strong>en</strong>ce, Atlantic City (1990),<br />

pp 322-331<br />

[Bottou1991] L. Bottou, Une approche théorique <strong>de</strong> l'appr<strong>en</strong>tissage connexionniste, Application à la reconnaissance<br />

<strong>de</strong> la parole, Thèse <strong>de</strong> l'Université <strong>de</strong> Paris Sud, C<strong>en</strong>tre d'Orsay ( 1991), pp 0-<br />

[Bracewell1986] Ronald Bracewell, The Hartley Transform, Oxford University Press (1986), pp 0-<br />

[Bres<strong>en</strong>ham1965] J. E. Bres<strong>en</strong>ham, Algorithm for computer control of a digital plotter, IBM System Journal<br />

4(1) (1965), pp 25-30<br />

[Bres<strong>en</strong>ham1977] J. E. Bres<strong>en</strong>ham, A linear algorithm for increm<strong>en</strong>tal digital display of circuler arcs,<br />

CGIP 20(2) (1977), pp 0-<br />

[Broy<strong>de</strong>n1967] C.G. Broy<strong>de</strong>n, Quasi-Newton methods and their application to function minimization,<br />

Math. Comput 21 (1967), pp 368-<br />

[Davidon1959] C. W. Davidon, Variable metric method for minimization, A.E.C. Research and Developm<strong>en</strong>t<br />

Report, ANL-5990 (1959), pp 0-<br />

[Dempster1977] A. P. Dempster, N. M. Laird, D. B. Rubin, Maximum-Likelihood <strong>fr</strong>om incomplete data<br />

via the EM algorithm, Journal of Royal Statistical Society B 39 (1977), pp 1-38<br />

[Driancourt1996] X. Driancourt, Optimisation par <strong>de</strong>sc<strong>en</strong>te <strong>de</strong> gradi<strong>en</strong>t stochastique <strong>de</strong> systèmes modulaires<br />

combinant réseaux <strong>de</strong> neurones et programmation dynamique, Application à la reconnaissance<br />

<strong>de</strong> la parole, Thèse <strong>de</strong> l'Université <strong>de</strong> Paris Sud, C<strong>en</strong>tre d'Orsay (1996), pp 0-<br />

[Duhamel1990] P. Duhamel, M. Vetterli, Fast Fourier Transform : a tutorial review and a state of the art.,<br />

Signal Processing 19(4) (1990), pp 259-299<br />

[Faragó1993] A. Faragó, T. Lin<strong>de</strong>r, G. Lugosi, Fast Nearest-Neighbor Search in Dissimilarity Spaces, IEEE<br />

Transactions on Pattern Analysis and Machine Intellig<strong>en</strong>ce 15(9) (1993), pp 957-962<br />

[Faure2000] Robert Faure, Bernard Lemaire, Christophe Picouleau, Précis <strong>de</strong> recherche opérationnelle,<br />

5 i meédition, Dunod (2000), pp 0-<br />

[Fletcher1963] R. Fletcher, M. J. D. Powell, A rapidly converg<strong>en</strong>t <strong>de</strong>sc<strong>en</strong>t method for minimization, ,<br />

Computer Journal (1963), pp 6-163 168<br />

[Fletcher1993] R. Fletcher, An overview of Unconstrained Optimization, Numerical Analysis Report<br />

NA/149 (1993), pp 0-<br />

[Fukunaga1975] - jamais cité - K. Fukunaga, P. M. Nevada, A branch and bound algorithm for computing<br />

k nearest neighbors, IEEE Transactions on Computers 24 (1975), pp 750-753<br />

[Guttman1984] A. Guttman, R-Trees : A Dynamic In<strong>de</strong>x Structure for Spatial Searching, Proceedings<br />

ACM SIGMOD (1984), pp 47-57<br />

[Helsgaun2000] K. Helsgaun, An eective implem<strong>en</strong>tation of the Lin-Kernighan traveling salesman heuristic,<br />

European Journal of Operational Research 126 (2000), pp 106-130<br />

[Koivisto1999] L. Koivisto, Quinqu<strong>en</strong>nal report of Neural Networks Research C<strong>en</strong>ter and Laboratory<br />

of Computer Sci<strong>en</strong>ce and Information Sci<strong>en</strong>ce, Helsinki, University of Technology,<br />

http ://www.cis.hut./research/reports/quinqu<strong>en</strong>nial/ 126 (1999), pp 106-130<br />

[Mallat2000] Stéphane Mallat, Une exploration <strong>de</strong>s signaux <strong>en</strong> on<strong>de</strong>lettes, Editions <strong>de</strong> l'Ecole Polytechnique<br />

(2000), pp 0-<br />

186


[Math2000] auteurs divers, Les mathématici<strong>en</strong>s, Belin pour la sci<strong>en</strong>ce (2000), pp 0-<br />

[Moré1977] J. J. Moré, The Lev<strong>en</strong>berg-Marquardt algorithm : Implem<strong>en</strong>tation and theory, Proceedings of<br />

the 1977 Dun<strong>de</strong>e Confer<strong>en</strong>ce on Numerical Analysis, G. A. Watson, ed., Lecture Notes in Mathematics,<br />

vol. 630, Springer-Verlag, Berlin (1977), pp 105-116<br />

[Mor<strong>en</strong>o2003] Francisco Mor<strong>en</strong>o-Seco, Luisa Mico, Jose Oncina, A modication of the LAESA algorithm<br />

for approximated k-NN classication, Pattern Recognition Letters 24 (2003), pp 47-53<br />

[Nussbaumer1982] H. J. Nussbaumer, Fast Fourier Transform and Convolutions Algorithms, Springer-<br />

Verlag, Berlin (1982), pp 0-<br />

[Rico-Juan2003] J. R. Rico-Juan, L. Mico, Comparison of AESA and LAESA search algorithms using<br />

string and tree-edit-distances, Pattern Recognition Letters 24 (2003), pp 1417-1426<br />

[Robert1996] Christian Robert, Métho<strong>de</strong>s <strong>de</strong> Monte-Carlo par chaînes <strong>de</strong> Markov, Economica (1996), pp<br />

0-<br />

[Sellis1987] T. Sellis, N. Roussopoulos, C. Faloutos, The R+tree - a Dynamic In<strong>de</strong>x for Multi-Dim<strong>en</strong>sional<br />

Objects, Proceedings of the 13th VLDB confer<strong>en</strong>ce (1987), pp 507-518<br />

[Vesanto2000] Juha Vesanto, Esa Alhoniemi, Clustering of the Self-Organizing Map, IEEE Transactions<br />

on Neural Networks 11(3) (2000), pp 586-600<br />

[www-TSP] site internet, http ://www.tsp.gatech.edu//in<strong>de</strong>x.html, (2004), pp 0-<br />

187


.<br />

.<br />

gradi<strong>en</strong>t<br />

gradi<strong>en</strong>t conjugué .<br />

188


Fig. 13.3 Exemple <strong>de</strong> minima locaux.<br />

Fig. 13.4 Ce graphique conti<strong>en</strong>t un histogramme qui approche la <strong>de</strong>nsité <strong>de</strong> l'échantillon (X 1 , . . . , X n ).<br />

La première courbe <strong>en</strong> trait d'union est celle <strong>de</strong> la <strong>de</strong>nsité d'une loi normale <strong>de</strong> paramètres tirés au hasard.<br />

De manière évi<strong>de</strong>nte, l'expression (13.16) calculée pour la courbe <strong>de</strong> ce graphique est inférieure au résultat<br />

obt<strong>en</strong>u pour une même courbe mais c<strong>en</strong>trée sur l'histogramme. Et le calcul du paragraphe 13.1.5 montre<br />

que la meilleure courbe épouse la forme <strong>de</strong> l'histogramme.<br />

Fig. 13.5 Distribution <strong>de</strong>s tailles <strong>de</strong> poissons selon le mélange <strong>de</strong> lois (13.37) avec pour paramètres<br />

α = 0, 55, µ m = 0, 40, µ f = 0, 35, σ m = 0, 020, σ = 0, 015.<br />

189


Fig. 13.6 Comparaison <strong>de</strong> la distribution qui a servi à générer les données (α = 0, 55, µ m = 0, 4,<br />

µ f = 0, 35, σ m = 0, 020, σ = 0, 15) avec la distribution qui a été estimée (α = 0, 50, µ m = 0, 40,<br />

µ f = 0, 35, σ m = 0, 019, σ = 0, 016).<br />

Fig. 13.7 Comparaison <strong>de</strong> la distribution qui a servi à générer les données (α = 0, 55, µ m = 0, 4,<br />

µ f = 0, 35, σ m = 0, 020, σ = 0, 15) avec la distribution qui a été estimée (α = 0, 54, µ m = 0, 40,<br />

µ f = 0, 35, σ m = 0, 020, σ = 0, 015).<br />

190


In<strong>de</strong>x<br />

A<br />

accélération. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .127<br />

ACP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .54<br />

Algorithme<br />

1-PPV ou plus proche voisin. . . . . . . . . . . . . . .136<br />

algorithme BFGS. . . . . . . . . . . . . . . . . . . . . . . . . .165<br />

algorithme <strong>de</strong> Kruskal . . . . . . . . . . . . . . . . . . . . . . 58<br />

algorithme du plus court chemin (Ford) . . . . . 45<br />

carte <strong>de</strong> Kohon<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . 55<br />

construction d'un circuit euléri<strong>en</strong> . . . . . . . . . . . 60<br />

construction d'un circuit hamiltoni<strong>en</strong> . . . . . . . 61<br />

<strong>de</strong>sc<strong>en</strong>te <strong>de</strong> gradi<strong>en</strong>t <strong>de</strong> Newton . . . . . . . . . . . 161<br />

fusion <strong>de</strong> <strong>de</strong>ux listes triées . . . . . . . . . . . . . . . . . . . 8<br />

insertion d'un objet dans un R-tree . . . . . . . . 140<br />

k-PPV ou k-plus proches voisins . . . . . . . . . . . 137<br />

LAESA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

LAESA : sélection <strong>de</strong>s pivots . . . . . . . . . . . . . . 141<br />

lancer <strong>de</strong> rayon . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103<br />

mouvem<strong>en</strong>t d'une cor<strong>de</strong> . . . . . . . . . . . . . . . . . . . 128<br />

optimisation stochastique . . . . . . . . . . . . . . . . . . 167<br />

plus court chemin à partir d'un arbre <strong>de</strong> poids<br />

minimum. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .57<br />

plus proche voisin d'après [Faragó1993] . . . . 143<br />

recherche dans un R-tree . . . . . . . . . . . . . . . . . . 139<br />

recherche dichotomique . . . . . . . . . . . . . . . . . 9, 129<br />

simplication du circuit hamiltoni<strong>en</strong> . . . . . . . . 62<br />

simulation d'une variable selon un mélange <strong>de</strong><br />

<strong>de</strong>ux lois normales. . . . . . . . . . . . . . . . . . . . .178<br />

tracé d'une ellipse (Bres<strong>en</strong>ham) . . . . . . . . . . . . . 18<br />

tracé d'une ligne (Bres<strong>en</strong>ham) . . . . . . . . . . . . . . 17<br />

tri par fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />

tri par insertion dichotomique. . . . . . . . . . . . . . . .9<br />

tri par sélection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

algorithme<br />

coût . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79, 46, 89<br />

aliasing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

ampoule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />

antialiasing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104<br />

appr<strong>en</strong>tissage<br />

second ordre. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .164<br />

arbre <strong>de</strong> poids minimum . . . . . . . . . . . . . . . . . . . . 57, 64<br />

B<br />

B+ tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137<br />

baryc<strong>en</strong>tre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

Bellman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />

BFGS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164, 165<br />

boîte<br />

<strong>en</strong>globante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

f<strong>en</strong>être . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

Branch and bound . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140<br />

Bres<strong>en</strong>ham . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17, 18<br />

Broch, H<strong>en</strong>ri. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .29<br />

C<br />

carte <strong>de</strong> Kohon<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

cartes <strong>de</strong> Kohon<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54<br />

Cauchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163<br />

CD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

c<strong>en</strong>tre d'une sphère . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99<br />

chambre noire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96<br />

Charpak, Georges. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .29<br />

chaînes <strong>de</strong> Markov . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

chires manuscrits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

circuit<br />

déplacem<strong>en</strong>t. . . . . . . . . . . . . . . . . . . . . . . . .62, 63, 71<br />

euléri<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56, 58<br />

hamiltoni<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56, 60<br />

retournem<strong>en</strong>t. . . . . . . . . . . . . . . . . . . . . . . .62, 63, 71<br />

classication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

coloriage <strong>de</strong>s cartes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

compact disque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

composante connexe . . . . . . . . . . . . . . . . . . . . . . . 57, 101<br />

compression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89, 90<br />

condition d'arrêt. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .161<br />

conique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22<br />

connexité<br />

4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16<br />

8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1618, 23<br />

consanguinité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />

contrainte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63<br />

converg<strong>en</strong>ce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64, 163<br />

vitesse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .163<br />

Cooley. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91<br />

cosinus hyperbolique . . . . . . . . . . . . . . . . . . . . . . 126, 127<br />

couleur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97<br />

courbe <strong>de</strong> niveau. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21<br />

Courrier International . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />

coût . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56<br />

algorithme. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .46<br />

polynômial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

coût d'un algorithme. . . . . . . . . . . . . . . . . . . . . . . . . . . .13<br />

critère<br />

191


Cauchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163<br />

D<br />

<strong>de</strong>gré <strong>de</strong> liberté . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63<br />

<strong>de</strong>nsité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

<strong>de</strong>sc<strong>en</strong>te<br />

gradi<strong>en</strong>t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />

Dev<strong>en</strong>ez sorciers, <strong>de</strong>v<strong>en</strong>ez savants . . . . . . . . . . . . . . . 29<br />

DFP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .166<br />

diraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

Dijstra. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .45<br />

discriminant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99<br />

distance<br />

Kullback-Leiber . . . . . . . . . . . . . . . . . . . . . . . . . . . 170<br />

distribution<br />

queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170<br />

diviser pour régner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

droite<br />

tracé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16<br />

Dénition<br />

arbre <strong>de</strong> poids minimum . . . . . . . . . . . . . . . . . . . . 57<br />

B+ tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

circuit euléri<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56<br />

circuit hamiltoni<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . 56<br />

composante connexe . . . . . . . . . . . . . . . . . . . . . . . . 57<br />

coût d'un algorithme . . . . . . . . . . . . . . . . . . . . . . . 46<br />

cycle dans un graphe . . . . . . . . . . . . . . . . . . . . . . . 45<br />

problème NP-complet. . . . . . . . . . . . . . . . . . . . . . .53<br />

rayon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99<br />

déplacem<strong>en</strong>t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62, 63<br />

E<br />

ecacité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />

EM. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .173<br />

emv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167<br />

espace<br />

métrique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137<br />

vectoriel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137<br />

espérance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168<br />

estimateur du maximum <strong>de</strong> vraisemblance 167, 172<br />

Exemple<br />

E1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5<br />

E10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96<br />

E11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125<br />

E12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

E13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />

E2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7<br />

E3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13<br />

E4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .16<br />

E5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .27<br />

E6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .29<br />

E7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .38<br />

E8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .53<br />

E9. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .89<br />

Expectation-Maximisation . . . . . . . . . . . . . . . . . . . . . 173<br />

F<br />

facette . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100, 105, 120<br />

Fast Fourier Transform . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

Fermat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42<br />

FFT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

Fibonacci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />

le d'att<strong>en</strong>te . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

ltrage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

ltre<br />

passe-bas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

passe-haut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

Finlan<strong>de</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />

ot maximal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

fonction<br />

cos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />

t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35<br />

random. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .35<br />

xrange . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />

fonction <strong>de</strong> répartition . . . . . . . . . . . . . . . . . . . . . . . . . . 35<br />

fonction quadratique. . . . . . . . . . . . . . . . . . . . . . . . . . .167<br />

Ford . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />

Ford-Fulkerson. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .46<br />

Fourier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

G<br />

Gibbs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91<br />

Gouraud. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .105<br />

gradi<strong>en</strong>t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21, 161<br />

conjugué . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164<br />

<strong>de</strong>sc<strong>en</strong>te . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161, 162<br />

stochastique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166<br />

graphe<br />

Bellman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />

chemin, poids. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .45<br />

circuit euléri<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . 56, 58<br />

circuit hamiltoni<strong>en</strong> . . . . . . . . . . . . . . . . . . . . . . 56, 60<br />

composante connexe . . . . . . . . . . . . . . . . . . . . . . . . 57<br />

cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />

Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />

ot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

Ford. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .45<br />

matrice d'adjac<strong>en</strong>ce. . . . . . . . . . . . . . . . . . . . . . . . .39<br />

non-ori<strong>en</strong>té . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38<br />

ordonnancem<strong>en</strong>t . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

ori<strong>en</strong>té . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38<br />

192


plus court chemin . . . . . . . . . . . . . . . . . . . . . . . 38, 42<br />

poids minimum . . . . . . . . . . . . . . . . . . . . . . . . . 57, 64<br />

successeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

théorie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42<br />

gravitation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125<br />

gêne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />

H<br />

Hartley . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

histogramme. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .169<br />

I<br />

illumination. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .97, 105<br />

image <strong>de</strong> synthèse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96<br />

indép<strong>en</strong>dance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168<br />

interpolation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .105<br />

intersection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99<br />

intégrale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />

inégalité <strong>de</strong> J<strong>en</strong>s<strong>en</strong>. . . . . . . . . . . . . . . . . . . . . . . . . . . . .171<br />

J<br />

J<strong>en</strong>s<strong>en</strong>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .171<br />

jeu <strong>de</strong> dames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />

K<br />

k-ppv. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .136<br />

Kelvin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

Kohon<strong>en</strong>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .53<br />

kPPV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

Kruskal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

Kullback-Leiber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170<br />

L<br />

LAESA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140, 142<br />

lancer <strong>de</strong> rayon . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96, 102<br />

likelihood. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .169<br />

log-vraisemblance . . . . . . . . . . . . . . . . . . . . . . . . . 169, 178<br />

loi<br />

expon<strong>en</strong>tielle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

Poisson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30, 35<br />

lumière ambi<strong>en</strong>te . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106<br />

Léonard <strong>de</strong> Pise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />

M<br />

Markov . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

mesure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

MNIST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146<br />

modulation <strong>de</strong> <strong>fr</strong>équ<strong>en</strong>ce . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

module<br />

biggles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91<br />

bres<strong>en</strong>ham ligne . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71<br />

copy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65, 71, 91<br />

FFT. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91<br />

math . . . . . . . . . . . . 5, 35, 39, 65, 71, 91, 129, 179<br />

Matrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

numarray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

Numeric . . . . . . . . . . . . . . . . . . . . . . . . . . . 39, 91, 174<br />

PIL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

PIL.Image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

PIL.ImageDraw . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

PIL.random . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

psyco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71, 174, 182<br />

pygame. . . . . . . . . . . . . . . . . . . . . . . . .65, 71, 91, 129<br />

random. . . . . . . . . . . . . . . . . . . . . . . . .35, 65, 71, 179<br />

scipy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6, 91<br />

string. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91<br />

UserArray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

modèle<br />

illumination . . . . . . . . . . . . . . . . . . . . . . . . . . . 97, 105<br />

moy<strong>en</strong>ne. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .167<br />

MP3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .89<br />

métho<strong>de</strong><br />

non-paramétrique . . . . . . . . . . . . . . . . . . . . . . . . . 170<br />

noyaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170<br />

paramétrique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170<br />

second ordre. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .164<br />

sort. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7<br />

métho<strong>de</strong> <strong>de</strong>s pot<strong>en</strong>tiels. . . . . . . . . . . . . . . . . . . . . . . . . .46<br />

métho<strong>de</strong> <strong>de</strong>s rectangles . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />

N<br />

nearest neighbors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

Newton. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .161<br />

normale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21<br />

normale d'une shpère . . . . . . . . . . . . . . . . . . . . . . . . . . 100<br />

noyau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170<br />

O<br />

optimisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />

avec contrainte. . . . . . . . . . . . . . . . . . . . . . . . . .53, 57<br />

BFGS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164<br />

combinatoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

DFP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166<br />

Newton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />

stochastique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167<br />

valeurs initiale. . . . . . . . . . . . . . . . . . . . . . . . . . . . .178<br />

ordonnancem<strong>en</strong>t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

ordre<br />

métho<strong>de</strong> du second ordre . . . . . . . . . . . . . . . . . . 164<br />

P<br />

papilon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55<br />

permutation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

193


pesanteur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .125<br />

Phong . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

pivot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140<br />

sélection. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .141<br />

pixel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16, 102<br />

plan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100<br />

plannication <strong>de</strong> tâches . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

plus proches voisins. . . . . . . . . . . . . . . . . . . . . . . . . . . .136<br />

poids . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125<br />

poisson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171<br />

polygone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101<br />

PPV<br />

1-PPV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

k-PPV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

principe d'optimalité. . . . . . . . . . . . . . . . . . . . . . . . . . . .42<br />

Problème<br />

distance <strong>de</strong> Kullback-Leiber . . . . . . . . . . . . . . . 171<br />

problème jouet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .47<br />

processus<br />

Poisson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35<br />

programmation dynamique. . . . . . . . . . . . . . . . . . . . . .42<br />

Programme<br />

E6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .30<br />

E11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129<br />

E7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .42<br />

E3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13<br />

E5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .28<br />

E13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173<br />

E10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106<br />

E1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5<br />

E12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

E9. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91<br />

E4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .23<br />

E2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10<br />

E8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .64<br />

projection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54<br />

propagation <strong>de</strong> la chaleur . . . . . . . . . . . . . . . . . . . . . . . 90<br />

psychokinèse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .29<br />

Q<br />

quadrilatère. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .100, 120<br />

quadrillage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58, 63<br />

quasi-Newton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163<br />

quicksort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

R<br />

R ∗ tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140<br />

R+ Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140<br />

R-tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

radiosité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106<br />

Rappel<br />

loi <strong>de</strong> Poisson et loi expon<strong>en</strong>tielle . . . . . . . . . . . 30<br />

rayon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96, 99<br />

recherche dichotomique . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />

recherche par dichotomie . . . . . . . . . . . . . . . . . . . . . . 127<br />

reconnaissance<br />

manuscrite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

reet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120<br />

remarque<br />

Algorithme Expectation-Maximisation . . . . . 173<br />

converg<strong>en</strong>ce <strong>de</strong> l'algorithme 8.11 . . . . . . . . . . . . 64<br />

coût dép<strong>en</strong>dant du cont<strong>en</strong>u <strong>de</strong>s données. . . . .46<br />

retournem<strong>en</strong>t, déplacem<strong>en</strong>t, ... . . . . . . . . . . . . . . 63<br />

utilité <strong>de</strong> l'arbre <strong>de</strong> poids minimum . . . . . . . . . 64<br />

représ<strong>en</strong>tation <strong>de</strong>s données . . . . . . . . . . . . . . . . . . . . . . 13<br />

représ<strong>en</strong>tation paramétrique . . . . . . . . . . . . . . . . . . . . 99<br />

repère . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104<br />

ressort. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .127<br />

retournem<strong>en</strong>t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62, 63<br />

réexion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97<br />

ré<strong>fr</strong>action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97<br />

référ<strong>en</strong>ces<br />

Beckmann1990 . . . . . . . . . . . . . . . . . . . . . . . 140, 186<br />

Berchtold1996 . . . . . . . . . . . . . . . . . . . . . . . . 140, 186<br />

Bottou1991 . . . . . . . . . . . . . . . . . . . . . . 163, 166, 186<br />

Bracewell1986 . . . . . . . . . . . . . . . . . . . . . . . . . 91, 186<br />

Bres<strong>en</strong>ham1965 . . . . . . . . . . . . . . . . . . . . . . . . 16, 186<br />

Bres<strong>en</strong>ham1977 . . . . . . . . . . . . . . . . . . . . 18, 22, 186<br />

Broy<strong>de</strong>n1967 . . . . . . . . . . . . . . . . . . . . . . . . . 164, 186<br />

Davidon1959 . . . . . . . . . . . . . . . . . . . . . . . . . 166, 186<br />

Dempster1977 . . . . . . . . . . . . . . . . . . . . . . . . 173, 186<br />

Driancourt1996 . . . . . . . . . . . . . . . . . . . . . . . 163, 186<br />

Duhamel1990 . . . . . . . . . . . . . . . . . . . . . . . . . . 90, 186<br />

Faragó1993 . . . . . . . . . . . . . . . . . . . . . . . . . . . 142, 186<br />

Faure2000 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33, 186<br />

Fletcher1963. . . . . . . . . . . . . . . . . . . . . . . . . .166, 186<br />

Fletcher1993. . . . . . . . . . . . . . . . . . . . . . . . . .164, 186<br />

Fukunaga1975 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186<br />

Guttman1984. . . . . . . . . . . . . . . . . . . . . . . . .138, 186<br />

Helsgaun2000 . . . . . . . . . . . . . . . . . . . 53, 6163, 186<br />

Koivisto1999. . . . . . . . . . . . . . . . . . . . . . . . . . .54, 186<br />

Mallat2000 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90, 186<br />

Math2000. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .90, 186<br />

Mor<strong>en</strong>o2003 . . . . . . . . . . . . . . . . . . . . . . . . . . 141, 187<br />

Moré1977. . . . . . . . . . . . . . . . . . . . . . . . . . . . .164, 187<br />

Nussbaumer1982. . . . . . . . . . . . . . . . . . . . . . .90, 187<br />

Rico-Juan2003 . . . . . . . . . . . . . . . . . . . . . . . . 141, 187<br />

Robert1996 . . . . . . . . . . . . . . . . . . . . . . . . . . . 178, 187<br />

Sellis1987 . . . . . . . . . . . . . . . . . . . . . . . . 139, 140, 187<br />

Vesanto2000 . . . . . . . . . . . . . . . . . . . . . . . 54, 55, 187<br />

www-TSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53, 187<br />

194


S<br />

Schwartz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91<br />

scène . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96<br />

Self Organized Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54<br />

simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />

mélange <strong>de</strong> lois normales . . . . . . . . . . . . . . . . . . 177<br />

simulation numérique . . . . . . . . . . . . . . . . . . . . . . . 27, 30<br />

SOM. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .54, 55<br />

son . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

stationnaire. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .30<br />

stochastique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166, 167<br />

T<br />

tableau<br />

<strong>de</strong>ux dim<strong>en</strong>sions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />

t<strong>en</strong>sion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125<br />

The In<strong>de</strong>p<strong>en</strong><strong>de</strong>nt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />

Théorème<br />

[Faragó1993] 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .143<br />

[Faragó1993] 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .143<br />

converg<strong>en</strong>ce <strong>de</strong> la métho<strong>de</strong> <strong>de</strong> Newton (Bottou1991)<br />

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162<br />

distance <strong>de</strong> Kullback-Leiber . . . . . . . . . . . . . . . 171<br />

toy problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47<br />

traction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />

tracé d'une ellipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />

tracé d'une ligne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17<br />

transformée <strong>de</strong> Fourier<br />

discrète. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .89<br />

rapi<strong>de</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

transformée <strong>de</strong> Hartley . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

travelling salesman problem. . . . . . . . . . . . . . . . . . . . .53<br />

tri<br />

fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

insertion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />

insertion dichotomique . . . . . . . . . . . . . . . . . . . . . . . 7<br />

quicksort . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7, 46, 138<br />

rapi<strong>de</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

sélection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

triangle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .120<br />

TSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

type<br />

dictionnaire. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13<br />

liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />

T-uple. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .64<br />

V<br />

vague. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .170<br />

variables<br />

indép<strong>en</strong>dantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168<br />

variance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168<br />

vecteur normal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21<br />

voyageur <strong>de</strong> commerce . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

vraisemblance. . . . . . . . . . . . . . . . . . . . . . . . . . . . .167, 178<br />

Z<br />

zone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63<br />

§<br />

§<br />

Algorithme<br />

10.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103<br />

11.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128<br />

11.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129<br />

12.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

12.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137<br />

12.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139<br />

12.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140<br />

12.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141<br />

12.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

12.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143<br />

13.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />

13.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165<br />

13.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167<br />

13.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178<br />

2.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

2.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />

2.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />

2.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />

2.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />

4.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17<br />

4.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />

7.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />

8.10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61<br />

8.11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62<br />

8.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55<br />

8.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />

8.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

8.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60<br />

démonstration<br />

théorème 13.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . 162<br />

Dénition<br />

10.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99<br />

12.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

7.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />

7.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

8.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

8.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56<br />

8.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56<br />

8.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />

8.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />

Problème<br />

195


13.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171<br />

Rappel<br />

6.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

Théorème<br />

12.10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143<br />

12.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143<br />

13.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162<br />

13.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171<br />

É<br />

écart-type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167, 168<br />

échantillon. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .169<br />

énénem<strong>en</strong>t rare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170<br />

équation d'une shère . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99<br />

équation diér<strong>en</strong>tielle . . . . . . . . . . . . . . . . . . . . . . . . . . 126<br />

196

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!