3. Chapitre 1
Introduction
Ce rapport s’interesse à un problème d’algorithmique géométrique très répandu. Elle se
résume de la manière suivante : " Comment déterminer un polygone qui contient un ensemble
de points donné ? ".
C’est cette problématique que nous allons essayer de résoudre en mettant en avant les avan-
tages et les inconvénients de nos solutions. Pour ce faire, nous allons adopter deux stratégies :
• Le cercle qui consiste à déterminer un cercle qui contient l’ensemble de points donnée.
• L’enveloppe convexe qui consiste à déterminer un polygone qui contient l’ensemble de
points donnée.
Dans un premier temps, nous resolverons ces problèmatiques par un algorithme naif et
par la suite nous verrons ce qu’apportent Ritter et Graham face à ces problèmes.
Concernant la partie expérimentation, nous utiliserons le génerateur de nombre aléatoire
de Java 7. En effet, d’après la spécification, Java 7 implémente un générateur linéaire
congruentiel. Java 7 nous affirme que ce générateur est à distribution uniforme et qu’il gé-
nère des nombres sur 32 bits. C’est un algorithme plus que suffisant pour la simulation de
nos cas de test.
Nous fixerons une graine arbitrairement au générateur afin de pouvoir rejouer plus fois les
mêmes simulations et pour réaliser du debuggage.
4. Chapitre 2
Théorie
2.1 Le cercle minimum
Ecrivons un algorithme naif en Java permettant de déterminer le cercle minimum et
décrivons cette algorithme.
public Circle calculCercleMinCouvrantNaif(ArrayList <Point >
points) {
// Initialisation des valeurs
int rayonMin = Integer.MAX_VALUE;
Point centreMin = points.get(0);
int rayonCourant;
// pour tout les triplets de points
for(Point a : points){
for(Point b : points){
for(Point c :points){
if(!a.equals(b) && !b.equals(c)){
// On trouve le cercle circonscrit de a,b,c
Circumcircle cc = new Circumcircle(a,b,c);
// si le cercle existe
if(cc.hasCircumcircle ()){
Point centre = new Point(
(int)cc.getCenter ().getX(),
(int)cc.getCenter ().getY());
rayonCourant = (int) dist(centre ,a);
boolean tousDansLeCercle = true;
5. 2.2 Approximation de Ritter 3
// on verifie que tout les points sont dans
le cercle
for (Point p : points) {
if(!p.equals(a) && !p.equals(b) &&
!p.equals(c)
!cc.containsPoint(new DPoint(p.x,
p.y))){
tousDansLeCercle = false;
break;
}
}
// si le rayon est le plus petit
// et que tout les points sont
// dans le cercle
if(tousDansLeCercle &&
rayonCourant <rayonMin){
rayonMin = rayonCourant;
centreMin=centre;
}
}
}
}
}
}
return new Circle(centreMin , rayonMin);
}
La compléxité temporelle de cette algorithme est borné par la quadruple boucle for et est
donc de Θ(n4
)
2.2 Approximation de Ritter
Ecrivons l’algorithme décrit par Ritter en Java permettant de déterminer le cercle mi-
nimum et décrivons cette algorithme.
public Circle CercleMinRitter (ArrayList <Point > points) {
// Initialisation des variables
int pos_alea = (int) (Math.random ()*( points.size()));
6. 2.2 Approximation de Ritter 4
Point dummy = points.get(pos_alea);
Point P = null;
Point Q = null;
Point C = null;
Point S = null;
Point Cbis = null; // C'
Circle circle;
double distance = 0;
int control = 0;
// borne du nombre de tours pour le while
int max_nb_tours = 200;
// recherche le point le plus eloigne de dummy --> P
for (int i = 0 ; i < points.size() ; i++) {
if (dist(dummy , points.get(i)) > distance) {
P = points.get(i);
distance = dist(dummy , points.get(i));
}
}
// recherche le point le plus eloigne de P --> Q
distance = 0;
for (int i = 0 ; i < points.size() ; i++) {
if (dist(P, points.get(i)) > distance) {
Q = points.get(i);
distance = dist(dummy , points.get(i));
}
}
// Le point C, centre de P et Q
C = new Point((P.x + Q.x) / 2, (P.y + Q.y) / 2);
circle = new Circle(C, (int) dist(C, P));
while (control < max_nb_tours) {
control ++;
C = circle.getCenter ();
// On enleve tout les points qui
// sont dans le cercle
ArrayList <Point > tmp = new ArrayList <Point >();
for (Point p : points) {
7. 2.3 Le parcours de Graham 5
if (dist(C, p) > circle.getRadius ()) {
tmp.add(p);
}
}
points = tmp;
if (points.size() > 0) {
S = points.get((int) (Math.random ()*points.size()));
} else {
break;
}
// S'il reste des points , CS coupe
// le perimetre du cercle en T et S
double cs = dist(C, S);
double cbs = (circle.getRadius () + cs) / 2; // nouveau
rayon
double ccb = cs - cbs; // distance entre C et C'
// Calcul des coordonnees de C'
double x = (ccb/cs) * S.x + (cbs/cs) * C.x;
double y = (ccb/cs) * S.y + (cbs/cs) * C.y;
Cbis = new Point((int) x, (int) y);
circle = new Circle(Cbis , (int) cbs);
}
return circle;
}
La compléxité temporelle de cette algorithme est borné par la boucle for et est donc de
Θ(n)
2.3 Le parcours de Graham
Ecrivons l’algorithme décrit par Graham en Java permettant de déterminer l’enveloppe
convexe et décrivons cette algorithme.
public ArrayList <Point > enveloppeConvexe(ArrayList <Point >
points){
8. 2.3 Le parcours de Graham 6
// Tri des points
points = triParPixel(points);
ArrayList <Point > enveloppe = new ArrayList <Point >();
boolean isOk = true;
// On determine l'enveloppe convexe
// Pour chaque couple de points
for (Point a : points) {
for (Point b : points) {
isOk = true;
Vecteur ab = getVecteur(a, b);
// Un point C dans points different de A et B
Point c = getRandomPoint(points , a, b);
// Signe du produit vectoriel ab et ac
double signe = signeVect(ab, getVecteur(a, c));
for (Point x : points) {
if ((!(x.equals(c) || x.equals(a) ||
x.equals(b)))
&& (signe * Vecteur.signeVect(ab,
getVecteur(a, x))) <= 0) {
isOk = false;
break;
}
}
// Si pour tout les points x, le signe du
// produit vectoriel ab,ac est different
// du produit vectoriel ab, ax
if (isOk) {
enveloppe.add(a);
enveloppe.add(b);
}
}
}
return enveloppe;
}
9. 2.3 Le parcours de Graham 7
La compléxité temporelle de cette algorithme est borné par le tri des points et est donc de
Θ(nlog(n))
10. Chapitre 3
Experimentations
Qu’en est-il de la pratique ? Réalisons une étude statistiques de l’ensemble des algo-
rithmes vu précédemment : l’algorithme naif du cercle minimum, l’algorithme de Ritter et
l’algorithme de Graham. Cette étude se basera uniquement sur le temps d’éxecution et l’aire
obtenu par ces algortihmes. Nous pourrons donc faire une analyse croisée des différents ré-
sultats obtenus.
3.1 Les aires et le temps d’exécution
Le temps d’éxecution des algorithmes est obtenu sur une machine ayant les caractéris-
tiques suivantes : Processeur Intel CORE i7 vPro 3.7 GHz, RAM de 16 Go et un SSD de
500 Go.
Les tests sont fait sur 3540 instances et chaque instance contient 256 points (voir les graphes
3.1 et 3.2).
La graphe 3.1 montre clairement que Aire(Enveloppe convexe) < Aire(Cercle Minimum)
et Aire(Cercle Minimum) < Aire(Cercle Ritter).
En moyenne, Aire(Enveloppe convexe) = 964613, Aire(Cercle Ritter) = 1724529
et Aire(Cercle Minimum) = 1449114.
Le rapport d’aire c’est-à-dire la qualité Ritter/Cercle Minimum est de 0.19 et de Ritter/En-
veloppe convexe est de 0.78. Et, le rapport approximation Ritter/Cercle Minimum est de 0.07.
La graphe 3.2 montre clairement que Execution(Enveloppe convexe) < Execution(Cercle Ritter)
et Execution(Cercle Ritter) << Execution(Cercle Minimum).
En moyenne, Execution(Enveloppe convexe) = 132566 nanosecondes ≈ 0.1 millisecondes,
Execution(Cercle Ritter) = 426273 nanosecondes ≈ 0.4 millisecondes
et Execution(Cercle Minimum) = 4005357042 nanosecondes ≈ 4 secondes.
Le rapport d’exécution Ritter/Enveloppe convexe est de 2.21 et Cercle minimum/Enveloppe
convexe est de 30213.
11. 3.1 Les aires et le temps d’exécution 9
Figure 3.1 –
12. 3.1 Les aires et le temps d’exécution 10
Figure 3.2 –
13. Chapitre 4
Discussion
Ritter est-il un "bon" algorithme ? Quel algorithme choisir ? ...
Du point de vue de l’implantation de ces algorithmes, il est évident que l’algorithme naîf du
cercle minimum est celui à privilégier (implantation en quelques minutes). Les algorithmes
de Ritter et de l’enveloppe convexe sont à ex-aequo après l’algorithme du cercle minimum.
Si on s’interesse à l’aire minimal d’un polygone contenant un ensemble de points, il faut
se tourner vers l’algorithme de l’enveloppe convexe. L’algorithme de Ritter arrive en 3ème
position derrière l’algorithme du cercle minimum.
Concernant la vitesse d’exécution, l’algorithme de l’enveloppe convexe est le plus adapté
(Θ(nlog(n))). L’algorithme de Ritter est en 2nd position (Θ(n))) et l’algorithme du cercle
minimum est en dernière position (Θ(n4
)).
Si l’on a besoin d’un algorithme peu coûteux en espace mémoire, il faudra implanter Ritter
ou le cercle minimum (maintenir un point et un rayon). L’algorithme de l’enveloppe convexe
est, quand à lui, plus coûteux (maintenir une liste de points).
Prenons des exemples concret qui mettent en evidence l’utilisation de ces algorithmes ...
• Cercle Minimum : Un mathématicien qui veut obtenir le plus petit cercle qui contient
un ensemble de points.
• Ritter : Dans le monde du jeux vidéo, cet algorithme est très interessant pour la
détection de collisions. On dispose donc d’un algorithme qui coûte rien en mémoire (
imaginons un open world à la GTA like avec des milliers d’objets) et qui est rapide en
temps d’exécution (la collision entre un personnage et une voiture dans GTA doit être
déterminer très rapidemennt).
• Enveloppe convexe : Dans le monde de la modélisation, cet algorithme doit être le
14. 12
plus utilisé. Dans les logiciels de simulation 3D (les simulateurs de crash de voiture,
l’analyse du mouvement des fluides sur un avion ...), on a besoin de connaître au mieux
et de manière réaliste les objets représentés.
15. Chapitre 5
Conclusion
L’analyse du cercle minimum, de Ritter et de l’enveloppe convexe nous a permis de mettre
en évidence les points forts et les points faibles de ces algorithmes.
L’utilisation de ces algorithmes dépendent du contexte dans lequel on se trouve. Il est né-
cessaire de s’interroger sur l’algorithme le plus intéressant à nos besoins avant de l’implanter.
L’algorithme de Ritter a donc bien sa place face au cercle minimum et à l’enveloppe convexe.
En effet, il sera plus judicieux de l’utiliser lorsque l’on veux un algorithme temporel et spatial
plutôt faible.