Sunteți pe pagina 1din 20

L'informatique au lyce

Chapitre 9

http://ow.ly/36Jth

Chapitre 9
Algorithmique
On dsigne par algorithmique l'ensemble des activits logiques qui relvent des algorithmes ; en
particulier, en informatique, cette discipline dsigne l'ensemble des rgles et des techniques qui sont
impliques dans la dfinition et la conception des algorithmes.

9.1.

Quelques dfinitions

Le mot algorithme vient du nom du mathmaticien Al Khwarizmi, qui, au 9me sicle crivit
le premier ouvrage systmatique sur la solution des quations linaires et quadratiques. La notion
d'algorithme est donc historiquement lie aux manipulations numriques, mais elle s'est
progressivement dveloppe pour porter sur des objets de plus en plus complexes : des textes, des
images, des formules logiques, des objets physiques, etc.
Un algorithme est un nonc d'une suite d'oprations permettant de donner la rponse un
problme.

Didier Mller

Si les oprations s'excutent sur plusieurs processeurs en parallle, on parle d'algorithme


parallle.
Si les tches s'excutent sur un rseau de processeurs on parle d'algorithme distribu.
Un algorithme qui contient un appel lui-mme est dit rcursif.
Un algorithme glouton est un algorithme qui suit le principe de faire, tape par tape, un
choix optimum local, dans l'espoir d'obtenir un rsultat optimum global. Dans les cas o
l'algorithme ne fournit pas systmatiquement la solution optimale, il est appel une
heuristique gloutonne.
En optimisation combinatoire, thorie des graphes et thorie de la complexit, une
heuristique est un algorithme qui fournit rapidement (en temps polynomial) une solution
ralisable, mais pas ncessairement optimale, pour un problme d'optimisation difficile.
Une heuristique, ou mthode approximative, est donc le contraire d'un algorithme exact qui
trouve une solution optimale pour un problme donn. L'usage d'une heuristique est
pertinente pour calculer une solution approche d'un problme et ainsi acclrer le
processus de rsolution exacte.

9-1

mai 2015

Algorithmique

Le texte et les
images de ce
paragraphe
proviennent
de [9].

9.2.

Les mtaheuristiques forment une famille d'algorithmes d'optimisation visant rsoudre des
problmes d'optimisation difficile (souvent issus des domaines de la recherche
oprationnelle, de l'ingnierie ou de l'intelligence artificielle) pour lesquels on ne connat
pas de mthode classique plus efficace. Ces mthodes utilisent cependant un haut niveau
d'abstraction, leur permettant d'tre adaptes une large gamme de problmes diffrents.
Les mtaheuristiques les plus connues sont la recherche avec tabous, le recuit simul, les
algorithmes gntiques et les colonies de fourmis.

Sortir d'un labyrinthe

Vous tes prisonnier d'un labyrinthe et vous voulez en sortir. Comment faire ? Vous n'avez
videmment aucun plan du labyrinthe. Voici quelques algorithmes, plus ou moins efficaces.

9.2.1.

La mthode de la souris

L'algorithme de la souris est l'algorithme le plus simple pour se sortir d'un labyrinthe : il consiste
prendre chaque intersection un chemin au hasard. Les thormes sur les marches alatoires sont
formels : en procdant de cette faon, vous finirez par sortir du labyrinthe... Cependant, le temps que
vous mettrez pour trouver la sortie sera vraiment trs long (proportionnel au moins au carr de la
taille du labyrinthe).

9.2.2.

La mthode de la main sur le mur

Cet algorithme consiste se dplacer en gardant la main contre un des murs, disons le droit.
Ainsi, chaque intersection, on tournera droite. En notant les endroits o l'on est pass qu'une seule
fois, on peut en dduire un chemin (pas forcment le plus rapide).

L'algorithme du mur en action : le point rouge est le point de dpart, le chemin en orange est le
chemin obtenu en suivant le mur droit. On peut en dduire le chemin le plus rapide, en bleu.
Cette mthode permet de sortir coup sr du labyrinthe en un temps raisonnable, mais on prend
le risque de visiter l'ensemble du labyrinthe.
La mthode du mur pose un problme quand le labyrinthe contient des lots.

L'algorithme du mur en action dans un labyrinthe avec lots : en suivant toujours le mur (gauche
ou droit), on finit par tourner en rond.

Didier Mller

9-2

mai 2015

L'informatique au lyce

9.2.3.

Chapitre 9

L'algorithme de Pledge

Le principe de base, c'est de longer les murs, mais en vitant de rester coinc sur un mme lot.
Pour a, il faut trouver le bon moment o lcher son mur. Dans la pratique, il faut garder un
compteur dans la tte, que l'on initialise 0 : si le compteur indique 0, on va tout droit jusqu'au mur
en face. partir de ce mur, on tourne du ct que l'on prfre (mais toujours le mme, disons
gauche) et on suit le mur en ajoutant 1 au compteur ds que l'on tourne droite et en soustrayant 1
ds que l'on tourne gauche. Si le compteur indique 0, on lche le mur, et on va tout droit.

L'algorithme de Pledge en action : on part du point rouge (compteur en position 0) et on file tout
droit. On suit alors le mur par la gauche. Ds que l'on tourne gauche (angle bleu), on
incrmente le compteur), et si on tourne droite (angle vert fonc) on le dcrmente. Si le
compteur atteint nouveau 0 (angle vert clair), on lche le mur, et on continue tout droit.
videmment, cet algorithme ne marche que dans le cas o le labyrinthe est orthogonal (tous les
angles sont 90), mais on peut adapter l'algorithme aux autres labyrinthes. Au lieu d'incrmenter le
compteur de 1 ds que l'on tourne, on l'incrmente par l'angle du virage (avec son signe + ou -).
Cet algorithme prsente un problme si la sortie est une trappe situe sur un lot.

L'algorithme de Pledge en action sur un labyrinthe o la sortie (en jaune) se trouve sur un lot :
au bout d'un moment, on tourne en rond...

9.2.4.

La mthode de Trmaux

Marquez la craie le chemin que vous suivez. Si vous tombez dans un cul-de-sac, faites
simplement demi-tour. Si vous tombez sur une intersection que vous n'aviez encore jamais croise,
prenez le chemin que vous prfrez. Si vous tes dj tomb sur cette intersection auparavant, faites
comme si vous tiez tomb sur un cul-de-sac en faisant demi-tour, pour rejoindre la dernire
intersection o vous avez fait un choix.
Si vous revenez un moment o un autre sur votre point de dpart, c'est que vous avez visit la
totalit du labyrinthe et qu'il n'avait aucune sortie... Par contre, si vous trouvez la sortie, rien ne vous
dit que c'tait le chemin le plus court !

Didier Mller

9-3

mai 2015

Algorithmique

L'algorithme de Trmaux en action : au fur et mesure, on peut marquer certains passages


comme tant des culs-de-sac, ici en gris.

9.3.

Le problme des huit dames

Le but du problme des huit dames, est de placer huit dames


d'un jeu d'checs sur un chiquier de 88 cases sans que les
dames ne puissent se menacer mutuellement, conformment aux
rgles du jeu d'checs. Par consquent, deux dames ne devraient
jamais partager la mme range, colonne, ou diagonale (voir
dessin ci-contre).
Durant des annes, beaucoup de mathmaticiens, y compris
Gauss ont travaill sur ce problme, qui est un cas particulier du
problme gnralis des n-dames, pos en 1850 par Franz
Nauck, et qui est de placer n dames libres sur un chiquier
de nn cases. En 1874, S. Gunther proposa une mthode pour
trouver des solutions en employant des dterminants, et J. W. L.
Glaisher affina cette approche.
Le problme des huit dames a 92 solutions distinctes, ou seulement 12 solutions si l'on tient
compte de transformations telles que des rotations ou des rflexions.

Le problme des huit dames est un bon exemple de problme simple mais non vident. Pour cette
raison, il est souvent employ comme support de mise en uvre de diffrentes techniques de
programmation, y compris d'approches non traditionnelles de la programmation telles que la
programmation par contraintes, la programmation logique ou les algorithmes gntiques.

9.3.1.

Algorithme naf

L'algorithme naf de recherche exhaustive teste toutes les manires possibles de placer une dame
par colonne, pour retirer toutes celles pour lesquelles des dames se menacent mutuellement.
Il y a 88 = 16'777'216 placements explorer.
Didier Mller

9-4

mai 2015

L'informatique au lyce

Chapitre 9

Exercice 9.1
Programmez l'algorithme naf pour trouver les 92 solutions au problme des huit dames.
Vous reprsenterez la position par une liste de nombres de 0 7. Ces nombres indiquent la ligne sur
laquelle la dame se trouve pour la colonne correspondante. Par exemple, la 12 me solution du
graphique ci-dessus sera reprsente par la liste : [2, 4, 1, 7, 0, 6, 3, 5].

9.3.2.

Recherche en profondeur

La recherche en profondeur consiste trouver les solutions en plaant les dames de gauche
droite. Prenons pour simplifier un exemple sur un damier 4x4. Imaginons que toutes les solutions
avec la premire dame sur la premire ligne ont t trouves (voir dessin ci-dessous).

On veut maintenant placer la premire dame sur la 2me ligne.


Plaons la deuxime dame sur la ligne 1. Il y a conflit : STOP. On ne cherche pas plus loin. Idem
pour les lignes 2 et 3. La seule solution possible est la 4me ligne.
Plaons maintenant la troisime dame. On peut la mettre sur la premire ligne sans conflit.
Plaons alors la 4me et dernire dame. La seule ligne possible est la 3me. On a une solution :
[1,3,0,2].
On remonte alors dans l'arbre : la troisime dame ne peut se placer sur aucune autre ligne.
Remontons encore d'un cran.
On a dj essay toutes les lignes pour la deuxime dame.
Remontons encore d'un cran : plaons la premire dame sur la 3me ligne et recommenons une
recherche en profondeur...

Exercice 9.2
Programmez la recherche en profondeur explique ci-dessus. Comptez le nombre de situations
(intermdiaires et finales) analyses.

9.3.3

Mthode heuristique

Un algorithme de rparation itrative commence typiquement partir d'un placement de


toutes les dames sur l'chiquier, par exemple avec une seule dame par ligne et par colonne. Il essaie
ensuite toutes les permutations des colonnes (il n'y a que des conflits diagonaux tester) pour ne
garder que les configurations sans conflits.
Il y a 8! = 40'320 placements explorer.

Exercice 9.3
Placez initialement les 8 dames sur la diagonale de l'chiquier, puis changez deux colonnes
choisies au hasard. Rptez l'opration jusqu' ce qu'une solution soit trouve.

Didier Mller

9-5

mai 2015

Algorithmique

Exercice 9.4
Modifiez le programme de l'exercice 9.3 pour trouver les 92 solutions. Le programme ci-dessous
pourra vous aider. N'essayez pas de le comprendre, il fait appel des notions avances de Python que
l'on n'a pas vues (Iterators et generators). Contentez-vous de l'intgrer votre programme.
def all_perms(str):
if len(str)<=1:
yield str
else:
for perm in all_perms(str[1:]):
for i in range(len(perm)+1):
yield perm[:i] + str[0:1] + perm[i:]
for p in all_perms([0,1,2]):
print(p)

Le rsultat sera :
[0,
[1,
[1,
[0,
[2,
[2,

9.4.

1,
0,
2,
2,
0,
1,

2]
2]
0]
1]
1]
0]

Algorithmes gloutons

Un algorithme glouton (en anglais : greedy) est un algorithme qui suit le principe de faire, tape
par tape, un choix optimum local, dans l'espoir d'obtenir un rsultat optimum global. Il n'y a pas de
retour en arrire : chaque tape de dcision dans l'algorithme, le choix qui semble le meilleur ce
moment est effectu et est dfinitif. Les algorithmes gloutons servent surtout rsoudre des
problmes d'optimisation.

Exemple : rendu de monnaie


Dans le problme du rendu de monnaie (donner une somme avec le moins possible de pices),
l'algorithme consistant rpter le choix de la pice de plus grande valeur qui ne dpasse pas la
somme restante est un algorithme glouton.
Suivant le systme de pices, l'algorithme glouton est optimal ou pas. Dans le systme de pices
europen (en centimes : 1, 2, 5, 10, 20, 50, 100, 200), o l'algorithme glouton donne la somme
suivante pour 37 : 20+10+5+2, on peut montrer que l'algorithme glouton donne toujours une solution
optimale.
Dans le systme de pices (1, 3, 4), l'algorithme glouton n'est pas optimal. En effet, il donne pour
6 : 4+1+1, alors que 3+3 est optimal.

Exercice 9.5
Une route comporte n stations-service, numrotes dans l'ordre du parcours, de 0 n1. La
premire est une distance d[0] du dpart, la deuxime est une distance d[1] de la premire, la
troisime une distance d[2] de la deuxime, etc. La fin de la route est une distance d[n] de la
n-ime et dernire station-service.
Un automobiliste prend le dpart de la route avec une voiture dont le rservoir d'essence est plein.
Sa voiture est capable de parcourir une distance r avec un plein.
Question 9.5.1
Donnez une condition ncessaire et suffisante pour que l'automobiliste puisse effectuer le
parcours. On la supposera ralise par la suite.

Didier Mller

9-6

mai 2015

L'informatique au lyce

Chapitre 9

Question 9.5.2
Prenez 17 stations-service avec les distances d = [23, 40, 12, 44, 21, 9, 67, 32, 51, 30, 11, 55, 24,
64, 32, 57, 12, 80] et r = 100.
L'automobiliste dsire faire le plein le moins souvent possible. crivez une fonction Python
rapide qui dtermine quelles stations-service il doit s'arrter.

Exercice 9.6
Un cambrioleur entre par effraction dans une maison. Il n'est capable de porter que K kilos : il lui
faudra donc choisir entre les diffrents objets de valeur, afin d'amasser le plus gros magot possible.
On supposera dans un premier temps que les objets sont fractionnables (on peut en prendre
n'importe quelle quantit, c'est le cas d'un liquide ou d'une poudre). Il y a n matires diffrentes,
numrotes de 0 n1, la i-me ayant un prix p[i] par kilo. La quantit disponible de cette matire
est q[i]. On suppose que tous les prix sont diffrents deux deux.
Question 9.6.1
Proposez un algorithme qui donne un choix optimal pour le voleur. Ce choix est-il unique ?
Programmez une fonction voleur en Python qui reprenne cet algorithme (vous pourrez supposer
que le tableau p est tri).
Prenez par exemple : p = [43, 40, 37, 33, 28, 25, 20, 17, 14, 13], q = [7, 6, 12, 11, 2, 23, 1, 4, 24,
43], K = 55.
Question 9.6.2
On suppose maintenant que les objets sont non fractionnables (c'est le cas d'un vase ou d'un
tlviseur). Le i-me objet vaut un prix p[i] et pse un poids q[i].
Proposez une mthode drive de la question 1 (sans la programmer).
Donne-t-elle un choix optimal ?

9.5.

http://ow.ly/4tpWm

Algorithmes de tri

Une des oprations les plus courantes en programmation est


le tri d'objets. Les ordres les plus utiliss sont l'ordre numrique
et l'ordre lexicographique.
Les principales caractristiques qui permettent de diffrencier
les algorithmes de tri sont la complexit algorithmique (voir
8.5) et les ressources ncessaires (notamment en terme
d'espace mmoire utilis) .
On peut montrer que la complexit temporelle en moyenne et dans le pire des cas d'un algorithme
bas sur une fonction de comparaison ne peut pas tre meilleure que O(nlog(n)). Les tris qui ne
demandent que O(nlog(n)) comparaisons en moyenne sont alors dits optimaux.
Dans les algorithmes ci-dessous, on effectuera des tris par ordre croissant.

9.5.1.

Tri par slection

Le tri par slection est un des algorithmes de tri les plus triviaux.
On recherche le plus grand lment que l'on va replacer sa position finale, c'est--dire en
dernire position.
Puis on recherche le second plus grand lment que l'on va placer en avant-dernire position, etc.,
jusqu' ce que le tableau soit entirement tri.
def swap(l,i,j):
# echange 2 valeurs d'une liste
l[i],l[i] = l[j], l[i]

Didier Mller

9-7

mai 2015

Algorithmique
def tri_selection(l):
for i in range(len(l)-1):
mini=i
for j in range(i+1,len(l)):
if l[j]<l[mini]: mini=j
swap(l,i,mini)

Complexit
Meilleur des cas : O(n2) quand le tableau est dj tri.
Pire cas : O(n2) quand le tableau est tri en ordre inverse.
En moyenne : O(n2).

9.5.2.

Tri bulles (Bubble sort)

Le tri bulles est un algorithme de tri qui consiste faire remonter progressivement les plus
petits lments d'une liste, comme les bulles d'air remontent la surface d'un liquide.
L'algorithme parcourt la liste, et compare les couples d'lments successifs. Lorsque deux
lments successifs ne sont pas dans l'ordre croissant, ils sont changs. Aprs chaque parcours
complet de la liste, l'algorithme recommence l'opration. Lorsqu'aucun change n'a lieu pendant un
parcours, cela signifie que la liste est trie : l'algorithme peut s'arrter.
Cet algorithme est souvent enseign en tant qu'exemple algorithmique. Cependant, il prsente une
complexit en O(n2) dans le pire des cas (o n est la longueur de la liste), ce qui le classe parmi les
mauvais algorithmes de tri. Il n'est donc quasiment pas utilis en pratique.
def swap(l,i,j):
# echange 2 valeurs d'une liste
l[i],l[j] = l[j],l[i]
def tri_a_bulles(l):
for i in range(len(l)):
for j in reversed(range(i,len(l))):
if l[j]<l[j-1]:
swap(l,j-1,j)

Complexit
Meilleur cas : O(n) quand le tableau est tri.
Pire des cas : O(n2) quand le tableau est tri en ordre inverse (n1)(n1) = O(n)

9.5.3.

Tri par insertion

Le tri par insertion est le tri le plus efficace sur des listes de petite taille. C'est pourquoi il est
utilis par d'autres mthodes comme le Quicksort (voir 9.5.4). Il est d'autant plus rapide que les
donnes sont dj tries en partie dans le bon ordre.
Le principe de ce tri est trs simple : c'est le tri que toute personne utilise naturellement quand
elle a des dossiers (ou n'importe quoi d'autre) classer. On prend un dossier et on le met sa place
parmi les dossiers dj tris. Puis on recommence avec le dossier suivant.
Pour procder un tri par insertion, il suffit de parcourir une liste : on prend les lments dans
l'ordre. Ensuite, on les compare avec les lments prcdents jusqu' trouver la place de l'lment
qu'on considre. Il ne reste plus qu' dcaler les lments du tableau pour insrer l'lment considr
sa place dans la partie dj trie.
def tri_insertion(liste):
j, n = 1, len(liste)
while j != n:
i = j - 1
temp = liste[j]
while i > -1 and liste[i] > temp:
liste[i+1] = liste[i]
i = i - 1
liste[i+1] = temp
j = j + 1

Didier Mller

9-8

mai 2015

L'informatique au lyce

Chapitre 9

Complexit
Pire cas : O(n2) quand le tableau est tri en ordre inverse
Moyenne : O(n2)

9.5.4.

Quicksort

Le Quicksort est une mthode de tri invente par Sir


Charles Antony Richard Hoare en 1961 et fonde sur la
mthode de conception diviser pour rgner . Il peut
tre implment sur un tableau ou sur des listes ; son
utilisation la plus rpandue concerne tout de mme les
tableaux.
Le Quicksort est un tri dont la complexit moyenne
est en O(nlog(n)), mais dont la complexit dans le pire
des cas est un comportement quadratique en O(n2).
Malgr ce dsavantage thorique, c'est en pratique un des
tris les plus rapide pour des donnes rparties
alatoirement. Les entres donnant lieu au comportement
quadratique dpendent de l'implmentation de
l'algorithme, mais sont souvent (si l'implmentation est
maladroite) les entres dj presque tries. Il sera plus
avantageux alors d'utiliser le tri par insertion.
La mthode consiste placer un lment du tableau
(appel pivot) sa place dfinitive, en permutant tous les
lments de telle sorte que tous ceux qui lui sont
infrieurs soient sa gauche et que tous ceux qui lui sont
suprieurs soient sa droite. Cette opration s'appelle le
partitionnement.
Pour chacun des sous-tableaux, on dfinit un nouveau
pivot et on rpte l'opration de partitionnement. Ce
processus est rpt rcursivement, jusqu' ce que
l'ensemble des lments soit tri.
def partition(tab, debut, fin):
while debut < fin:
while debut < fin:
if tab[debut] > tab[fin]:
tab[debut], tab[fin] = tab[fin], tab[debut]
break
fin = fin - 1
while debut < fin:
if tab[debut] > tab[fin]:
tab[debut], tab[fin] = tab[fin], tab[debut]
break
debut = debut + 1
return debut
def quicksort(tab, debut=None, fin=None):
if debut is None: debut = 0
if fin is None: fin = len(tab)
if debut < fin:
i = partition(tab, debut, fin-1)
quicksort(tab, debut, i)
quicksort(tab, i+1, fin)

Dans la pratique, pour les partitions avec un faible nombre d'lments (jusqu' environ 15
lments), on a souvent recours un tri par insertion qui se rvle plus efficace que le Quicksort.
Le problme le plus important est le choix du pivot. Une implmentation du Quicksort qui ne
choisit pas adquatement le pivot sera trs inefficace pour certaines entres. Par exemple, si le pivot
est toujours le plus petit lment de la liste, Quicksort sera aussi inefficace qu'un tri par slection.

Didier Mller

9-9

mai 2015

Algorithmique

Complexit
Pire des cas : O(n2) quand le tableau est tri en ordre inverse
En moyenne et dans le meilleur des cas : O(nlog(n))

9.5.5.

Tri par fusion (Mergesort)

Le tri par fusion repose sur le fait que, pour fusionner deux listes/tableaux tri(e)s dont la somme
des longueurs est n, n1 comparaisons au maximum sont ncessaires. Pour aller aussi vite que le
Quicksort, il a donc besoin d'utiliser O(n)
mmoire supplmentaire, mais il a l'avantage
d'tre stable c'est--dire de ne pas mlanger ce qui
est dj tri.
L'algorithme peut tre dcrit rcursivement :
1.
2.
3.

On dcoupe en deux parties peu prs


gales les donnes trier.
On trie les donnes de chaque partie.
On fusionne les deux parties.

La rcursivit s'arrte car on finit par arriver


des listes composes d'un seul lment et le tri est
alors trivial.
On peut aussi utiliser un algorithme itratif :
1.
2.
3.

On trie les lments deux deux.


On fusionne les listes obtenues.
On recommence l'opration prcdente
jusqu' ce qu'on ait une seule liste trie.

Complexit

Moyenne et pire des cas : O(n log(n))


def merge(l1, l2):
liste = []
i = j = 0
n1=len(l1)
n2=len(l2)
while True:
if i<n1 and j<n2:
if l1[i]<l2[j]:
liste.append(l1[i])
i+=1
else:
liste.append(l2[j])
j+=1
elif i>=n1:
liste.extend(l2[j:])
break
else:
liste.extend(l1[i:])
break
return liste
def tri_fusion(liste):
def tri_fusion_interne(liste):
if len(liste)<2 :
return liste

Didier Mller

9-10

mai 2015

L'informatique au lyce

Chapitre 9
return merge(tri_fusion_interne(liste[:len(liste)//2]),
tri_fusion_interne(liste[len(liste)//2:]))
liste[:] = tri_fusion_interne(liste)

9.5.6.

Tri par tas (Heapsort)

Cet algorithme permet de trier les lments d'un tableau en O(n log (n)) dans le pire des cas, o n
est le nombre d'lments trier. Les principaux atouts de cette mthode sont la faible consommation
mmoire et l'efficacit optimale.

Principe
L'ide qui sous-tend cet algorithme consiste voir le tableau comme un arbre binaire. Le premier
lment est la racine, le deuxime et le troisime sont les deux descendants du premier lment, etc.
Ainsi le n-ime lment a pour fils les lments 2n+1 et 2n+2. Si le tableau n'est pas de taille 2n, les
branches ne se finissent pas tout fait la mme profondeur.
Dans l'algorithme, on cherche obtenir un tas, c'est--dire un arbre binaire vrifiant les proprits
suivantes (les deux premires proprits dcoulent de la manire dont on considre les lments du
tableau) :
la diffrence maximale de profondeur entre deux feuilles est de 1 (i.e. toutes les feuilles se
trouvent sur la dernire ou sur l'avant-dernire ligne) ;
les feuilles de profondeur maximale sont tasses sur la gauche.
chaque nud est de valeur suprieure celles de ses deux fils, pour un tri ascendant.
Comme expliqu au paragraphe 6.4.2, un tas ou un arbre binaire presque complet peut tre stock
dans un tableau, en posant que les deux descendants de l'lment d'indice n sont les lments
d'indices 2n+1 et 2n+2 (pour un tableau indic partir de 0). En d'autres termes, les nuds de l'arbre
sont placs dans le tableau ligne par ligne, chaque ligne tant dcrite de gauche droite.
Une fois le tas de dpart obtenu, l'opration de base de ce tri est le tamisage d'un lment,
suppos le seul mal plac dans un arbre qui est presque un tas. Plus prcisment, considrons un
arbre T = T[0] dont les deux sous-arbres (T[1] et T[2]) sont des tas, tandis que la racine est
ventuellement plus petite que ses fils. L'opration de tamisage consiste changer la racine avec le
plus grand de ses fils, et ainsi de suite rcursivement jusqu' ce qu'elle soit sa place.
Pour construire un tas partir d'un arbre quelconque, on tamise les racines de chaque sous-tas, de
bas en haut (par taille croissante) et de droite gauche.
Pour trier un tableau partir de ces oprations, on commence par le transformer en tas. On
change la racine avec le dernier lment du tableau, et on restreint le tas en ne touchant plus au
dernier lment, c'est--dire l'ancienne racine. On tamise la racine dans le nouveau tas, et on rpte
l'opration sur le tas restreint jusqu' l'avoir vid et remplac par un tableau tri.
def faire_tas(tab, debut, n):
# transforme le tableau "tab" en un tas
racine = debut
while racine*2 + 1 < n:
fils = racine*2 + 1
if fils < n-1 and tab[fils] < tab[fils+1]:
fils += 1
if tab[racine] < tab[fils]:
tab[racine], tab[fils] = tab[fils], tab[racine]
racine = fils
else:
return
def heapsort(tab):
n = len(tab)
debut = n//2 - 1
fin = n - 1
while debut >= 0:
faire_tas(tab, debut, n)
debut -= 1
while fin > 0:
tab[fin], tab[0] = tab[0], tab[fin]
faire_tas(tab, 0, fin)
fin -= 1

Didier Mller

9-11

mai 2015

Algorithmique

9.6.

Tester si un point est dans un polygone

Dans le plan, tant donn un polygone simple (dont les artes ne se coupent pas), comment
dterminer si un point se trouve l'intrieur ou l'extrieur de ce polygone ?

Mthode
La mthode la plus utilise est de tracer une demi-droite de ce point vers l'infini et de compter les
intersections entre cette droite et les segments du polygone.
Tracer une demi-droite partir du point vers l'infini.
Calculer le nombre L d'intersections entre cette demi-droite et les cts du polygone.
Un nombre L impair indique que le point est l'intrieur.
Il faut bien sr faire attention aux coins qui tombent juste sur la demi-droite.

Nombre pair d'intersection :


point l'extrieur

9.6.1.

Nombre impair d'intersections :


point l'intrieur

Pour savoir si deux segments se coupent

La premire chose faire est de trouver une mthode


pour localiser un point P par rapport une ligne passant
par les points P0 et P1. Le dessin ci-dessous illustre les
trois possibilits.
Pour diffrencier ces trois cas, on peut utiliser les
dterminants. En effet, rappelons-nous qu'un dterminant
peut tre interprt comme une aire signe.
Ainsi, sur le dessin ci-contre :
dt(P0P1, P0P2) > 0
dt(P0P1, P0P3) < 0
dt(P0P1, P0P4) = 0
o les PiPj dsignent des vecteurs-colonnes
et dt un dterminant 2x2.
On dira que :
orientation(P0, P1, P2) = 1
orientation(P0, P1, P3)= 1
orientation(P0, P1, P4)= 0
D'o la condition ci-dessous :
SI orientation(Q0,Q1,P0) orientation(Q0,Q1,P1)
ET orientation(P0,P1,Q0) orientation(P0,P1,Q1) ALORS
RETOURNER les deux segments se coupent

Didier Mller

9-12

mai 2015

L'informatique au lyce

Chapitre 9

Exercice 9.7
crivez un programme Python qui implmente la mthode vue ci-dessus. Le polygone sera donn
par la liste de ses sommets. Vous trouverez sur le site compagnon une bauche complter.

9.7.

Enveloppe convexe

Imaginons une planche avec des clous qui dpassent. Englobons ces points avec un lastique que
l'on relche. Le polygone obtenu (en bleu ci-dessous) est l'enveloppe convexe (convex hull).

En trois dimensions, l'ide serait la mme avec un ballon qui se dgonflerait jusqu' tre en
contact avec tous les points qui sont la surface de l'enveloppe convexe.

9.7.1.

Marche de Jarvis (Gift wrapping algorithm)

La marche de Jarvis est un algorithme qui enveloppe un ensemble de points dans un papier
cadeau : on accroche ce papier un point initial p1, puis on le tend, et on tourne autour du nuage de
points Le premier point rencontr par le papier sera p1, puis p2, ... jusqu' retrouver p0.
On construit donc l'enveloppe convexe segment par segment, en partant du point p0. Il ne doit y
avoir aucun point gauche du prochain segment pi pi+1. Il n'y a
qu'un point pi+1 qui satisfait cette condition ; il faut le chercher
parmi les points qui ne sont pas encore sur l'enveloppe convexe.
On s'arrte lorsque pi+1 = p0.
Remarquons que les points ne sont pas tris, alors que ce sera
le cas pour le parcours de Graham.
Cet algorithme doit son nom R. A. Jarvis, qui publia cet
algorithme en 1973.

Complexit
La complexit est en O(nh), o n est le nombre total de
sommets et o h reprsente le nombre de sommets de
l'enveloppe convexe. On qualifie ce genre d'algorithme de sensible la sortie .

Didier Mller

9-13

mai 2015

Algorithmique

Exercice 9.8
crivez un programme Python qui implmente la marche de Jarvis. Vous trouverez sur le site
compagnon une bauche complter.

9.7.2.

Parcours de Graham (Graham's scan)

Cet algorithme doit son nom Ronald Graham, qui a publi l'algorithme original en 1972.
La premire tape de cet algorithme consiste rechercher le point le plus gauche. S'il y a galit
entre plusieurs points, l'algorithme choisit parmi eux le point de plus petite ordonne. Nommons P0
ce point. La complexit en temps de cette tape est en O(n), n tant le nombre de points de
l'ensemble.
Les autres points Pi sont ensuite tris en fonction de la pente du segment P0Pi, de la plus grande
pente la plus petite. N'importe quel algorithme efficace de tri convient pour cela. l'issue de cette
tape, on dispose d'un tableau T contenant les points ainsi tris. P0 sera le premier lment de ce
tableau.

L'algorithme considre ensuite successivement les squences de trois points contigus dans le
tableau T, vus comme deux segments successifs. On regarde ensuite si ces deux segments mis about
bout constitue un tournant gauche ou un tournant droite .
Si l'on rencontre un tournant droite , l'algorithme passe au point suivant de T.
Si c'est un tournant gauche , cela signifie que l'avant-dernier point considr (le
deuxime des trois) ne fait pas partie de l'enveloppe convexe, et qu'il doit tre enlev de T.
Cette analyse se rpte ensuite, tant que l'ensemble des trois derniers points est un
tournant gauche .

Le processus se terminera quand on retombera sur le point P0. T contiendra alors les points
formant l'enveloppe convexe.

Complexit
Le tri des points peut se faire avec une complexit en temps en O(nlog(n)). La complexit de la
boucle principale peut sembler tre en O(n2), parce que l'algorithme revient en arrire chaque point
pour valuer si l'un des points prcdents est un tournant droite . Mais elle est en fait en O(n),
parce que chaque point n'est considr qu'une seule fois. Ainsi, chaque point analys soit termine la
sous-boucle, soit est retir de T et n'est donc plus jamais considr. La complexit globale de
l'algorithme est donc en O(nlog(n)), puisque la complexit du tri domine la complexit du calcul
effectif de l'enveloppe convexe.
Didier Mller

9-14

mai 2015

L'informatique au lyce

Chapitre 9

Exercice 9.9
crivez un programme Python qui implmente le parcours de Graham. Vous trouverez sur le site
compagnon une bauche complter.

9.8.

Algorithmes probabilistes

Un algorithme probabiliste est un algorithme dont le droulement fait appel des donnes tires
au hasard. Dans leur ouvrage Algorithmique, conception et analyse, G. Brassard et P. Bratley
classent les algorithmes probabilistes en quatre catgories :

Algorithmes numriques

Utiliss pour approcher la solution des problmes numriques (ex. calcul de pi, intgration
numrique, etc.).
La prcision augmente avec le temps disponible.
Certains auteurs classent ces algorithmes dans la catgorie Monte Carlo

Algorithmes de Sherwood

Utiliss lorsqu'un algorithme dterministe fonctionne plus rapidement en moyenne qu'en


pire cas.
Ces algorithmes peuvent liminer la diffrence entre bonnes et mauvaises entres.
Exemple : Quicksort

Algorithmes de Las Vegas

Ces algorithmes peuvent parfois retourner un message disant qu'ils n'ont pas pu trouver la
rponse.
La probabilit d'un chec peut tre rendu arbitrairement petite en rptant l'algorithme
suffisamment souvent.

Algorithmes de Monte Carlo

Ces algorithmes retournent toujours une rponse mais celle-ci n'est pas toujours juste.
La probabilit d'obtenir une rponse correcte augmente avec le temps disponible.

Exercice 9.10
Modifiez le programme de l'exercice 9.7 qui teste si un point est l'intrieur d'un polygone, afin
d'estimer l'aire de ce polygone. Pour ce faire, vous gnrerez 100'000 points au hasard et calculerez
le pourcentage de points tombs l'intrieur du polygone.

Exercice 9.11
Dveloppez et programmez une mthode base sur l'estimation de l'aire d'un quart de cercle pour
approcher la valeur de .

Exercice 9.12 : Le compte est bon (2)


Nous avons vu au 7.5 un algorithme rcursif cherchant les solutions du jeu. Nous allons ici
nous contenter d'une mthode nave et peu efficace, mais facile programmer : il s'agira de
rechercher alatoirement des solutions et de ne garder que celle qui se rapproche le plus du rsultat
demand, ou qui l'atteint.
Donnes : six nombres dans une liste L et le rsultat r approcher.
1.
2.

Didier Mller

Choisir deux nombres a et b au hasard dans la liste L.


Choisir une opration arithmtique (+, -, *, /) au hasard. L'opration doit
tre possible (par exemple, on ne peut pas diviser 5 par 9) et utile (il est

9-15

mai 2015

Algorithmique

3.
4.
5.
6.
7.

inutile par exemple de multiplier un nombre par 1).


Poser c := opration(a, b). Mmoriser ce calcul intermdiaire dans une chane
de caractres (une string).
liminer a et b de la liste L.
Ajouter c la liste L.
SI c = r ALORS
afficher tous les calculs intermdiaires.
STOP
SI il y a plus d'un nombre dans la liste ALORS
aller 1
SINON afficher la liste des calculs intermdiaires et le rsultat obtenu

On rptera cet algorithme des milliers de fois et on n'affichera que la meilleure solution trouve.
Programmez cet algorithme en Python.

Exercice 9.13
Modifiez le programme du 8.11.2 pour trouver la plus belle grille de Ruzzle grce un
algorithme probabiliste. Par plus belle , on entend celle qui contient le plus de mots franais.
Vous utiliserez le dictionnaire du 8.10 qui est disponible sur le site web compagnon :
http://ow.ly/35Jlt
Voici les frquences des lettres de ce dictionnaire (on a analys tous les mots de 2 16 lettres
sans tiret et sans apostrophe) :
E

14.89% 10.21%

9.71%

9.40%

8.67%

7.34%

6.82%

5.82%

4.01%

3.60%

3.40%

2.54%

2.36%

2.35%

1.60%

1.40%

1.36%

1.16%

1.07%

0.96%

0.50%

0.34%

0.25%

0.18%

0.05%

0.01%

9.9.

Le problme des n dames pour illustrer les


mtaheuristiques

Le problme des n dames est une gnralisation de du problme des 8 dames, vu au paragraphe
9.3 : on considre un chiquier nxn au lieu d'un chiquier 8x8. Bien que ce ne soit pas proprement
parl un problme d'optimisation, il prsente de nombreux avantages :
il est visuel et facile comprendre ;
on peut coder la position des dames trs simplement : pour chaque colonne, on note sur
quelle ligne se trouve la dame, et on lui soustrait 1. La
position ci-contre sera : [1, 3, 5, 7, 2, 0, 6, 4]
on passe trs facilement d'une configuration une
configuration voisine (qui ne satisfait pas forcment les
contraintes du problme) : il suffit d'changer deux
colonnes. Une position voisine de celle ci-contre pourrait
tre [1, 0, 5, 7, 2, 3, 6, 4].
On peut le traiter comme un problme d'optimisation si l'on
considre qu'il faut minimiser le nombre de conflits (on parlera de
conflit quand deux dames se menacent mutuellement). Il s'agira ici
de placer n dames sur l'chiquier nxn, sans aucun conflit, en partant
d'une solution avec une seule dame par ligne et par colonne (par
exemple toutes les dames sur la diagonale) et en changeant deux
colonnes. On ne cherchera pas toutes les solutions possibles : une
seule nous suffira. Notons que dans un problme d'optimisation classique, il n'y a en gnral qu'une
seule meilleure solution. Ici il y en a plusieurs.
Il est noter qu'il existe un algorithme permettant de trouver une solution quel que soit n
suprieur 3 (voir exercice 9.14). L'intrt du 9.9 n'est donc pas de trouver une solution, mais
d'illustrer sur ce problme classique comment se comportent trois des mtaheuristiques les plus
connues : la recherche avec tabous, le recuit simul et un algorithme gntique.

Didier Mller

9-16

mai 2015

L'informatique au lyce

Chapitre 9

Exercice 9.14
1.
2.
3.
4.
5.
6.

soit r = n mod 12
crire les nombres pairs de 2 n
si r = 3 ou r = 9 mettre le 2 la fin de la liste
crire ensuite les nombres impairs de 1 n, mais si r=8 permuter les nombres
impairs 2 par 2 (i.e. 3, 1, 7, 5, 11, 9, ...)
si r = 2, permuter les places de 1 et 3, puis mettre 5 la fin de la liste
si r = 3 ou r = 9, mettre 1 puis 3 en fin de liste

Programmez cet algorithme en Python. Il donnera une solution au problme des n dames. Ainsi,
pour n=8 on obtient la position [2, 4, 6, 8, 3, 1, 7, 5] (dans cet algorithme, les lignes sont numrotes
partir de 1). Pour n=15, on aura la solution [4, 6, 8, 10, 12, 14, 2, 5, 7, 9, 11, 13, 15, 1, 3].
Vrifiez la validit de cet algorithme pour n = 4 ... 1000.

9.9.1.
1.
2.
3.

Premire approche : descente de plus grande pente


Gnrer une position de dpart.
Essayer toutes les permutations de deux colonnes et changer les deux
colonnes qui permettent de diminuer le plus le nombre de conflits.
Retourner 2 jusqu' obtenir 0 conflit (dans l'idal) ou un blocage.

Rsultats avec la descente de plus grande pente


Nombre de dames (n)
Temps ou nombre de conflits restants

20
2 conflits

40
13 sec.

60
1 conflit

80
418 sec.

100
1308 sec.

Commentaires

Selon la position initiale et le nombre de dames, il arrive que l'algorithme se bloque dans un
minimum local. C'est arriv ici avec 20 et 60 dames places au dpart sur la diagonale.
Avec 100 dames, on passe d'une situation avec 4950 conflits une configuration sans
conflit en 82 changes de colonnes.
Les temps sont seulement indicatifs et dpendent videmment de l'ordinateur utilis. Toutes
les expriences ont t faites sur le mme ordinateur et dans les mmes conditions.

Exercice 9.15
Modifiez le programme de l'exercice 9.13 pour rechercher la meilleure grille de Ruzzle grce
une descente de plus grande pente.

9.9.2.

Deuxime approche : recherche avec tabous

La mthode taboue est une mtaheuristique d'optimisation prsente par Fred Glover en 1986.
On trouve souvent l'appellation recherche avec tabous en franais.
La mthode taboue consiste, partir d'une position donne, explorer le voisinage et choisir la
position dans ce voisinage qui minimise la fonction objectif (comme dans la descente de plus grande
pente). Il est essentiel de noter que cette opration peut conduire dgrader la valeur de la fonction :
c'est le cas lorsque tous les points du voisinage ont une valeur plus leve. Le risque est qu' l'tape
suivante, on retombe dans le minimum local auquel on vient d'chapper. C'est pourquoi il faut que
l'heuristique ait de la mmoire : le mcanisme consiste interdire (d'o le nom de tabou ) de
revenir sur les dernires positions explores.
Les positions dj explores sont conserves dans une file (voir 6.2), souvent appele liste des
tabous, d'une taille donne. Cette file doit conserver des positions compltes, mais cela ne pose pas
de problmes avec les n dames. Pour nos expriences, nous avons utilis une liste de 10 mouvements
tabous.
Mthode taboue
Rappelons que l'on
passe d'une position

Didier Mller

1.
2.
3.

Gnrer une position de dpart.


Parmi les positions voisines, choisir la meilleure qui n'est pas dans la
liste des tabous.
Si la liste des tabous est pleine, retirer de cette liste la position la plus

9-17

mai 2015

Algorithmique
une position
voisine en
changeant deux
colonnes.

4.
5.

ancienne.
Mettre la position actuelle en queue de la liste des tabous.
Retourner 2, tant qu'on n'a pas dcid de s'arrter.

Rsultats avec la mthode taboue


Nombre de dames (n)
Temps ou nombre de conflits restants

20

40

60

80

100

1 sec.

15 sec.

114 sec.

422 sec.

1756 sec.

Commentaires

Le programme ne se bloque plus dans un minimum local.


La solution finale est toujours la mme et dpend de la position initiale.
Avec 100 dames, on passe d'une situation avec 4950 conflits une configuration sans
conflit en 95 changes de deux colonnes.

Exercice 9.16
Modifiez le programme de l'exercice 9.13 pour rechercher la meilleure grille de Ruzzle grce
une recherche avec tabous.

9.9.3.

Troisime approche : recuit simul

Le recuit simul (Simulated Annealing en anglais) est une mtaheuristique inspire d'un
processus utilis en mtallurgie. Ce processus alterne des cycles de refroidissement lent et de
rchauffage (recuit) qui tendent minimiser l'nergie du matriau. Elle est aujourd'hui utilise en
optimisation pour trouver les extrema d'une fonction.
Elle a t mise au point par trois chercheurs de la socit IBM, S. Kirkpatrick, C.D. Gelatt et
M.P. Vecchi en 1983, et indpendamment par V. Cerny en 1985.
Recuit simul
1.
2.
3.
4.
5.
Remarque sur le
point 4
Ici, on cherche
minimiser le score,
qui est le nombre
de conflits.
Si on cherche
maximiser le score,
il faut calculer
= score(P1)
score(P2)

6.
7.
8.
9.

Choisir une temprature de dpart T.


Gnrer une position alatoire. Appelons-la
Copier la position P1 dans une position P2,
choisies alatoirement.
Calculer = score(P2) score(P1)
Si 0, P1 P2 ; le cas chant, mettre
meilleure position. Aller 8.
Gnrer un nombre rel alatoire entre 0 et
Si r < exp(/T), P1 P2.
Diminuer T.
Retourner 3, tant qu'on n'a pas dcid de

P1.
puis changer deux colonnes de P1

jour le meilleur score et la


1, que nous appellerons r.
s'arrter.

Le rglage des paramtres est ici plus dlicat. En particulier, comment faut-il faire baisser la
temprature ? Trop vite, on risque de se bloquer dans un minimum local. Trop lentement, le temps de
calcul augmentera, sans garantie de trouver une solution.
La solution retenue a t de faire baisser la temprature par palier de longueur 10, avec une
temprature initiale T=100, avec Tk+1=0.6Tk, k reprsentant un numro de palier. C'est trs inhabituel,
car on prend gnralement un coefficient proche de 1.

Rsultats avec le recuit simul


Nombre de dames (n)
Temps moyen en cas de succs*
Nombre de succs sur 10 essais

20
0.3 sec.
7

40
1.5 sec
10

60
4 sec.
10

80
9 sec.
10

100
23 sec.
10

*Il s'agit d'une moyenne sur 10 essais, puisque le hasard joue ici un rle important. Les temps des
essais o l'on n'a pas trouv de solution n'ont pas t pris en compte.

Didier Mller

9-18

mai 2015

L'informatique au lyce

Chapitre 9

Commentaires

tant donn l'usage du hasard, on ne sait pas quelle solution finale sera trouve. Ce ne sera
pas toujours la mme, contrairement la recherche avec tabous.
Curieusement, le recuit simul marche le moins bien quand il y a peu de dames (7 succs
seulement avec 20 dames).
Il est par contre redoutable en temps de calcul, puisqu'il ne lui faut que 23 secondes pour
trouver une solution avec 100 dames, alors que la mthode taboue en mettait 1756.

Exercice 9.17
Modifiez le programme de l'exercice 9.13 pour rechercher la meilleure grille de Ruzzle grce un
recuit simul.

9.9.4.

Quatrime approche : algorithme gntique

Les algorithmes gntiques appartiennent la famille des algorithmes volutionnistes (un sousensemble des mtaheuristiques). Leur but est d'obtenir une solution approche, en un temps correct,
un problme d'optimisation, lorsqu'il n'existe pas ou qu'on ne connat pas de mthode exacte pour
le rsoudre en un temps raisonnable. Les algorithmes gntiques utilisent la notion de slection
naturelle dveloppe au XIXe sicle par le scientifique Darwin et l'appliquent une population de
solutions potentielles au problme donn. On se rapproche par bonds successifs d'une solution.
L'utilisation d'algorithmes gntiques, dans la rsolution de problmes, est l'origine le fruit des
recherches de John Holland et de ses collgues et lves de l'Universit du Michigan qui ont, ds
1960, travaill sur ce sujet. Le premier aboutissement de ces recherches a t la publication en 1975
de Adaptation in Natural and Artificial System.
Algorithme gntique
1.
2.
3.
4.
5.
6.
7.
8.

Gnrer une population de 2n positions alatoires.


Classer et numroter ces 2n positions selon leur score, du meilleur au moins
bon.
Croiser les grilles 2k1 et 2k, pour k allant de 1 n.
Effectuer une mutation pour chaque position : avec une certaine probabilit,
effacer une case et placer un nouveau chiffre de 0 7.
Classer et numroter les 2n grilles obtenues selon leur score, du meilleur au
moins bon.
Dupliquer la grille 1 (la meilleure) et placer ce doublon en position 2n,
aprs avoir limin la grille 2n (la moins bonne).
Le cas chant, mettre jour le meilleur score et la meilleure position.
Retourner 3, tant qu'on n'a pas dcid de s'arrter.

Schma de l'algorithme gntique

Quelques commentaires sur ce schma

Didier Mller

Chaque position est reprsente pas une liste de n nombres indiquant la ligne occupe pour
la colonne correspondante.
9-19

mai 2015

Algorithmique
Avec 8 dames, la position ci-dessous est reprsente par la liste [0, 5, 1, 4, 6, 3, 7, 2].

Chaque position est value grce la fonction de performance, ici ce sera le nombre de
conflits. (b)
Une phase de reproduction dtermine quelles positions seront slectionnes pour la
reproduction. Certaines positions peuvent tre reproduites plusieurs fois, d'autres
disparatront. (c)
Pour chaque paire se combinant, on dtermine alatoirement le point de croisement. (c)
Les enfants sont crs en croisant chaque paire. (d)
Finalement, chaque position subit ventuellement une mutation alatoire. (e)

Commentaire

Cette approche n'a (pour l'instant) pas donn de rsultats intressants. Elle ne fonctionne
qu'avec de petits damiers. Il semble difficile de choisir les diffrents paramtres.

Exercice 9.18
Modifiez le programme de l'exercice 9.13 pour rechercher la meilleure grille de Ruzzle grce un
algorithme gntique.

Sources
[1] Wikipdia, Algorithmique , <http://fr.wikipedia.org/wiki/Algorithmique>
[2] Wikipdia, Algorithmes de tri ,
<http://fr.wikipedia.org/wiki/Catgorie:Algorithme_de_tri>
[3] Wikipdia, Marche de Jarvis , <http://fr.wikipedia.org/wiki/Marche_de_Jarvis>
[4] Wikipdia, Parcours de Graham , <http://fr.wikipedia.org/wiki/Parcours_de_Graham>
[5] Wikipdia, Test de primalit de Miller-Rabin ,
<http://fr.wikipedia.org/wiki/Test_de_primalit_de_Miller-Rabin>
[6] Wikipdia, Mta-heuristique , <http://fr.wikipedia.org/wiki/Mtaheuristique>
[7] Wikipdia, Recuit simul , <http://fr.wikipedia.org/wiki/Recuit_simul>
[8] Wikipdia, Algorithme gntique , <http://fr.wikipedia.org/wiki/Algorithme_gntique>
[9] Le labyrinthe dont vous tes le hros,
<http://eljjdx.canalblog.com/archives/2011/02/20/20431821.html>

Didier Mller

9-20

mai 2015

S-ar putea să vă placă și