diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 797acea53eb091cf5b30518802c3073f544adeed..0000000000000000000000000000000000000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="RunConfigurationProducerService"> - <option name="ignoredProducers"> - <set> - <option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" /> - </set> - </option> - </component> -</project> \ No newline at end of file diff --git a/README.md b/README.md index 80dffb8ec53ef0a425f2fa1835e4696a3ed73816..a58fd2b4820ad3e99ec5dc9114b9ca26d1d1c152 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,454 @@ # TP3 -ici votre rapport + + +Ce projet a été réalisé par **Sarah Cherchem** et **Celia Arezki**. + +## Algorithmes Réalisés : + + +Nous avons implémenté plusieurs algorithmes d'arbres couvrants aléatoires, dont certains sont +présentés de manière similaire mais avec des variantes intéressantes. Voici les algorithmes que nous a +vons traités dans ce projet : + +### 3.1 Arbres couvrants de poids minimum aléatoire +Un algorithme simple pour obtenir un arbre couvrant aléatoire. L'idée est de : +1. Attribuer un poids aléatoire à chaque arête dans l'intervalle [0,1]. +2. Retourner un arbre couvrant de poids minimum en utilisant l'algorithme de **Prim** ou **Kruskal**. + (on a utilisé Prim) + +### 3.2 Parcours aléatoire +Effectuer un parcours du graphe en partant d’un sommet aléatoire et choisir les arcs de la frontière +au hasard pour définir un arbre couvrant. + +### 3.3 Insertion aléatoire d’arêtes +Construire un arbre couvrant en insérant des arêtes aléatoires, en s'assurant que chaque insertion +ne crée pas de cycle, à l'aide de structures de données **Union-Find**. + +### 3.4 Algorithme d’Aldous-Broder +Cet algorithme consiste à effectuer une marche aléatoire sur le graphe, où l'arbre couvrant est +constitué des arêtes qui ont permis d'atteindre chaque sommet pour la première fois. + +### 3.5 Par contraction d’arêtes +Choisir une arête aléatoire, la contracter, puis chercher un arbre couvrant dans le graphe contracté. +Cette méthode peut créer des boucles ou des arêtes parallèles, il est donc important de les gérer correctement. + +### 3.6 Algorithme de Wilson +Construire un arbre couvrant en effectuant des marches aléatoires depuis des sommets extérieurs +à l'arbre jusqu'à ce qu'ils atteignent un sommet de l'arbre. Ces chemins sont ajoutés à l'arbre. + + + +### 3.8 Par suppression de cycles +Construire un arbre couvrant en choisissant aléatoirement des arêtes incidentes à chaque sommet et +en supprimant les cycles qui peuvent apparaître. ### Pour cette algorithme nous avons commencé son implémentation +cependant il reste quelque erreur lié à la logique. + + + + + + +## Contenu du projet +Le projet contient les éléments suivants : + +1. **Classes principales** + - **Graph** : Structure de base pour représenter les graphes non orientés. + - **Edge** et **Arc** : Représentent respectivement les arêtes et les arcs orientés. + - **RootedTree** : Permet d’analyser un arbre enraciné (diamètre, centre, indice de Wiener, etc.). + - **UnionFind** : Structure de données pour la gestion des ensembles disjoints, utile dans les algorithmes comme la contraction ou l’insertion aléatoire. + +2. **Algorithmes de génération d'arbres couvrants** + - **Aldous-Broder** : Utilise une marche aléatoire pour construire un arbre couvrant uniforme. + - **Prim** : Génère un arbre de poids minimum avec des poids aléatoires. + - **Wilson** : Marche aléatoire modifiée pour générer des arbres couvrants uniformément. + - **Insertion aléatoire** : Ajoute des arêtes aléatoires sans former de cycles. + - **Contraction d'arêtes** : Contracte des arêtes aléatoires tout en maintenant la connectivité. + - **Parcours aléatoire (RandomSearch)** : Parcours aléatoire à partir d'un sommet pour construire l'arbre. + - **Suppression de cycles** : Supprime les cycles formés par des arêtes aléatoirement choisies. + +3. **Types de graphes pour les tests** + - **Grille** : Graphe en forme de grille. + - **Complet** : Graphe où tous les sommets sont connectés. + - **Erdős-Rényi** : Graphe généré avec une probabilité de connexion entre les sommets. + - **Lollipop** : Graphe combinant un cycle et un chemin. + +4. **Visualisation et statistiques** + - **Labyrinth** : Visualisation des arbres couvrants sous forme de labyrinthe. + - **Stats** : Calcul de métriques telles que le diamètre moyen, l’indice de Wiener, et le nombre de feuilles. + + + + +1 - AbstractArbreCouvrant (Classe abstraite) : + + +La classe AbstractArbreCouvrant sert de base pour toutes les implémentations d'algorithmes d'arbres couvrants. +Elle contient les éléments communs qui seront utilisés par toutes les classes qui en héritent. Voici ses +principales caractéristiques : + +Attributs : + +protected final Graph graph: Il s'agit d'un objet Graph qui représente le graphe sur +lequel l'algorithme doit être exécuté. Ce graphe est passé en paramètre lors de la création +de l'objet AbstractArbreCouvrant. + + +protected final Random generator: Un générateur de nombres aléatoires utilisé dans certaines +implémentations d'algorithmes (comme pour l'initialisation des poids dans l'algorithme de Prim). +protected final Set<Edge> arbre: Un ensemble qui contient les arêtes de l'arbre couvrant. Ce set +est commun à toutes les implémentations d'algorithmes, et les arêtes de l'arbre sont ajoutées au +fur et à mesure de l'exécution de l'algorithme. + + +2. ArbreCouvrant (Interface) + Cette interface définit le contrat que toutes les implémentations d'algorithmes d'arbres + couvrants doivent respecter. Elle contient une seule méthode : + - Set<Edge> arbreCouvrant() : Cette méthode doit être implémentée par toutes les classes + dérivées pour générer un arbre couvrant du graphe. Elle retourne un ensemble d'arêtes qui composent + l'arbre couvrant. +3. Prim (Algorithme de Prim) + La classe Prim hérite de AbstractArbreCouvrant et implémente l'algorithme de Prim pour générer + un arbre couvrant minimum dans un graphe. Voici les éléments clés de l'implémentation : + +Attributs : + +private final Map<Edge, Double> poids: Une carte qui associe à chaque arête du graphe un poids +aléatoire. Ces poids sont utilisés pour choisir l'arête de poids minimal à chaque étape de l'algorithme de Prim. +private final PriorityQueue<Edge> queue: Une file de priorité qui contient les arêtes du graphe, triées par +leur poids. Cela permet de toujours récupérer l'arête de poids minimal de manière efficace. + +Méthodes : + +- genererPoidsAleatoires() : Cette méthode attribue un poids aléatoire à chaque arête du graphe. +Elle génère un nombre aléatoire entre 0 et 1 pour chaque arête. Le poids est stocké dans la carte poids. +Le poids des arêtes est normalisé pour qu'il soit dans l'intervalle [0, 1]. Si le nombre d'arêtes +est supérieur à 500, un calcul basé sur le cube du nombre d'arêtes est utilisé pour définir une +borne supérieure au poids aléatoire. +- arbreCouvrant() : Cette méthode implémente l'algorithme de Prim pour générer un arbre +couvrant. Voici les étapes de l'algorithme : +- Choisir un sommet de départ : L'algorithme commence avec un sommet arbitraire (dans ce cas, le sommet 0). +Ajouter les arêtes sortantes du sommet au queue : Toutes les arêtes connectées au + sommet de départ sont ajoutées à la file de priorité. +Boucle principale : Tant que la queue n'est pas vide, l'algorithme extrait l'arête de poids +minimal et vérifie si le sommet de destination de cette arête a déjà été visité. Si ce n'est pas le cas, +l'arête est ajoutée à l'arbre couvrant, et toutes les arêtes sortantes du sommet de destination sont +ajoutées à la file de priorité. +Retourner l'arbre couvrant : Une fois que l'arbre couvrant a été généré, il est retourné sous forme +d'un ensemble d'arêtes. + + +2- Insertion +La classe Insertion implémente un algorithme de génération d'arbre couvrant en utilisant une +approche de type Kruskal avec un tirage aléatoire des arêtes. Elle évite la formation de cycles +grâce à la structure de données Union-Find. L'algorithme procède comme suit : + +Mélange aléatoirement les arêtes du graphe. +Ajoute une arête à l'arbre couvrant si elle ne forme pas de cycle, en vérifiant +la connectivité des sommets avec Union-Find. +Répète ce processus jusqu'à ce que l'arbre couvrant contienne 𝑉 − 1 arêtes, où +V est le nombre de sommets du graphe. +Cette approche est simple et efficace pour la génération d'arbres couvrants dans des graphes non orientés. + +3- AldousBroder +La classe AldousBroder implémente un algorithme de génération d'arbre couvrant basé sur la marche aléatoire. +L'algorithme fonctionne de la manière suivante : + +Un sommet initial est choisi aléatoirement. +L'algorithme effectue une marche aléatoire en sélectionnant des arcs sortants du sommet actuel jusqu'à ce +que tous les sommets aient été visités. +Lorsqu'un nouveau sommet est visité pour la première fois, l'arête menant à ce sommet est ajoutée à l'arbre +couvrant. +L'algorithme continue cette marche jusqu'à ce que l'arbre couvrant contienne tous les sommets du graphe. +Cet algorithme est basé sur la marche aléatoire de type Aldous-Broder et garantit la construction d'un +arbre couvrant. Il est particulièrement efficace dans les graphes aléatoires. + +4- Wilson : +La classe Wilson implémente un algorithme de génération d'arbre couvrant appelé algorithme de Wilson. +Cet algorithme est basé sur la marche aléatoire, et il est plus spécifique que d'autres algorithmes comme +celui de Prim ou Kruskal. Voici une explication détaillée du fonctionnement de cet algorithme : + +Description de l'algorithme de Wilson : + +Initialisation : + +L'algorithme commence avec un ensemble de sommets visités (sommetVisite) qui est initialement vide. +Il crée une map (outArc) pour stocker les arêtes rencontrées lors des marches aléatoires. +Itérations sur les sommets : + +L'algorithme traite les sommets du graphe un par un, en choisissant d'abord un sommet de départ (ici, +les sommets sont triés en fonction de leur degré, du plus grand au plus petit). +Si un sommet n'a pas encore été visité, il commence une marche aléatoire depuis ce sommet pour rejoindre +un sommet déjà visité. + +Marche aléatoire : + +À chaque étape de la marche, un sommet est choisi au hasard parmi les voisins du sommet courant. +L'algorithme enregistre les arêtes empruntées dans une map (outArc) pour pouvoir retracer le chemin +lorsque la marche atteint un sommet déjà visité. + +Retour sur le chemin parcouru : + +Une fois qu'une marche atteint un sommet déjà visité, l'algorithme commence à retracer le chemin +parcouru en marquant chaque sommet comme visité et en ajoutant chaque arête à l'arbre couvrant (arbre). + +Terminaison : + +Ce processus continue jusqu'à ce que tous les sommets aient été visités, ce qui garantit que l'arbre +couvrant contient tous les sommets du graphe. +Points importants : +Marche aléatoire : La caractéristique de l'algorithme de Wilson repose sur la marche aléatoire qui démarre +à un sommet choisi au hasard et avance jusqu'à ce qu'elle atteigne un sommet déjà visité. +Complexité : L'algorithme de Wilson est un peu plus complexe que les algorithmes de Prim ou Kruskal, mais +il garantit un arbre couvrant de manière probabiliste. + + +5- Recherche Aléatoire : +La classe RandomSearch implémente un algorithme de recherche aléatoire pour générer un arbre couvrant. +Voici une explication détaillée du fonctionnement de cet algorithme : + +Description générale de l'algorithme : +L'algorithme RandomSearch fonctionne en explorant le graphe de manière aléatoire. Voici les étapes principales : + +Initialisation : + +Un sommet de départ est choisi aléatoirement parmi les sommets du graphe. +Ce sommet est ajouté à l'ensemble des sommets visités. +La liste des arcs à explorer (frontier) est initialement vide, mais elle est remplie avec +les arcs sortants du sommet de départ. + + +Exploration aléatoire : + +L'algorithme fonctionne tant qu'il reste des arcs dans la liste frontier à explorer. +À chaque itération, un arc est choisi au hasard parmi ceux disponibles dans frontier (la liste +est mélangée aléatoirement avec Collections.shuffle). +Si l'arc mène à un sommet qui n'a pas encore été visité, cet arc est ajouté à l'arbre couvrant, et +ce sommet est marqué comme visité. +Tous les arcs sortants du sommet destination de l'arc choisi sont ajoutés à la liste frontier pour être +explorés plus tard. + +Terminaison : + +L'algorithme continue jusqu'à ce que tous les sommets aient été visités. Cela garantit que l'arbre couvrant +contient tous les sommets du graphe, mais peut contenir un nombre d'arêtes supérieur au minimum nécessaire. + + +6- Contraction : +1. ContractionGraph : + Cette classe est responsable de la gestion du graphe après la contraction des arêtes. + Lorsqu'une arête est contractée, elle relie deux sommets ensemble en une seule entité (un sommet "combiné"), + supprimant ainsi l'arête contractée et réarrangeant le graphe. + +Attributs principaux : + +incidence : Une liste de listes (LinkedList<Integer>). Chaque liste correspond à un sommet et contient +les indices des arêtes incidentes à ce sommet. +extrimite1 et extrimite2 : Listes qui contiennent respectivement les sommets de départ et d'arrivée pour +chaque arête dans le graphe. +initialEdges : Liste des arêtes initiales du graphe. +Méthodes principales : +ContractionGraph(Graph graph) : Le constructeur initialise les listes incidence, extrimite1, extrimite2 +et initialEdges en parcourant toutes les arêtes du graphe. Il ajoute chaque arête à ces listes et associe +les indices des arêtes aux sommets correspondants dans incidence. + +boucle(int indexEdge) : Cette méthode vérifie si une arête est une boucle (c'est-à-dire si elle relie un +sommet à lui-même). Elle retourne true si les deux extrémités de l'arête sont le même sommet. + +contract(int edgeIndex) : Cette méthode contracte une arête spécifiée par edgeIndex. Si l'arête n'est pas +une boucle, elle relie les deux sommets qu'elle connecte. Tous les arcs incident au deuxième sommet sont +réajustés pour pointer vers le premier sommet, et les arêtes sont modifiées en conséquence pour refléter +cette contraction dans la structure de données du graphe. + +getInitialEdges(), getExtrimite1(), getExtrimite2() : Ces méthodes retournent respectivement les listes +des arêtes initiales et des extrémités des arêtes. Elles sont utilisées pour obtenir des informations sur +le graphe initial après la contraction. + +2. Contraction : + Cette classe implémente l'algorithme de contraction d'arêtes pour créer un arbre couvrant. Elle utilise +3. ContractionGraph pour gérer les contractions des arêtes et UnionFind pour détecter et éviter les cycles +4. lors de la construction de l'arbre couvrant. + +Attributs principaux : +contractionGraph : Instance de la classe ContractionGraph utilisée pour gérer le graphe après contraction +des arêtes. +unionFind : Instance de l'algorithme Union-Find (ou Disjoint Set Union), utilisée pour suivre et gérer les +connexions entre les sommets et éviter de former des cycles dans l'arbre couvrant. +nodes : Un tableau de Node, où chaque Node contient des informations sur la profondeur et la hauteur de +chaque sommet. Cependant, ces informations ne sont pas utilisées dans cette version du code. +Méthodes principales : +Contraction(Graph graph) : Le constructeur initialise les objets contractionGraph (pour la gestion des +contractions) et unionFind (pour suivre les connexions entre les sommets). Il initialise également le tableau +nodes, bien qu'il ne soit pas utilisé dans cette version du code. + +arbreCouvrant() : Cette méthode implémente l'algorithme de contraction pour générer un arbre couvrant. +Les étapes sont les suivantes : + +Initialisation des arêtes : Une liste edges est remplie avec les indices des arêtes du graphe. +Mélange des arêtes : Les arêtes sont mélangées de manière aléatoire pour assurer que l'algorithme ne +suit pas un ordre fixe. +Parcours des arêtes : Pour chaque arête, si l'arête est une boucle, elle est ignorée. Si les sommets +reliés par l'arête sont déjà connectés dans l'arbre (vérifié par unionFind.find()), l'arête est ignorée +pour éviter les cycles. +Ajout de l'arête à l'arbre couvrant : Si l'arête relie deux sommets non connectés, elle est ajoutée à +l'arbre couvrant. +Union des sommets : Les sommets reliés par l'arête sont unis (avec unionFind.union()). +Contraction de l'arête : L'arête est contractée dans le graphe (avec contractionGraph.contract()). +À la fin, l'algorithme retourne l'ensemble arbre, qui contient les arêtes de l'arbre couvrant généré. + +Classe interne Node : +Cette classe représente un sommet avec deux propriétés : + +depth : La profondeur du sommet dans l'arbre. Cependant, dans ce code, elle reste à 0 et n'est pas utilisée. +height : La hauteur du sommet. Comme la profondeur, elle n'est pas utilisée ici. +Interaction entre les classes : +ContractionGraph gère la structure interne du graphe après chaque contraction d'arête. Elle suit les +modifications des arêtes et des sommets, en ajustant les indices et en évitant de créer des boucles. + +Contraction utilise ContractionGraph pour manipuler le graphe en cours et pour générer l'arbre couvrant. +Elle se repose également sur UnionFind pour garantir que l'algorithme ne crée pas de cycles en ajoutant +des arêtes. + +Comparaison des résultats pour chaque algorithme : +1. Algorithme Prim : + Excentricité moyenne : 70.30 + Indice de Wiener moyen : 1,599,399,905 + Diamètre moyen : 263 + Nombre moyen de feuilles : 1,228 + Nombre moyen de sommets de degré 2 : 2,729 + Temps de calcul moyen : 38,686,424 ms + + + + +2. Algorithme Aldous : + + Excentricité moyenne : 112.54 + Indice de Wiener moyen : 2,219,478,278 + Diamètre moyen : 424 + Nombre moyen de feuilles : 1,514 + Nombre moyen de sommets de degré 2 : 2,330 + Temps de calcul moyen : 38,690,744 ms + + + + + +3. Algorithme Insertion : + Excentricité moyenne : 104.01 + Indice de Wiener moyen : 2,038,044,944 + Diamètre moyen : 387 + Nombre moyen de feuilles : 1,583 + Nombre moyen de sommets de degré 2 : 2,226 + Temps de calcul moyen : 38,694,069 ms + + + + +4. Recherche Aléatoire : + Excentricité moyenne : 53.80 + Indice de Wiener moyen : 1,193,672,822 + Diamètre moyen : 207 + Nombre moyen de feuilles : 1,670 + Nombre moyen de sommets de degré 2 : 2,084 + Temps de calcul moyen : 38,698,474 ms + +  + +5. Algorithme Contraction : + + Excentricité moyenne : 106.99 + Indice de Wiener moyen : 2,081,870,127 + Diamètre moyen : 424 + Nombre moyen de feuilles : 1,577 + Nombre moyen de sommets de degré 2 : 2,234 + Temps de calcul moyen : 38,704,749 ms + + + + +6. Algorithme Wilson : + Excentricité moyenne : 127.26 + Indice de Wiener moyen : 2,487,683,163 + Diamètre moyen : 500 + Nombre moyen de feuilles : 1,515 + Nombre moyen de sommets de degré 2 : 2,340 + Temps de calcul moyen : 38,709,040 ms + + + + +Analyse des résultats : +Excentricité : + +- **L'algorithme Recherche aléatoire reste celui avec la plus basse excentricité moyenne (53.80), indiquant +une meilleure uniformité dans la distribution des sommets. +Wilson génère l'arbre avec la plus grande excentricité (127.26), suggérant des arbres plus "étendus"** +- **Indice de Wiener : +Wilson a l'indice de Wiener le plus élevé (2,487,683,163), ce qui montre que les arbres générés par Wilson ont +des distances plus longues entre certaines paires de sommets. +L'algorithme Recherche aléatoire a l'indice de Wiener le plus bas (1,193,672,822), indiquant des arbres avec des +distances plus courtes en moyenne**. + +-**Diamètre : +Wilson génère des arbres avec le diamètre le plus élevé (500), ce qui correspond à l'excentricité et à l'indice de Wiener élevés. +Recherche aléatoire a le diamètre le plus bas (207), correspondant à sa faible excentricité et indice de Wiener** + +-**Nombre de feuilles : +L'algorithme Recherche aléatoire génère le plus grand nombre de feuilles (1,670), suggérant des arbres plus ramifiés. +Aldous a le nombre de feuilles le plus faible (1,514), indiquant des arbres plus concentrés.** +-**Nombre de sommets de degré 2 : +Prim a le plus grand nombre de sommets de degré 2 (2,729), ce qui pourrait refléter des arbres plus équilibrés. +Wilson et Contraction ont des résultats similaires (2,340 et 2,234), avec un nombre modéré de sommets de degré 2.** + + +-**Temps de calcul : +Les temps de calcul pour tous les algorithmes sont relativement proches (environ 38.7 millions de millisecondes +en moyenne), avec une légère variation qui pourrait être influencée par les spécificités des algorithmes et de +l'implémentation.** + +### Conclusion : +Recherche aléatoire semble générer des arbres avec des caractéristiques plus équilibrées (faible excentricité, +faible indice de Wiener et diamètre), et un plus grand nombre de feuilles. +Wilson génère des arbres plus étendus avec un diamètre plus élevé, une excentricité plus grande et un indice de +Wiener élevé. +Contraction et Aldous génèrent des arbres plus proches en termes de métriques, mais Wilson et Prim se distinguent +par des arbres ayant un diamètre plus large et une plus grande dispersion des sommets. +Les algorithmes comme Prim et Aldous semblent produire des arbres plus réguliers tandis que Wilson et Contraction +génèrent des arbres plus "étendus", avec des métriques plus élevées. + + + + + + +## Comparaison des algorithmes + +### Métriques évaluées +- **Diamètre moyen** : Distance maximale entre deux sommets. +- **Indice de Wiener** : Somme des distances entre toutes les paires de sommets. +- **Distribution des degrés** : Proportion des sommets ayant un degré donné. + +### Observations +- **Aldous-Broder** : Produit des arbres couvrants uniformément mais peut être lent sur de grands graphes. +- **Prim** : Rapide pour les graphes denses, mais les poids aléatoires influencent les résultats. +- **Wilson** : Garantit une distribution uniforme tout en étant plus efficace qu'Aldous-Broder. +- **Insertion aléatoire** : Simple à implémenter mais sensible à l'ordre des arêtes. +- **Contraction** : Efficace mais plus complexe à coder. +- **Suppression de cycles** : Produit des arbres non uniformes avec un bon compromis entre qualité et performance. + +--- + +## Limitations +- Certains algorithmes (par ex. contraction d'arêtes) peuvent être inefficaces pour les très grands graphes. +- Les visualisations sont limitées à certains types de graphes comme les grilles. + + +## Ressources utilisées + +- [PDF - Math.ENS.PSL](chrome-extension://efaidnbmnnnibpcajpcglclefindmkaj/https://www.math.ens.psl.eu/shared-files/10422/?bureaux.pdf) +- [Site d'Olivier Pons - Aldous-Broder](https://www.olivierpons.fr/labyrinthes/algorithme-daldous-broder/) +- - Consigne du TP : [Lien vers le document fourni] +- Documentation Java officielle : [https://docs.oracle.com/en/java/] +- Structure Union-Find : [https://en.wikipedia.org/wiki/Disjoint-set_data_structure] diff --git a/out/production/tp3-git/Graph/Arc.class b/out/production/tp3-git/Graph/Arc.class new file mode 100644 index 0000000000000000000000000000000000000000..288a2e56b9c70572c27d79c76226d230581b829c Binary files /dev/null and b/out/production/tp3-git/Graph/Arc.class differ diff --git a/out/production/tp3-git/Graph/Edge.class b/out/production/tp3-git/Graph/Edge.class new file mode 100644 index 0000000000000000000000000000000000000000..3714023ebb05072bd0a067e4feb091b5d8559393 Binary files /dev/null and b/out/production/tp3-git/Graph/Edge.class differ diff --git a/out/production/tp3-git/Graph/Graph.class b/out/production/tp3-git/Graph/Graph.class new file mode 100644 index 0000000000000000000000000000000000000000..d809e0c38eb9593738b10506151a3977ca1c59d3 Binary files /dev/null and b/out/production/tp3-git/Graph/Graph.class differ diff --git a/out/production/tp3-git/Graph/RootedTree$Node.class b/out/production/tp3-git/Graph/RootedTree$Node.class new file mode 100644 index 0000000000000000000000000000000000000000..0692e5e6788bac829ff47d8db1df42f59485c60e Binary files /dev/null and b/out/production/tp3-git/Graph/RootedTree$Node.class differ diff --git a/out/production/tp3-git/Graph/RootedTree.class b/out/production/tp3-git/Graph/RootedTree.class new file mode 100644 index 0000000000000000000000000000000000000000..2375a9e9f7b7df869d7d43d5701510fb0363b7e0 Binary files /dev/null and b/out/production/tp3-git/Graph/RootedTree.class differ diff --git a/out/production/tp3-git/GraphClasses/Complete.class b/out/production/tp3-git/GraphClasses/Complete.class new file mode 100644 index 0000000000000000000000000000000000000000..660b632008c96e3208eba172b4ce1352b215a378 Binary files /dev/null and b/out/production/tp3-git/GraphClasses/Complete.class differ diff --git a/out/production/tp3-git/GraphClasses/ErdosRenyi.class b/out/production/tp3-git/GraphClasses/ErdosRenyi.class new file mode 100644 index 0000000000000000000000000000000000000000..3a3417eeb6f925354377fb1003136f22974cbea2 Binary files /dev/null and b/out/production/tp3-git/GraphClasses/ErdosRenyi.class differ diff --git a/out/production/tp3-git/GraphClasses/Grid.class b/out/production/tp3-git/GraphClasses/Grid.class new file mode 100644 index 0000000000000000000000000000000000000000..d31c739d685a5be1ab0f86cb6da8ea84090982dc Binary files /dev/null and b/out/production/tp3-git/GraphClasses/Grid.class differ diff --git a/out/production/tp3-git/GraphClasses/Lollipop.class b/out/production/tp3-git/GraphClasses/Lollipop.class new file mode 100644 index 0000000000000000000000000000000000000000..5c5377578fbe4a289b18cbdcbf36e8ac8240bd14 Binary files /dev/null and b/out/production/tp3-git/GraphClasses/Lollipop.class differ diff --git a/out/production/tp3-git/Graphics/Labyrinth.class b/out/production/tp3-git/Graphics/Labyrinth.class new file mode 100644 index 0000000000000000000000000000000000000000..5376478c78c236af7097968d8f8304398b92cbd3 Binary files /dev/null and b/out/production/tp3-git/Graphics/Labyrinth.class differ diff --git a/out/production/tp3-git/Main$Stats.class b/out/production/tp3-git/Main$Stats.class new file mode 100644 index 0000000000000000000000000000000000000000..03a521f3d20941071b7fa9c2e240fbd9d6336fba Binary files /dev/null and b/out/production/tp3-git/Main$Stats.class differ diff --git a/out/production/tp3-git/Main.class b/out/production/tp3-git/Main.class new file mode 100644 index 0000000000000000000000000000000000000000..bf550d1e2c9d24f22f8194d98ca808909a8f355e Binary files /dev/null and b/out/production/tp3-git/Main.class differ diff --git a/out/production/tp3-git/Makefile b/out/production/tp3-git/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..ebb1dca2694c7a8ff6cb7bee78a3388e5dd8ed7a --- /dev/null +++ b/out/production/tp3-git/Makefile @@ -0,0 +1,26 @@ +# Le nom de votre classe principale +# Renommez si nécessaire +MAINCLASS=Main +## Le chemin vers où votre classe compilée est installée +# Renommez si nécessaire +INSTALLDIR=../out/production/TP3 +MAINDIR=$(dir $(subst .,/,$(MAINCLASS))) +CLASSES=$(shell find $(INSTALLDIR)/$(MAINDIR) -name \*.class) + +compile: $(subst .,/,$(MAINCLASS)).class + +%.class: %.java + javac -g -d $(INSTALLDIR) *.java + +clean: + ## Rien à faire, tout à été compilé et installé dans INSTALLDIR + + +install: + ## Rien à faire, tout à été compilé et installé dans INSTALLDIR + +cleanInstall: + rm $(subst $$,\$$,$(CLASSES)) + +test: + diff --git a/out/production/tp3-git/RandomTreeAlgos/BreadthFirstSearch.class b/out/production/tp3-git/RandomTreeAlgos/BreadthFirstSearch.class new file mode 100644 index 0000000000000000000000000000000000000000..1c2c79cb37ae64a1ff25061a68320522b2bfc684 Binary files /dev/null and b/out/production/tp3-git/RandomTreeAlgos/BreadthFirstSearch.class differ diff --git a/out/production/tp3-git/Structures/ContractionGraph.class b/out/production/tp3-git/Structures/ContractionGraph.class new file mode 100644 index 0000000000000000000000000000000000000000..ad4906be7dae348e8116f3f02be7760df5e490cf Binary files /dev/null and b/out/production/tp3-git/Structures/ContractionGraph.class differ diff --git a/out/production/tp3-git/TreesAlgo/AbstractArbreCouvrant.class b/out/production/tp3-git/TreesAlgo/AbstractArbreCouvrant.class new file mode 100644 index 0000000000000000000000000000000000000000..988299124c618a82dffcc9f346ef553b96703380 Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/AbstractArbreCouvrant.class differ diff --git a/out/production/tp3-git/TreesAlgo/AldousBroder.class b/out/production/tp3-git/TreesAlgo/AldousBroder.class new file mode 100644 index 0000000000000000000000000000000000000000..8c5342916222618860b3a54fcad215f5ddd4d820 Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/AldousBroder.class differ diff --git a/out/production/tp3-git/TreesAlgo/ArbreCouvrant.class b/out/production/tp3-git/TreesAlgo/ArbreCouvrant.class new file mode 100644 index 0000000000000000000000000000000000000000..2c4c301851f9cc18ad57ff61b1ecd0e08566fe46 Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/ArbreCouvrant.class differ diff --git a/out/production/tp3-git/TreesAlgo/Contraction$Node.class b/out/production/tp3-git/TreesAlgo/Contraction$Node.class new file mode 100644 index 0000000000000000000000000000000000000000..621f34e9d1ff5957b03b76400576beb518d1928b Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/Contraction$Node.class differ diff --git a/out/production/tp3-git/TreesAlgo/Contraction.class b/out/production/tp3-git/TreesAlgo/Contraction.class new file mode 100644 index 0000000000000000000000000000000000000000..8d91add90f69645de43972d7edc23e16cc60a812 Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/Contraction.class differ diff --git a/out/production/tp3-git/TreesAlgo/Insertion.class b/out/production/tp3-git/TreesAlgo/Insertion.class new file mode 100644 index 0000000000000000000000000000000000000000..4ce03939ee2be7ae42821ae7ff26b4c50547ab61 Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/Insertion.class differ diff --git a/out/production/tp3-git/TreesAlgo/Prim.class b/out/production/tp3-git/TreesAlgo/Prim.class new file mode 100644 index 0000000000000000000000000000000000000000..27f32abb69999f554dfe7ecb99ae0faf40745c78 Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/Prim.class differ diff --git a/out/production/tp3-git/TreesAlgo/RandomSearch.class b/out/production/tp3-git/TreesAlgo/RandomSearch.class new file mode 100644 index 0000000000000000000000000000000000000000..68390f1088140e003c60b4ef0eab3b18dd03a7dd Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/RandomSearch.class differ diff --git a/out/production/tp3-git/TreesAlgo/SuppressionCycle.class b/out/production/tp3-git/TreesAlgo/SuppressionCycle.class new file mode 100644 index 0000000000000000000000000000000000000000..3409d947a9068baa04fb659127584d1239b2b779 Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/SuppressionCycle.class differ diff --git a/out/production/tp3-git/TreesAlgo/UnionFind.class b/out/production/tp3-git/TreesAlgo/UnionFind.class new file mode 100644 index 0000000000000000000000000000000000000000..e0883abc29b4d053dcdbd48e6363676b5ae08802 Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/UnionFind.class differ diff --git a/out/production/tp3-git/TreesAlgo/Wilson.class b/out/production/tp3-git/TreesAlgo/Wilson.class new file mode 100644 index 0000000000000000000000000000000000000000..011558f96f2e6be06b014adcfa246aa688e57a6c Binary files /dev/null and b/out/production/tp3-git/TreesAlgo/Wilson.class differ diff --git a/resources/AldousBroder.png b/resources/AldousBroder.png new file mode 100644 index 0000000000000000000000000000000000000000..fdaf2a44f9541ab1e8a0ea49a516fb169dec5e84 Binary files /dev/null and b/resources/AldousBroder.png differ diff --git a/resources/Contraction.png b/resources/Contraction.png new file mode 100644 index 0000000000000000000000000000000000000000..32c0cef005ba8dda1635ca102aea30c51fd14cf1 Binary files /dev/null and b/resources/Contraction.png differ diff --git a/resources/Insertion.png b/resources/Insertion.png new file mode 100644 index 0000000000000000000000000000000000000000..9ed98b89b522cd8a6fa0ad92128ed2e30f5ae2d5 Binary files /dev/null and b/resources/Insertion.png differ diff --git a/resources/Prim.png b/resources/Prim.png new file mode 100644 index 0000000000000000000000000000000000000000..96d7d6324cd52032807ea2800253d71407066404 Binary files /dev/null and b/resources/Prim.png differ diff --git a/resources/RandomSearch.png b/resources/RandomSearch.png new file mode 100644 index 0000000000000000000000000000000000000000..5f9faf2cb8be020829db0be169897dfd33a8d776 Binary files /dev/null and b/resources/RandomSearch.png differ diff --git a/resources/Wilson.png b/resources/Wilson.png new file mode 100644 index 0000000000000000000000000000000000000000..dda28592a8ba0a9453f4c2cdc30dc4d2e5b775ae Binary files /dev/null and b/resources/Wilson.png differ diff --git a/resources/random.png b/resources/random.png new file mode 100644 index 0000000000000000000000000000000000000000..ff9a0d384ef7800918ee99af3266a66c3fa5d4f4 Binary files /dev/null and b/resources/random.png differ diff --git a/resources/search.png b/resources/search.png new file mode 100644 index 0000000000000000000000000000000000000000..11fb0fd7682567b4d3bdf5088e94efc5f1a9e65f Binary files /dev/null and b/resources/search.png differ diff --git a/src/Graph/Edge.java b/src/Graph/Edge.java index a439044e0a90abce9c8e5935f6aa888341e2faf4..52a339847588d735b7bf835a90fb73a3a3cbf2b7 100644 --- a/src/Graph/Edge.java +++ b/src/Graph/Edge.java @@ -4,7 +4,7 @@ public class Edge implements Comparable<Edge> { public int source; public int dest; - double weight; + public double weight; public Edge(int source, int dest, double weight) { this.source = source; diff --git a/src/Graph/Graph.java b/src/Graph/Graph.java index 383b0b9b1017fb548676495aec7fdb5ec9badd40..3ed4c13b2f6dcc94ef6fb413b5e88be32ecbbfc0 100644 --- a/src/Graph/Graph.java +++ b/src/Graph/Graph.java @@ -2,6 +2,7 @@ package Graph; import java.util.ArrayList; import java.util.LinkedList; +import java.util.List; public class Graph { @@ -50,7 +51,10 @@ public class Graph { } public void addVertex(int vertex) { + if (vertex >= upperBound) { + + throw new IllegalArgumentException("Sommet hors limites."); } while (incidency.size() <= vertex) { @@ -59,6 +63,7 @@ public class Graph { outIncidency.add(new LinkedList<>()); } order = Math.max(order, vertex + 1); + } @@ -112,7 +117,10 @@ public class Graph { edgeCardinality++; } - + public int degreSommet(int vertex) { + // Le degré d'un sommet est simplement le nombre d'arêtes qui lui sont incidentes + return incidency.get(vertex).size(); + } public Arc[] outEdges(int vertex) { if (!isVertex(vertex)) { @@ -123,4 +131,90 @@ public class Graph { return outIncidency.get(vertex).toArray(new Arc[0]); } + public int getEdgeCardinality() { + return edgeCardinality; + } + + public ArrayList<LinkedList<Edge>> getIncidency() { + return incidency; + } + public Edge getEdge(int u, int v) { + if (!isVertex(u) || !isVertex(v)) { + throw new IllegalArgumentException("Un des sommets n'existe pas."); + } + + // Chercher l'arête entre u et v dans la liste des incidences du sommet u + for (Edge edge : incidency.get(u)) { + if (edge.oppositeExtremity(u) == v) { + return edge; + } + } + // Si l'arête n'existe pas, retourner null + return null; + } + public List<Integer> getVertices() { + List<Integer> vertices = new ArrayList<>(); + for (int i = 0; i < order; i++) { + if (incidency.get(i) != null) { // vérifier si le sommet existe + vertices.add(i); + } + } + return vertices; + } + public Graph copy() { + Graph newGraph = new Graph(this.upperBound); // Crée un nouveau graphe avec le même upperBound + newGraph.order = this.order; // Copie l'ordre du graphe + newGraph.edgeCardinality = this.edgeCardinality; // Copie le nombre d'arêtes + + // Copier les arêtes (incidency) + for (int i = 0; i < this.incidency.size(); i++) { + if (this.incidency.get(i) != null) { + LinkedList<Edge> newEdges = new LinkedList<>(); + for (Edge edge : this.incidency.get(i)) { + // Crée un nouvel Edge en copiant la source, destination et poids + newEdges.add(new Edge(edge.getSource(), edge.getDest(), edge.weight)); + } + newGraph.incidency.set(i, newEdges); + } else { + newGraph.incidency.set(i, null); + } + } + + // Copier les arcs entrants (inIncidency) + for (int i = 0; i < this.inIncidency.size(); i++) { + if (this.inIncidency.get(i) != null) { + LinkedList<Arc> newInArcs = new LinkedList<>(); + for (Arc arc : this.inIncidency.get(i)) { + // Crée un nouvel Arc en copiant le support (Edge) et la direction (reversed) + Edge edgeCopy = new Edge(arc.support.getSource(), arc.support.getDest(), arc.support.weight); + newInArcs.add(new Arc(edgeCopy, arc.reversed)); + } + newGraph.inIncidency.set(i, newInArcs); + } else { + newGraph.inIncidency.set(i, null); + } + } + + // Copier les arcs sortants (outIncidency) + for (int i = 0; i < this.outIncidency.size(); i++) { + if (this.outIncidency.get(i) != null) { + LinkedList<Arc> newOutArcs = new LinkedList<>(); + for (Arc arc : this.outIncidency.get(i)) { + // Crée un nouvel Arc en copiant le support (Edge) et la direction (reversed) + Edge edgeCopy = new Edge(arc.support.getSource(), arc.support.getDest(), arc.support.weight); + newOutArcs.add(new Arc(edgeCopy, arc.reversed)); + } + newGraph.outIncidency.set(i, newOutArcs); + } else { + newGraph.outIncidency.set(i, null); + } + } + + return newGraph; + } + + + + + } diff --git a/src/Graph/RootedTree.java b/src/Graph/RootedTree.java index 9793f129f8638680a5fde292419b256c33bc6167..a82097d74086b6ad4dcc7e4bfaf3d72b4238e22d 100644 --- a/src/Graph/RootedTree.java +++ b/src/Graph/RootedTree.java @@ -183,12 +183,13 @@ public class RootedTree { public int getRoot() { return root.vertex; } - public int getHeight(int vertex) { - return nodes[vertex].height; + public int getHeight(int vertex) { + return nodes[vertex].height; } public int getDepth(int vertex) { - return nodes[vertex].depth; + return nodes[vertex].depth; + } public int getSubtreeSize(int vertex) { @@ -274,14 +275,14 @@ public class RootedTree { swapRootWith(root.maxHeightSon()); resetBfsOrdering(); } - - + + private void createNode(Node nodes[], Arc arc) { int son = arc.getDest(); int father = arc.getSource(); nodes[son] = new Node(son); nodes[father].sons.add(nodes[son]); - } + } private void createTree(int root, ArrayList<Arc> sortedArcs) { @@ -290,6 +291,7 @@ public class RootedTree { nodes = new Node[order]; nodes[root] = new Node(root); + this.bfsOrder.add(nodes[root]); for (Arc arc : sortedArcs) { createNode(nodes,arc); @@ -300,22 +302,22 @@ public class RootedTree { Collections.reverse(inverseBfsOrder); this.root = nodes[root]; } - - + + public RootedTree(ArrayList<Edge> edges, int root) { this.order = edges.size() + 1; Graph graph = new Graph(order); for (Edge e : edges) graph.addEdge(e); createTree(root, BreadthFirstSearch.generateTree(graph, root)); - + rerootTree(); computeAllHeights(); computeAllSizes(); computeAllDepths(); } - - - - + + public int getOrder() { + return order; + } } diff --git a/src/Graphics/Labyrinth.java b/src/Graphics/Labyrinth.java index efa29846d114e1cf641366f91182861d20e0254c..18db9962b26885fd998b84f42bb37454545cd05c 100644 --- a/src/Graphics/Labyrinth.java +++ b/src/Graphics/Labyrinth.java @@ -192,8 +192,10 @@ public class Labyrinth extends JPanel { if (grid.isHorizontal(e)) drawHorizontalEdge(g,e); else drawVerticalEdge(g,e); } - for (int i = 0; i < grid.graph.order; i++) { - drawVertex(g,i); + for (int i = 0; i < tree.getOrder(); i++) { + + + drawVertex(g,i); } if (tree != null) drawRoot(g,tree.getRoot()); diff --git a/src/Main.java b/src/Main.java index 741f059fd2ade456ac0b73f5449b990ac11b1628..7ccc68846761d21ba2d1b9a449a108ca0b3df49a 100644 --- a/src/Main.java +++ b/src/Main.java @@ -2,10 +2,10 @@ import Graph.*; import GraphClasses.*; import RandomTreeAlgos.BreadthFirstSearch; import Graphics.*; +import TreesAlgo.*; import java.io.IOException; -import java.util.ArrayList; -import java.util.Random; +import java.util.*; import javax.swing.JFrame; @@ -21,33 +21,67 @@ public class Main { public static void main(String argv[]) throws InterruptedException { Graph graph = chooseFromGraphFamily(); - ArrayList<Edge> randomTree = null; - int noOfSamples = 10; + + + // Tester l'algorithme Prim + System.out.println("-------------------Testing Prim Algorithm------------------------"); + testAlgorithm(new Prim(graph), noOfSamples); + + // Tester l'algorithme AldousBroder + System.out.println("-------------------Testing AldousBroder Algorithm----------------"); + testAlgorithm(new AldousBroder(graph), noOfSamples); + + // Tester l'algorithme Contraction + System.out.println("------------------Testing Contraction Algorithm-----------------"); + testAlgorithm(new Contraction(graph), noOfSamples); + + // Tester l'algorithme Insertion + System.out.println("--------------------Testing Insertion Algorithm-------------------"); + testAlgorithm(new Insertion(graph), noOfSamples); + System.out.println("---------------------Testing Wilson Algorithm----------------------"); + testAlgorithm(new Wilson(graph), noOfSamples); + // Tester l'algorithme RandomSearch + System.out.println("---------------------Testing RandomSearch Algorithm----------------"); + testAlgorithm(new RandomSearch(graph), noOfSamples); + + + + + } + private static void testAlgorithm(AbstractArbreCouvrant algorithm, int noOfSamples) { + Graph graph = chooseFromGraphFamily(); // Créer un nouveau graphe pour chaque test + ArrayList<Edge> randomTree = null; Stats stats = new Stats(noOfSamples); + for (int i = 0; i < noOfSamples; i++) { - randomTree = genTree(graph); + randomTree = genTree(graph, algorithm); // Utilisation de l'algorithme stats.update(randomTree); } stats.print(); - - if (grid != null) showGrid(grid, randomTree); + if (grid != null) { + try { + showGrid(grid, randomTree); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } } private static Graph chooseFromGraphFamily() { // Parametriser ici cette fonction afin de pouvoir choisir // quelle classe de graphe utiliser - grid = new Grid(1920 / 11, 1080 / 11); + grid = new Grid(1920 / 20, 1080 / 20); Graph graph = grid.graph; - //Graph graph = new Complete(400).graph; + // Graph graph = new Complete(400).graph; //Graph graph = new ErdosRenyi(1_000, 100).graph; - //Graph graph = new Lollipop(1_000).graph; + // Graph graph = new Lollipop(1_000).graph; return graph; } - public static ArrayList<Edge> genTree(Graph graph) { - ArrayList<Edge> randomTree; + public static ArrayList<Edge> genTree(Graph graph,ArbreCouvrant arbre) { + /* ArrayList<Edge> randomTree; // TOOO : modifier l'algorithme utilisé ici // ou bien parametriser à l'aide de la ligne de commande @@ -58,7 +92,14 @@ public class Main { randomTree = new ArrayList<>(); for (Arc a : randomArcTree) randomTree.add(a.support); - return randomTree; + return randomTree;*/ + Set<Edge> arbreCouvrant = arbre.arbreCouvrant(); // Récupérer l'arbre couvrant minimal + if (arbreCouvrant == null || arbreCouvrant.isEmpty()) { + return new ArrayList<>(); // Retourner une liste vide si l'arbre est invalide + } + + return new ArrayList<>(arbreCouvrant); + } @@ -139,11 +180,11 @@ public class Main { // Pour générer un fichier image. try { - laby.saveImage("resources/random.png"); + laby.saveImage("resources/search.png"); } catch (IOException e1) { e1.printStackTrace(); } } -} +} \ No newline at end of file diff --git a/src/RandomTreeAlgos/BreadthFirstSearch.java b/src/RandomTreeAlgos/BreadthFirstSearch.java index f357e68d300103370be9327be459548612776519..8d7620bc3501cf0a08d286c4b75bd1da09ddce26 100644 --- a/src/RandomTreeAlgos/BreadthFirstSearch.java +++ b/src/RandomTreeAlgos/BreadthFirstSearch.java @@ -32,7 +32,7 @@ public class BreadthFirstSearch { } } - private BreadthFirstSearch (Graph graph) { + public BreadthFirstSearch (Graph graph) { this.graph = graph; this.frontier = new LinkedList<>(); this.tree = new ArrayList<>(); diff --git a/src/Structures/ContractionGraph.java b/src/Structures/ContractionGraph.java new file mode 100644 index 0000000000000000000000000000000000000000..4c166cb0387cba9a201e6a8fe35c935d9e133485 --- /dev/null +++ b/src/Structures/ContractionGraph.java @@ -0,0 +1,89 @@ +package Structures; + +import Graph.Edge; +import Graph.Graph; + +import java.util.*; + +public class ContractionGraph { + + private List<LinkedList<Integer>> incidence; // Liste des arêtes incidentes pour chaque sommet + private final List<Integer> extrimite1 = new ArrayList<>(); + private final List<Integer> extrimite2 = new ArrayList<>(); + private final List<Edge> initialEdges = new ArrayList<>(); + + public ContractionGraph(Graph graph) { + // Initialisation de la liste incidence avec une nouvelle ArrayList pour chaque sommet + this.incidence = new ArrayList<>(graph.order); + for (int i = 0; i < graph.order; i++) { + incidence.add(new LinkedList<>()); // Initialisation de la liste pour chaque sommet + } + + // Parcours tous les sommets du graphe + for (int vertex = 0; vertex < graph.order; vertex++) { + // Récupère les arêtes sortantes du sommet + for (var arc : graph.outEdges(vertex)) { + Edge edge = arc.support; + + // Ajout des arêtes aux listes + initialEdges.add(edge); + extrimite1.add(edge.getSource()); + extrimite2.add(edge.getDest()); + + // Ajout de l'indice de l'arête dans la liste d'incidences des deux sommets + incidence.get(edge.getSource()).add(extrimite1.size()); + incidence.get(edge.getDest()).add(extrimite1.size()); + } + } + } + + // Vérifie si une arête est une boucle + public boolean boucle(int indexEdge) { + return (extrimite1.get(indexEdge).equals(extrimite2.get(indexEdge))); + } + + // Contraction de l'arête spécifiée + public void contract(int edgeIndex) { + if (edgeIndex < 0 || edgeIndex >= extrimite1.size()) { + return; + } + + int sommet1 = this.extrimite1.get(edgeIndex); + int sommet2 = this.extrimite2.get(edgeIndex); + + if (boucle(edgeIndex)) return; + + // Pour chaque arête incidente sur sommet2 + for (int edge : incidence.get(sommet2)) { + if (edge < 0 || edge >= extrimite1.size()) { + continue; // Ignore l'arête invalide + } + + // Ajouter l'arête incidente à sommet1 si elle n'est pas déjà présente + if (extrimite1.get(edge) == sommet1 || extrimite2.get(edge) == sommet1) { + this.incidence.get(sommet1).add(edge); + } + + // Relier l'arête à sommet1 + if (extrimite1.get(edge) == sommet2) { + this.extrimite1.set(edge, sommet1); + } + if (extrimite2.get(edge) == sommet2) { + this.extrimite2.set(edge, sommet1); + } + } + } + + // Retourne la liste des arêtes initiales + public List<Edge> getInitialEdges() { + return initialEdges; + } + + public List<Integer> getExtrimite1() { + return extrimite1; + } + + public List<Integer> getExtrimite2() { + return extrimite2; + } +} diff --git a/src/Structures/UnionFind.java b/src/Structures/UnionFind.java new file mode 100644 index 0000000000000000000000000000000000000000..f62b26050620afd662f5d17b2f79ad5d98eda9da --- /dev/null +++ b/src/Structures/UnionFind.java @@ -0,0 +1,47 @@ +package TreesAlgo; + +import java.util.*; + +public class UnionFind<T> { + private Map<T, T> parent; + private Map<T, Integer> rank; + + public UnionFind() { + parent = new HashMap<>(); + rank = new HashMap<>(); + } + + // Trouver le représentant de l'ensemble, avec compression de chemin + public T find(T x) { + if (!parent.containsKey(x)) { + parent.put(x, x); + rank.put(x, 0); + } + if (!x.equals(parent.get(x))) { + parent.put(x, find(parent.get(x))); // Compression de chemin + } + return parent.get(x); + } + + // Fusionner deux ensembles + public void union(T x, T y) { + T rootX = find(x); + T rootY = find(y); + if (!rootX.equals(rootY)) { + // Union par rang (hauteur) + if (rank.get(rootX) > rank.get(rootY)) { + parent.put(rootY, rootX); + } else if (rank.get(rootX) < rank.get(rootY)) { + parent.put(rootX, rootY); + } else { + parent.put(rootY, rootX); + rank.put(rootX, rank.get(rootX) + 1); + } + } + } + + // Vérifie si deux éléments appartiennent au même ensemble + public boolean connected(T x, T y) { + return find(x).equals(find(y)); + } +} diff --git a/src/TreesAlgo/AbstractArbreCouvrant.java b/src/TreesAlgo/AbstractArbreCouvrant.java new file mode 100644 index 0000000000000000000000000000000000000000..d0912f20249b1ba4bbf69cf24ba7f2d0ca6e0c32 --- /dev/null +++ b/src/TreesAlgo/AbstractArbreCouvrant.java @@ -0,0 +1,20 @@ +package TreesAlgo; + +import Graph.Graph; +import Graph.Edge; + +import java.util.HashSet; +import java.util.Random; + +import java.util.Set; + +public abstract class AbstractArbreCouvrant implements ArbreCouvrant{ + protected final Graph graph; + protected final Random generator = new Random(); + protected final Set<Edge> arbre = new HashSet<>(); + + + public AbstractArbreCouvrant(Graph graph) { + this.graph = graph; + } +} diff --git a/src/TreesAlgo/AldousBroder.java b/src/TreesAlgo/AldousBroder.java new file mode 100644 index 0000000000000000000000000000000000000000..62099a2ab778d7ee1879a0524b68e9c409dabeb5 --- /dev/null +++ b/src/TreesAlgo/AldousBroder.java @@ -0,0 +1,45 @@ +package TreesAlgo; + +import Graph.Edge; +import Graph.Graph; +import Graph.Arc; + +import java.util.HashSet; +import java.util.Set; + +public class AldousBroder extends AbstractArbreCouvrant { + + private int sommetActuel; // Sommet courant de la marche + private final Set<Integer> sommetVisite; // Ensemble des sommets visités + + public AldousBroder(Graph graph) { + super(graph); + this.sommetVisite = new HashSet<>(); + this.sommetActuel = generator.nextInt(graph.order); // Sommet initial aléatoire + this.sommetVisite.add(sommetActuel); // Marquer le sommet initial + marcher(); // Lancer la marche + } + + public void marcher() { + while (sommetVisite.size() < graph.order) { + // Sélection aléatoire d'un arc sortant + Arc arc = graph.outEdges(sommetActuel)[generator.nextInt(graph.outEdges(sommetActuel).length)]; + + // Récupérer le sommet voisin via l'arc + int voisin = arc.getDest(); + + // Si le voisin n'est pas visité, ajouter l'arête dans l'arbre + if (sommetVisite.add(voisin)) { // La méthode add() retourne false si le voisin est déjà présent + arbre.add(arc.support); // Ajouter l'arête supportée + } + + // Mettre à jour le sommet actuel + sommetActuel = voisin; + } + } + + @Override + public Set<Edge> arbreCouvrant() { + return arbre; // Retourner l'arbre couvrant construit + } +} diff --git a/src/TreesAlgo/ArbreCouvrant.java b/src/TreesAlgo/ArbreCouvrant.java new file mode 100644 index 0000000000000000000000000000000000000000..6a66a1469e7fde71cfe37c1b2332bf80f43db4ed --- /dev/null +++ b/src/TreesAlgo/ArbreCouvrant.java @@ -0,0 +1,10 @@ +package TreesAlgo; + +import Graph.Edge; + +import java.util.Set; + +public interface ArbreCouvrant { + + public Set<Edge> arbreCouvrant(); +} diff --git a/src/TreesAlgo/Contraction.java b/src/TreesAlgo/Contraction.java new file mode 100644 index 0000000000000000000000000000000000000000..e13a28742cda100a4fa08322f3603e28c39cdf3a --- /dev/null +++ b/src/TreesAlgo/Contraction.java @@ -0,0 +1,68 @@ +package TreesAlgo; + +import Graph.Graph; +import Graph.Edge; +import Structures.ContractionGraph; + + +import java.util.*; + +public class Contraction extends AbstractArbreCouvrant { + private final ContractionGraph contractionGraph; + private final TreesAlgo.UnionFind<Integer> unionFind; + private final Node[] nodes; // Tableau pour stocker les informations des sommets + + public Contraction(Graph graph) { + super(graph); + this.contractionGraph = new ContractionGraph(graph); + this.unionFind = new TreesAlgo.UnionFind<>(); + this.nodes = new Node[graph.getIncidency().size()]; // Initialiser le tableau de noeuds + Arrays.fill(this.nodes, new Node()); // Initialiser chaque noeud avec un objet Node + } + + @Override + public Set<Edge> arbreCouvrant() { + List<Integer> edges = new ArrayList<>(); + + // Remplir la liste avec des indices d'arêtes + for (int i = 0; i < contractionGraph.getExtrimite1().size(); i++) { + edges.add(i); + } + + // Mélanger les arêtes pour les traiter dans un ordre aléatoire + Collections.shuffle(edges); + for (int edgeIndex : edges) { + if (contractionGraph.boucle(edgeIndex)) { + continue; // Passer les boucles + } + + // Récupérer les sommets connectés par l'arête + int vertex1 = contractionGraph.getExtrimite1().get(edgeIndex); + int vertex2 = contractionGraph.getExtrimite2().get(edgeIndex); + + // Si les sommets sont déjà connectés, ignorer cette arête + if (unionFind.find(vertex1) == unionFind.find(vertex2)) { + continue; + } + + // Ajouter l'arête à l'arbre couvrant + arbre.add(contractionGraph.getInitialEdges().get(edgeIndex)); + + // Union des sommets pour contracter l'arête + unionFind.union(vertex1, vertex2); + + // Contracter l'arête dans le graphe + contractionGraph.contract(edgeIndex); + } + + return arbre; + } + + + + // Classe interne pour représenter un sommet avec sa profondeur et hauteur + private static class Node { + int depth = 0; // Initialiser à 0 ou à une autre valeur par défaut + int height = 0; // Initialiser à 0 ou à une autre valeur par défaut + } +} diff --git a/src/TreesAlgo/Insertion.java b/src/TreesAlgo/Insertion.java new file mode 100644 index 0000000000000000000000000000000000000000..5cba34e6cabdda93785478bf358b7888e4f0e4f7 --- /dev/null +++ b/src/TreesAlgo/Insertion.java @@ -0,0 +1,43 @@ +package TreesAlgo; + +import Graph.Edge; +import Graph.Graph; + +import java.util.*; + +public class Insertion extends AbstractArbreCouvrant { + + private List<Edge> arretesRestant = new ArrayList<>(); + private final TreesAlgo.UnionFind<Integer> forest; + + public Insertion(Graph graph) { + super(graph); + this.forest = new TreesAlgo.UnionFind<>(); + // Initialiser la liste d'arêtes restantes + for (LinkedList<Edge> edgeList : graph.getIncidency()) { + arretesRestant.addAll(edgeList); + } + } + + @Override + public Set<Edge> arbreCouvrant() { + // Mélanger les arêtes pour assurer un tirage aléatoire + Collections.shuffle(arretesRestant, generator); + + while (arbre.size() < graph.order - 1) { + // Tirer une arête au hasard + Edge e = arretesRestant.remove(0); + int u = e.getSource(); + int v = e.getDest(); + + // Vérifier si l'ajout de l'arête forme un cycle + if (!forest.connected(u, v)) { + // Si ce n'est pas un cycle, ajouter l'arête à l'arbre couvrant + arbre.add(e); + forest.union(u, v); // Fusionner les ensembles contenant u et v + } + } + + return arbre; + } +} diff --git a/src/TreesAlgo/Prim.java b/src/TreesAlgo/Prim.java new file mode 100644 index 0000000000000000000000000000000000000000..539aaeca28fb14a7c4bfe716e633852a6bf20d26 --- /dev/null +++ b/src/TreesAlgo/Prim.java @@ -0,0 +1,79 @@ +package TreesAlgo; + +import Graph.Edge; +import Graph.Graph; +import Graph.Arc; + +import java.util.*; + +public class Prim extends AbstractArbreCouvrant{ + + + private final Map<Edge,Double> poids=new HashMap<>(); + + + private final PriorityQueue<Edge> queue= new PriorityQueue<>(Comparator.comparingDouble(poids::get)); + + + + public Prim(Graph graph) { + super(graph); + + genererPoidsAleatoires(); + + } + private void genererPoidsAleatoires() { + int nbEdge = graph.getEdgeCardinality(); + int borneSup = (nbEdge > 500) ? Integer.MAX_VALUE : nbEdge * nbEdge * nbEdge; + + // Étape 1 : Attribuer un poids aléatoire [0, 1] à chaque arête + for (int i = 0; i < graph.order; i++) { + if (graph.isVertex(i)) { + for (Edge edge : graph.getIncidency().get(i)) { + if (!poids.containsKey(edge)) { // Assurez-vous de ne pas ajouter deux fois une arête + // Utilisez nextInt() pour obtenir un entier entre 0 et borneSup, + // puis divisez par borneSup pour normaliser le poids entre 0 et 1. + double weight = generator.nextInt(borneSup) / (double) borneSup; + poids.put(edge, weight); + } + } + } + } + } + + @Override + public Set<Edge> arbreCouvrant() { + // Étape 1 : Choisir un sommet de départ + int startVertex = 0; // On peut commencer avec n'importe quel sommet + Set<Integer> visited = new HashSet<>(); + visited.add(startVertex); + + // Étape 2 : Ajouter toutes les arêtes sortantes du sommet choisi dans la priorité + for (Edge edge : graph.getIncidency().get(startVertex)) { + queue.add(edge); + } + + // Étape 3 : Boucle principale de Prim + while (!queue.isEmpty()) { + Edge edge = queue.poll(); // Obtenir l'arête de poids minimum + int dest = edge.getDest(); + + // Si l'une des extrémités de l'arête n'est pas encore visitée, l'ajouter à l'arbre + if (!visited.contains(dest)) { + arbre.add(edge); + visited.add(dest); + + // Ajouter toutes les arêtes sortantes de ce sommet dans la queue + for (Edge e : graph.getIncidency().get(dest)) { + if (!visited.contains(e.getDest())) { + queue.add(e); + } + } + } + } + + return arbre; + } + + +} diff --git a/src/TreesAlgo/RandomSearch.java b/src/TreesAlgo/RandomSearch.java new file mode 100644 index 0000000000000000000000000000000000000000..5bc258059a5fc423d7c77d67c83ec71946b267fd --- /dev/null +++ b/src/TreesAlgo/RandomSearch.java @@ -0,0 +1,57 @@ +package TreesAlgo; + +import Graph.Arc; +import Graph.Graph; +import Graph.Edge; + +import java.util.*; + +public class RandomSearch extends AbstractArbreCouvrant { + + private Set<Integer> visited= new HashSet<>(); + + public RandomSearch(Graph graph) { + super(graph); + } + + + @Override + public Set<Edge> arbreCouvrant() { + // Tirer un sommet aléatoire parmi les sommets existants du graphe + int root = generator.nextInt(graph.order); // Sommet aléatoire entre 0 et graph.order - 1 + visited.add(root); + + // Liste des arcs à explorer, initialement vide + List<Arc> frontier = new ArrayList<>(); + + // Ajouter les arcs sortants du sommet de départ à la frontière + for (Arc arc : graph.outEdges(root)) { + frontier.add(arc); + } + + // Tant qu'il reste des arcs à explorer + while (!frontier.isEmpty()) { + // Choisir un arc au hasard parmi ceux de la frontière + Collections.shuffle(frontier, generator); + Arc arc = frontier.remove(0); // Retirer l'arc choisi + + int dest = arc.getDest(); + + // Si le sommet destination n'a pas été visité, on l'explore + if (!visited.contains(dest)) { + visited.add(dest); + // Ajouter l'arc (sous forme d'Edge) à l'arbre couvrant + arbre.add(arc.support); // Accéder à l'Edge via le champ 'support' de l'Arc + + // Ajouter tous les arcs sortants du sommet destination à la frontière + for (Arc newArc : graph.outEdges(dest)) { + if (!visited.contains(newArc.getDest())) { + frontier.add(newArc); // Ajouter les arcs sortants à la frontière + } + } + } + } + + return arbre; // Retourner le Set<Edge> + } +} \ No newline at end of file diff --git a/src/TreesAlgo/SuppressionCycle.java b/src/TreesAlgo/SuppressionCycle.java new file mode 100644 index 0000000000000000000000000000000000000000..ab955caee5d409eb687977ec4ebcc71976ba7dd9 --- /dev/null +++ b/src/TreesAlgo/SuppressionCycle.java @@ -0,0 +1,91 @@ +package TreesAlgo; + +import Graph.Edge; +import Graph.Graph; + +import java.util.*; + +public class SuppressionCycle extends AbstractArbreCouvrant { + + private final Map<Integer, Integer> parent = new HashMap<>(); // Parent de chaque sommet + private final Set<Integer> visited = new HashSet<>(); // Sommets visités + private final Set<Integer> inCycle = new HashSet<>(); // Sommets dans un cycle + private final List<Edge> edgesInCycle = new ArrayList<>(); // Arêtes à supprimer dans un cycle + private final int root; // Racine de l'arbre + + public SuppressionCycle(Graph graph) { + super(graph); + this.root = 0; // Sommet racine (à ajuster si nécessaire) + construireArbre(); + } + + private void construireArbre() { + // Lancer le DFS à partir du sommet racine + dfsIteratif(root); + supprimerCycles(); + } + + private void dfsIteratif(int sommet) { + Stack<Integer> stack = new Stack<>(); + Stack<Edge> edgeStack = new Stack<>(); // Stack pour garder la trace des arêtes + stack.push(sommet); + visited.add(sommet); + + while (!stack.isEmpty()) { + int currSommet = stack.peek(); + boolean addedNew = false; + + // Parcourir les voisins à travers les arêtes incidentes + for (Edge edge : graph.getIncidency().get(currSommet)) { + int voisin = edge.oppositeExtremity(currSommet); + + if (!visited.contains(voisin)) { + // Ajouter l'arête dans l'arbre couvrant + arbre.add(edge); + parent.put(voisin, currSommet); // Mettre à jour le parent + visited.add(voisin); + edgeStack.push(edge); // Empiler l'arête + stack.push(voisin); // Empiler le voisin + addedNew = true; + break; // Sortir de la boucle dès qu'un voisin est ajouté + } else if (inCycle.contains(voisin)) { + // Si on revient à un sommet déjà dans un cycle, un cycle est détecté + System.out.println("Cycle détecté : " + edge); + edgesInCycle.add(edge); + inCycle.add(voisin); // Ajouter le sommet au cycle + return; // Fin de la détection de cycle + } else { + // Si l'arête forme un cycle avec un sommet déjà visité, on ajoute à la liste des arêtes + edgesInCycle.add(edge); + } + } + + // Si aucun voisin n'a été ajouté, on retire le sommet de la pile de récursion + if (!addedNew) { + stack.pop(); + if (!edgeStack.isEmpty()) { + edgeStack.pop(); // Retirer l'arête associée + } + } + } + } + + private void supprimerCycles() { + // Supprimer les cycles détectés + Set<Edge> edgesToRemove = new HashSet<>(); + for (Edge edge : edgesInCycle) { + if (arbre.contains(edge)) { + edgesToRemove.add(edge); + System.out.println("Suppression de l'arête : " + edge); + } + } + + // Supprimer les arêtes du cycle + arbre.removeAll(edgesToRemove); + } + + @Override + public Set<Edge> arbreCouvrant() { + return arbre; // Retourner l'arbre couvrant + } +} diff --git a/src/TreesAlgo/Wilson.java b/src/TreesAlgo/Wilson.java new file mode 100644 index 0000000000000000000000000000000000000000..a87fcbb89f12d9378fd4a196a5316e456ac9021c --- /dev/null +++ b/src/TreesAlgo/Wilson.java @@ -0,0 +1,55 @@ +package TreesAlgo; + +import Graph.Edge; +import Graph.Graph; + +import java.util.*; +import java.util.stream.IntStream; + +public class Wilson extends AbstractArbreCouvrant { + + private final Set<Integer> sommetVisite = new HashSet<>(); + private final Map<Integer, Edge> outArc = new HashMap<>(); + + public Wilson(Graph graph) { + super(graph); + // Sort vertices in descending order of degree and choose a vertex at each iteration + IntStream.range(0, graph.order) + .boxed() + .sorted(Comparator.comparingInt(graph::degreSommet).reversed()) + .forEach( + source -> { + if (sommetVisite.isEmpty()) { + sommetVisite.add(source); // Add the first vertex + } else { + walk(source); // Perform a random walk starting from other vertices + } + } + ); + } + + private void walk(int source) { + int currentVertex = source; + + // Perform a random walk until reaching an already visited vertex + while (!sommetVisite.contains(currentVertex)) { + int rand = generator.nextInt(graph.getIncidency().get(currentVertex).size()); + Edge edge = graph.getIncidency().get(currentVertex).get(rand); // Get a random edge + outArc.put(currentVertex, edge); // Record the edge to retrace the path later + currentVertex = edge.oppositeExtremity(currentVertex); // Move to the other endpoint of the edge + } + + currentVertex = source; + // Retrace the path and mark vertices as visited + while (!sommetVisite.contains(currentVertex)) { + sommetVisite.add(currentVertex); // Add the vertex to the spanning tree + arbre.add(outArc.get(currentVertex)); // Add the edge to the result + currentVertex = outArc.get(currentVertex).oppositeExtremity(currentVertex); // Move to the other endpoint + } + } + + @Override + public Set<Edge> arbreCouvrant() { + return arbre; // Return the spanning tree + } +}