Exemples de programmes en python - xavierdupre.fr
Exemples de programmes en python - xavierdupre.fr
Exemples de programmes en python - xavierdupre.fr
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