Nous allons nous intéresser ici au problème suivant : étant donné un ensemble de points du plan, identifier le couple de points les plus proches.
Ce type d’algorithme a son utilité dans les transports aériens ou maritimes par exemple ( ou de manière plus anecdotique, dans la gestion des rebonds
dans le TP que vous avez fait en introduction du cours sur la POO...)
Nous représenterons un point par un tuple de deux entiers correspondant aux coordonnées cartésiennes (x, y)
du point dans un repère orthonormé.
L’ensemble des points considérés sera quant à lui stocké dans un tableau t. Nous supposerons toujours que tous les couples stockés dans t sont distincts.
Considérons deux points P1(x1, y1) et P2(x2, y2); leur distance dite euclidienne est donnée par la relation : dP1P2 = √(x2 − x1 )2 + (y2 − y1)2.
Écrire une fonction distance_euclidienne
qui calcule la distance euclidienne séparant deux points.
Une méthode « naïve » permettant de déterminer les deux points les plus proches dans un tableau t consiste à considérer successivement tous les couples de points possibles, ce qui peut se faire facilement à l’aide de deux boucles imbriquées.
plus_proches
qui prend comme paramètre un tableau de points t et renvoie les points (x1, y1), (x2 , y2)
situés à la distance minimale
l’un de l’autre, ainsi que cette distance.
plus_proches([(3, 4), (2, -1), (0.5, 2), (3, -4), (-1, -0.5), (-2, 5)]) == (0.5, 2), (-1, -0.5), 2.9154759474226504
plus_proches
en fonction de la taille n du tableau t; en déduire la complexité de cet algorithme.Nous allons maintenant nous intéresser à la mise au point d’un algorithme permettant de résoudre ce même problème en complexité O(n log n).
On appelle P l'ensemble des n points à considérer.
On partage P verticalement en deux sous-ensembles A et B de même taille ( ligne rouge sur l'image ci-contre ); la plus petite distance entre deux points de P est alors celle, soit entre deux points de A, soit entre deux points de B, soit entre un point de A et un point de B.
On calcule alors récursivement la distance minimale dA entre deux points de A ( en rouge à gauche ) et la distance minimale dB entre deux points de B ( en rouge à droite ); on note δ le minimum entre dA et dB.
Il reste alors à calculer la distance minimale entre un point de A et un point de B, ce qui laisse beaucoup de combinaisons...
Mais on réduit en fait les possibilités en écartant toutes les distances supérieures à δ : pour cela, on ne s’intéresse donc qu'aux points situés dans la bande verticale de largeur 2δ de part et d'autre
du milieu de P ( lignes bleues sur la figure ).
Pas évident l'étape combiner !!!
L'algorithme nécessite de disposer de deux copies du tableau t, l’une triée par abscisses croissantes et l’autre par ordonnées croissantes.
tri_x
qui prend en paramètre le tableau t, et qui renvoie un nouveau tableau des points triés par abscisses croissantes.tri_y
qui renvoie un tableau des points triés par ordonnées croissantes.Le travail à faire ci-dessous vous propose 3 niveaux de difficulté décroissante ( ***, ** et * ) pour implémenter cet algorithme pas du tout évident...
A vous de choisir la difficulté qui vous convient.
Compléter la fonction plus_proches_dpr
ci-dessous de façon à implémenter l'algorithme décrit précédemment. Attention aux paramètres et aux résultats renvoyés par cette fonction.
Cette fonction fera appel à celles que vous aurez écrites auparavant.
Pour la construction des différents tableaux, on aura bien entendu intérêt à utiliser des méthodes par compréhension.
Compléter la fonction plus_proches_dpr
ci-dessous de façon à implémenter l'algorithme décrit précédemment. Attention aux paramètres et aux résultats renvoyés par cette fonction.
Cette fonction fera appel à celles que vous aurez écrites auparavant.
Pour la construction des différents tableaux, on aura bien entendu intérêt à utiliser des méthodes par compréhension.
En déplaçant les lignes dans le code ci-dessous, reconstruire l'implémentation correcte de l'algorithme présenté précédemment.
Tester le bon fonctionnement de la fonction plus_proches_dpr
:
Avec le tableau t, il n'y aucune différence dans le temps d’exécution des deux versions de la recherche, voire même l'approche DpR est plus lente à cause de ses multiples appels récursifs que l'on ne trouve pas dans la version naïve.
Mais comme d'habitude, la différence de complexité devient notable pour de grandes valeurs de n.
En utilisant le module timeit, comparer le temps d'exécution des deux versions de la recherche des deux points les plus proches.