• Aucun résultat trouvé

Dans les cas où l’arithmétique d’intervalles est devenue insuffisante pour déterminer l’ordre entre deux nombres, nous venons de voir qu’il était nécessaire d’évaluer les expressions représentant les valeurs exactes. Mais on peut détecter cette égalité sans aucun calcul exact dans le cas où les deux expressions symboliques sont identiques. Ce cas se présente assez fréquemment lors des calculs géométriques. On peut citer à titre d’exemple la comparaison de la pente de deux segment.

if(pente(s1) == pente(s2))…

Les deux appels de la fonction pente(s) fabriquent deux expressions symboliques identiques qui ne se différencient que par la valeur de leurs feuilles. Nous appellerons “clones” deux expressions de définitions identiques. Si les feuilles sont identiques (cas de deux segments confondus), la comparaison telle que nous l’avons définie dans les paragraphes précédents a recours à une évaluation complète des expressions pour déterminer leur égalité.

La détection de l’égalité de ce type d’expressions peut se faire sans évaluation en comparant de manière récursive les nœuds de chaque expression (figure 15 p. 50). Le nombre de comparaisons que nécessite cet algorithme est proportionnel au nombre de nœuds présents dans les expressions, soit une complexité en O n( ).

booléen Clone(a,b : Paresseux) { si adresse(a) == adresse(b) rendre vrai; si intersection(encadrement(a),encadrement(b)) == vide rendre faux; si feuille(a) et feuille(b) rendre a == b; si opérateur(a) == opérateur(b) si opérateur(a) == “binaire” rendre (Clone(filsG(a),filsG(b)) et Clone(filsD(a),filsD(b)) si opérateur(a) == “unaire” rendre (Clone(fils(a),fils(b))

sinon rendre faux; }

figure 15

La paresse Isomorphisme

Si l’algorithme que nous venons de présenter permet de détecter des clones, il est incapable de détecter les permutations sur les opérateurs commutatifs (addition et mul-tiplication). Nous appellerons isomorphisme d’expressions l’égalité de deux expressions symboliques à la commutativité près ( et ).

Nous définissons l’isomorphisme de deux DAG de la manière suivante :

Soient et deux DAG, est isomorphe à si et seulement si, il existe une bijection des sommets de dans les sommets qui conserve la sémantique des sommets, et qui induise une bijection des arètes de dans les arètes de [BERG 83]. La sémantique des sommets est définie de la manière suivante :

Quel que soit un sommet de A :

• Si est un rationnel, est un rationnel égal à .

• Si est une opération choisie dans {+, *, inv, opp}, est la même opération. La détection de l’isomorphisme d’arbres lorsque ceux-ci n’ont pas de valeur sémantique attachée à leurs sommets se fait en temps proportionnel au nombre de sommets ( ), et il en est de même dans le cas où chaque sommet porte une valeur unique dans l’arbre [AHO 74]. Une version plus simple à implanter que l’algorithme de [AHO 74] dans le cas d’un arbre où les sommets portent une valeur unique permet d’obtenir une complexité linéaire dans le cas des arbres binaires. Pour chaque sommet de l’arbre, il suffit de trouver les fils qui se correspondent en comparaisons ( est le degré des sommets) ou en si on les trie au préalable. Si tous les sommets sont en correspondance on continue récursivement, sinon les arbres ne sont pas isomorphes. La détection de l’isomorphisme se fait alors en ( est une constante égale à quatre dans le cas des arbres binaires).

Le problème que nous avons à résoudre est intermédiaire entre les deux problèmes précédents. Dans le cas général, la sémantique1 (et la commutativité de l’addition et de la multiplication) ajoutée à chaque sommet nous impose de parcourir l’ensemble des permutations de l’arbre et rend la détection de l’isomorphisme exponentielle

1. il est impossible de différencier deux nœuds + ou deux nœuds *.

a+b = b+a ab = ba A B A B f A B A B s s f s( ) s s f s( ) O n( ) O d( 2) d dlogd O nd( 2) d2 x x y y figure 16

Isomorphisme La paresse

( où est le nombre de feuilles d’un arbre binaire). Mais l’ajout des compa-raisons entre les opérateurs, les intervalles associés aux sommets et les rationnels nous rapproche du problème où chaque sommet porte une valeur unique dans l’arbre. Sans prétendre transformer la complexité du problème, expérimentalement la détection de l’isomorphisme se fait en général avec un coût linéaire (il subsistera toujours des cas où la détection de l’isomorphisme sera de coût exponentiel). Un prétraitement des expressions par une recherche d’isomorphisme d’arbres permettrait d’éliminer tous les cas d’arbres non isomorphes en opérations.

Les algorithmes que nous venons de décrire s’appliquent à des arbres ou à des DAG si ceux-ci sont parcourus comme des arbres. Si, lors de la détection de l’isomorphisme, deux sommets en correspondance partagent la même sous-expression il est inutile de vérifier que les deux sous-arbres sont isomorphes : ils sont identiques par construction. L’utilisation des opérateurs unaires opposé et inverse permet de traduire l’absence de commutativité des opérations soustraction et division et réduit le nombre des permu-tations possibles dans l’arbre.

Le coût de la vérification symbolique de cette égalité est très inférieur à celui de l’éva-luation rationnelle des nombres malgré son aspect très récursif et elle ne fait aucun calcul exact. Si les deux arbres d’expressions sont très différents, on s’en aperçoit rapidement : il suffit seulement d’un nœud ou d’une feuille qui différent. Pour accélérer les comparaisons, on a intérêt à partager le plus possible les expressions identiques. Les résultats expérimentaux obtenus avec les algorithmes décrits au chapitre 6 ont montré que les configurations particulières des arbres d’expressions permettaient de détecter rapidement leur égalité ou leur inégalité, et que le fait de faire le test complet

O 2( 2n–1) n

O n( )

booléen Isomorphe(a,b : Paresseux) { si adresse(a) == adresse(b) rendre vrai; si intersection(encadrement(a),encadrement(b)) == vide rendre faux; si feuille(a) et feuille(b) rendre a == b; si opérateur(a) == opérateur(b) si opérateur(a) == “binaire” rendre (Isomorphe(filsG(a),filsG(b)) et Isomorphe(filsD(a),filsD(b) ou (Isomorphe(filsG(a),filsD(b)) et Isomorphe(filsD(a),filsG(b) sinon

rendre Isomorphe(fils(a), fils(b));

sinon rendre faux; }

figure 17

La paresse Appartenance-Union (Union-Find)

d’isomorphisme plutôt que celui des clones ne ralentissait pas l’arithmétique et détectait plus d’expressions identiques.