Application des arbres binaires. Plan
• Compter les arbres binaires
• Tétrarbres (quad trees)
• Problème des n corps
• Recherche dans un intervalle
• Recherche dans un nuage de points
• Recherche dans un arbre d’intervalles
Rappel : un arbre binaire est soit vide, soit union disjointe d'une racine, d'un sous-arbre gauche et d'un sous-arbre droit.
Si bn est le nombre d'arbres binaires distincts à n nœuds, on a donc :
b0 = 1 bn = bibn-i-1
Compter les arbres binaires (1)
i = 0
Σ
n-1
n ≥ 0
Σ
La série B(x) = bnxn
La résolution de l'équation xB2(x) - B(x) + 1 = 0 donne
bn = =
d'où bn ~ π-1/24nn-3/2 + O(4nn-5/2)
Compter les arbres binaires (2)
(1) Dans un arbre,
nombre de nœuds = 1 + nombre d'arcs
(2) Dans un arbre binaire complet (tout nœud est d'arité 0 ou 2),
nombre de feuilles = 1 + nombre de nœuds internes
( 2nn )
1 n+1
(2n)!
n!(n+1)!
Application des arbres binaires. Plan
• Compter les arbres binaires
• Tétrarbres (quad trees)
• Problème des n corps
• Recherche dans un intervalle
• Recherche dans un nuage de points
• Recherche dans un arbre d’intervalles
Une vision hiérarchique des images
Une image est :
• soit de couleur uniforme
• soit formée de quatre images
NO NE
SO SE SO NO NE SE
Tétrarbres (quad trees)
Une image 2n x 2n est subdivisée itérativement en quadrants jusqu'au niveau du pixel.
NO NE
Tétrarbre associé à une image
Une échelle de 32 couleurs
Tétrarbre associé à une image
Tétrabres en Java (1)
class Tetrarbre {
boolean couleur; // version noir et blanc boolean estFeuille;
Tetrarbre so, no, ne, se;
// Constructeur des feuilles Tetrarbre(boolean couleur) {
this.estFeuille = true;
this.couleur = couleur;
}
Tétrarbres en Java (2)
...
// Constructeur des nœuds internes
Tetrarbre(Tetrarbre so, Tetrarbre no, Tetrarbre ne, Tetrarbre se)
{
this.estFeuille = false;
this.so = so; this.no = no;
this.ne = ne; this.se = se;
} }
static boolean[][] image; // pixels
Tétrarbres en Java (3)
static boolean estMonochrome(
Tetrarbre so, Tetrarbre no, Tetrarbre ne, Tetrarbre se) {
return (so.estFeuille && no.estFeuille
&& ne.estFeuille && se.estFeuille &&
so.couleur == no.couleur &&
so.couleur == ne.couleur &&
so.couleur == se.couleur);
}
i
i + t
j j + t
pixel (i, j)
t = taille/2
SE
NO NE
SO
Tétrarbres en Java (4)
static Tetrarbre faireArbre(int taille, int i, int j) {
if (taille == 1)
return new Tetrarbre(image[i][j]);
int t = taille/2;
Tetrarbre so = faireArbre(t, i+t, j);
Tetrarbre no = faireArbre(t, i, j);
Tetrarbre ne = faireArbre(t, i, j+t);
Tetrarbre se = faireArbre(t, i+t, j+t);
if (estMonochrome(so, no, ne, se)) return new Tetrarbre(so.couleur);
Codage
On code l'arbre par un parcours préfixe. Les nœuds internes sont codés 0 et les feuilles 1. Après chaque 1, on code la couleur de la feuille (0 pour blanc, 1 pour bleu).
0110101110110011101110100111010111101010010111001011101111
Equivaut au code :
On pourrait améliorer le code :
00 01 1 0
11 10
Tétrabres en Java (5)
static void parcours(Tetrarbre a) { if (a != null) {
if (a.estFeuille)
System.out.print("1" +
((a.couleur) ? "1" : "0"));
else // nœud interne
System.out.print("0");
parcours(a.so);
parcours(a.no);
parcours(a.ne);
parcours(a.se);
Autre avantage des tétrarbres
• On peut changer facilement la couleur d'une image.
• Faire une rotation d'un quart de tour.
NO NE
SO SE
SO NO NE SE
NE SE
NO SO
NO NE SE SO
Application des arbres binaires. Plan
• Compter les arbres binaires
• Tétrarbres (quad trees)
• Problème des n corps
• Recherche dans un intervalle
• Recherche dans un nuage de points
• Recherche dans un arbre d’intervalles
Problème des n corps
• Objectif : simuler le comportement de n particules (force de gravité ou électrostatique).
• Solution directe, itérer les calculs suivants :
– les n(n-1) forces F(p, q) entre 2 particules p et q, – la résultante des forces pour chaque particule, – le mouvement résultant pour une unité de temps.
• Trop lent pour n grand.
• Calcul hiérarchique
– calculs exacts pour des particules proches;
– on assimile les particules distantes à leur barycentre.
Algorithme de Barnes-Hut
• Octabres: c'est la version 3D des tétrarbres : un cube est divisé récursivement en 8 cubes.
• Calcul de la force F(p, C), sur la particule p, issue du cube C de coté c et de barycentre b.
– Si C ne contient qu'une particule q, F(p, C) = F(p, q) – Si la distance de p à b est > α.c (par ex. α = 2 ou 3),
F(p, C) = F(p, b) – Sinon
F(p, C) = résultante (F(p, C1), ..., F(p, C8))
1 2
3 4
5
6 7
8 9
12
10 11
1
2 3
4 5 6 7
8
9
10 11
12
Tétrarbre associé
1 2 3 4
2 4 1 3 4 3 4
1 3 1 4 2 4
2 4
Réalisation
• Données : pour chaque cube, la masse totale et le barycentre. Ce calcul peut se faire récursivement.
• Gestion de l'octarbre
– L'arbre est reconstruit à chaque unité de temps.
– Cas le pire en O(n2).
– En pratique, arbre de hauteur O(log n).
• Pour une distribution uniforme en 3D, le nombre d'interactions à calculer est
28πα3
Résultats expérimentaux
• Simulation d'un système de 5000 particules : 10 fois plus rapide qu'avec l'algorithme direct.
• L'algorithme est facilement parallélisable.
• La simulation réaliste d'une galaxie nécessite des millions d'étoiles.
• On connaît un algorithme en O(n). En 1994, on a simulé 107 particules pour 103 pas de calcul (une semaine de calcul sur 500 processeurs...)
Application des arbres binaires. Plan
• Compter les arbres binaires
• Tétrarbres (quad trees)
• Problème des n corps
• Recherche dans un intervalle
• Recherche dans un nuage de points
• Recherche dans un arbre d’intervalles
Recherche en une dimension
Problème : Compter les éléments d’un ensemble d’entiers qui sont dans un intervalle donné [min, max].
Exemple : E = {7, 9, 10, 12, 15, 20}
Entre 12 et 19, il y a 2 éléments.
L’ensemble est donné par un arbre binaire de
recherche. Le prétraitement se fait en O(n log n), la recherche se fait en temps O(r + log n) où r est le nombre d'éléments trouvés.
Exemple
20 10
15
3 9
7
12
25 6
• Entre 8 et 19, il y a 4 éléments.
• On peut couper les sous-arbres issus de 3 (à gauche de 7) et de 25 (à droite de 20).
Exemple plus compliqué
Entre 25 et 75
90 50
40 80 7
60
70 65 79
72 30
55
En Java
static int compter(Arbre a, int min, int max) {
int total = 0;
if (a == null) return total;
if (a.contenu >= min)
total += compter(a.filsG, min, max);
if (a.contenu >= min
&& a.contenu <= max) total++;
if (a.contenu <= max)
total += compter(a.filsD, min, max);
Application des arbres binaires. Plan
• Compter les arbres binaires
• Tétrarbres (quad trees)
• Problème des n corps
• Recherche dans un intervalle
• Recherche dans un nuage de points
• Recherche dans un arbre d’intervalles
Recherche en deux dimensions
Problème : Etant donné un ensemble de points dans le plan, déterminer ceux qui sont dans une zone donnée.
Exemples :
• Trouver les villes à moins de 100 km de Tours
• Trouver les personnes entre 25 et 29 ans qui gagnent entre 1000 et 2000 euros par mois.
A J
D B
C
K H
G
F L
E
I
Recherche en deux dimensions
Problème : Nombre de points dans un rectangle donné.
Solution en deux étapes
1. Prétraitement : un arbre pour le nuage de points 2. Réponse : une recherche dans l’arbre
Arbres pour des points
class Point { // Conflit avec MacLib ! int x, y;
Point(int a, int b) { x = a; y = b;
} }
class Arbre { Point p;
Arbre filsG, filsD;
Arbre(Arbre g, Point v, Arbre d) { filsG = g; p = v; filsD = d;
Arbre pour un nuage de points
Construire un arbre comme suit
• le premier point coupe le plan en deux, horizontalement.
• les points coupent des secteurs horizontalement aux niveaux pairs, et verticalement aux niveaux impairs.
Exemple
A
A J
D B
C
K H
G
F L
E
I
Exemple
A
A J
D B
C
K H
G
F L
E
I B
B est fils gauche de A car B.y < A.y
Exemple
A
A J
D B
C
K H
G
F L
E
I B
C
Exemple
A
A J
D B
C
K H
G
F L
E
I B
D C
D est fils droit de B car D.x > B.x
Exemple
A
A J
D B
C
K H
G
F L
E
I B
D C
E
E est fils droit de A car E.y > A.y
Insérer un point : au début
static Arbre ajouter(Point p, Arbre a) {
return ajouter(p, a, false);
}
static Arbre ajouter(Point p, Arbre a, boolean vertical)
{ ... }
• A la racine, le niveau est impair
Insérer un point : une étape
static Arbre ajouter(Point p, Arbre a, boolean vertical)
{
if (a == null)
return new Arbre(null, p, null);
boolean estAGauche = (vertical) ?
(p.x < a.p.x) : (p.y < a.p.y);
if (estAGauche) // insérer à gauche
return new Arbre(ajouter(p, a.filsG,
!vertical), a.p, a.filsD);
else // insérer à droite
return new Arbre(a.filsG, a.p,
La classe Rectangle
class Rectangle {
int g, d, b, h;
}
g : abcisse du côté gauche d : abcisse du côté droit b : ordonnée du côté bas h : ordonnée du côté haut
g d
b h
Le décompte (1)
static int compter(Rectangle r, Arbre a, boolean vertical) { if (a == null)
return 0;
int total = 0;
boolean aGauche = r.g <= a.p.x;
boolean aDroite = a.p.x < r.d;
boolean enBas = r.b <= a.p.y;
boolean enHaut = a.p.y < r.h;
boolean min, max;
min = (vertical) ? aGauche : enBas;
max = (vertical) ? aDroite : enHaut;
Le décompte (2)
static int compter(Rectangle r, Arbre a, boolean vertical) { ... // suite
if (min)
total += compter(r, a.filsG,
!vertical);
if (aGauche && aDroite &&
enBas && enHaut) total++;
if (max)
total += compter(r, a.filsD,
!vertical);
return total;
Application des arbres binaires. Plan
• Compter les arbres binaires
• Tétrarbres (quad trees)
• Problème des n corps
• Recherche dans un intervalle
• Recherche dans un nuage de points
• Recherche dans un arbre d’intervalles
Recherche d'un intervalle coupant
Problème : Chercher, dans un ensemble
d’intervalles, un élément qui rencontre un intervalle donné.
Exemple : Dans une base d’événements, chercher un événement qui a eu lieu durant une période donnée.
Type abstrait de données
Données : Un ensemble d'intervalles fermés [g, d].
Opérations :
• ajouter un intervalle
• supprimer un intervalle
• chercher si l'ensemble contient un intervalle qui rencontre un intervalle donné v.
Structure de données
• Les intervalles sont stockés dans un arbre binaire de recherche.
• La clé utilisée pour comparer les nœuds est l'extrémité gauche des intervalles.
• Chaque noeud n contient une information
supplémentaire, dmax, qui est le maximum des extrémités droites des intervalles du sous-arbre issu de n.
Exemple
[22,25] rencontre [11,14] non
0:3 6:10
5:810 15:23
23 17:19
20
19:20
26:26 26 25:30
30 16:21
30 8:923
dmax = max des extrémités droites du sous-arbre.
Utilisation de dmax (1)
dmax = max des extrémités droites du sous-arbre.
Détermine dans quel sous-arbre chercher.
0:33 6:10 10
5:810 15:23
23 17:19
20
19:20 20
26:26 26 25:30
30 16:21
30 8:923
Intervalle [g, d]. Règle 1
Si filsG.dmax < g,
inutile de chercher dans le sous-arbre gauche.
Intervalle [22, 25]
0:3 6:10
5:810 15:23
23 17:19
20
19:20
26:26 26 25:30
30 16:21
30 8:923
Règle 2 (justifiée plus loin)
dmax = 23 > 22 dmax = 10 < 22 Intersection !
Intervalle [g, d]=[22, 25]
0:33 6:10 10
5:810 15:23
23 17:19
20
19:20 20
26:26 26 25:30
30 16:21
30 8:923
Si filsG.dmax ≥ g,
inutile de chercher dans le sous-arbre droit.
Utilisation de dmax
dmax = 23 > 11 dmax = 10 < 11 pas d'intersection !
Intervalle [11, 14]
0:3 6:10
5:810 15:23
23 17:19
20
19:20
26:26 26 25:30
30 16:21
30 8:923
Justification
Soit v = [g, d]. Soit [g', d'] un intervalle du sous-arbre gauche d'extrémité droite maximale. Par hypothèse, on a d' ≥ g.
Supposons qu’aucun intervalle du sous-arbre gauche ne rencontre [g, d]. Alors
[g, d] ∩ [g', d'] = ∅, d'où d < g'.
Soit [g", d"] un intervalle du sous-arbre droit. On a g <
d < g' < g" < d", donc [g, d] et [g", d"] sont disjoints.
Donc aucun intervalle ne rencontre v !
Les classes
• Une classe d'intervalles
• Une classe d'arbres binaires de recherche avec
– un champ intervalle – un champ dmax
• Il faut revoir le constructeur d'arbres pour le calcul de dmax!
Classe Intervalle
class Intervalle { int min, max;
Intervalle(int a, int b) { min = a;
max = b;
} }
static boolean rencontre(
Intervalle u, Intervalle v) { return
u.min <= v.max && v.min <= u.max;
}
Classe Arbre
class Arbre { Intervalle u;
int dmax;
Arbre filsG, filsD;
Arbre(Intervalle v, Arbre g, Arbre d) {
u = v;
filsG = g;
filsD = d;
dmax = calculDmax(v, g, d);
}
Classe Arbre
class Arbre {
...
static int calculDmax(Intervalle v, Arbre g, Arbre d)
{
int m = v.max;
if (g != null && g.dmax > m) m = g.dmax;
if (d != null && d.dmax > m) m = d.dmax;
return m;
} }
Recherche
static Arbre chercher(Intervalle v, Arbre a) {
if (a == null || rencontre(v, a.u)) return a;
if (a.filsG != null) &&
a.filsG.dmax >= v.min)
return chercher(v, a.filsG);//Règle 2 else
return chercher(v, a.filsD);//Règle 1 }