diff --git a/Dijkstra/algo.py b/Dijkstra/algo.py index 51fd726..ebcc86c 100644 --- a/Dijkstra/algo.py +++ b/Dijkstra/algo.py @@ -1,74 +1,150 @@ -# https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm +def dijkstra(graph: dict, start: any) -> tuple: + """ + L'algorithme de Dijkstra pour trouver les plus courts chemins depuis un sommet de départ. -graph = { - 'A': [('B', 4), ('C', 2)], - 'B': [('C', 5), ('D', 10)], - 'C': [('D', 3), ('E', 8)], - 'D': [('E', 4), ('F', 11)], - 'E': [('G', 6)], - 'F': [('G', 2)], - 'G': [] -} + Args: + graph (dict): Le graphe à parcourir sous forme de dictionnaire, où les clés sont les sommets + et les valeurs sont des listes de tuples (voisin, poids). + start (any): Le sommet de départ. -p = {} - -def chemin(predecesseur, debut, fin): - chemin = [] - courant = fin - while courant != debut: - chemin.insert(0, courant) - courant = predecesseur.get(courant) - if courant is None: - return None - chemin.insert(0, debut) - return chemin - - -def initialisation(G, debut): - distances = {key: float('inf') for key in G} - distances[debut] = 0 - return distances - - -def trouve_min(Q, distances): - mini = float('inf') - sommet = None - for s in Q: - if distances[s] < mini: - mini = distances[s] - sommet = s - return sommet - -def poid(s1, s2): - for voisin, poids in graph[s1]: - if voisin == s2: - return poids - return float('inf') - -def distances_update(s1, s2, distances, predecesseur): - poids = poid(s1, s2) - if distances[s2] > distances[s1] + poids: - distances[s2] = distances[s1] + poids - predecesseur[s2] = s1 - -def dijkstra(G, debut='A'): - distances = initialisation(G, debut) - predecesseur = {} - Q = set(G.keys()) - - while Q: - s1 = trouve_min(Q, distances) - if s1 is None: - break - Q.remove(s1) - - for voisin, _ in G[s1]: - if voisin in Q: - distances_update(s1, voisin, distances, predecesseur) + Returns: + tuple: Un tuple contenant deux dictionnaires : + - Le premier associe à chaque sommet la distance minimale depuis le sommet de départ. + - Le second associe à chaque sommet (sauf le départ) son prédécesseur sur le chemin le plus court. + Exemples: + >>> graph = { + ... 'A': [('B', 4), ('C', 2)], + ... 'B': [('C', 5), ('D', 10)], + ... 'C': [('D', 3), ('E', 8)], + ... 'D': [('E', 4), ('F', 11)], + ... 'E': [('G', 6)], + ... 'F': [('G', 2)], + ... 'G': [] + ... } + >>> distances, predecesseur = dijkstra(graph, 'A') + >>> distances['A'] + 0 + >>> distances['B'] + 4 + >>> distances['C'] + 2 + >>> distances['D'] + 5 + >>> distances['E'] + 9 + >>> distances['F'] + 16 + >>> distances['G'] + 15 + >>> predecesseur['B'] + 'A' + >>> predecesseur['C'] + 'A' + >>> predecesseur['D'] + 'C' + >>> predecesseur['E'] + 'D' + >>> predecesseur['F'] + 'D' + >>> predecesseur['G'] + 'E' + """ + distances, predecesseur, unvisited = {}, {}, {} + for sommet in graph: + distances[sommet] = float('inf') + unvisited[sommet] = distances[sommet] + distances[start] = 0 + while unvisited: + current = min(unvisited, key=unvisited.get) + del unvisited[current] + for voisin, poid in graph[current]: + new_distance = distances[current] + poid + if new_distance < distances[voisin]: + distances[voisin] = new_distance + predecesseur[voisin] = current + if voisin in unvisited: + unvisited[voisin] = new_distance return distances, predecesseur + +def reconstruire_chemin(predecesseur: dict, start: any, end: any) -> list: + """reconstruit le chemin grace au predecesseur + + Args: + predecesseur (dict): les predecesseurs + start (any): le debut + end (any): la fin + + Returns: + list: renvoie une liste contenant le chemin le plus court (si aucun trouver alors renvoie liste vide) + Examples: + >>> pred = {'B': 'A', 'C': 'A', 'D': 'C', 'E': 'D', 'F': 'D', 'G': 'E'} + >>> reconstruire_chemin(pred, 'A', 'F') + ['A', 'C', 'D', 'F'] + >>> reconstruire_chemin(pred, 'A', 'G') + ['A', 'C', 'D', 'E', 'G'] + """ + chemin = [end] + current = end + while current != start: + if current not in predecesseur: + return [] + current = predecesseur[current] + chemin.append(current) + chemin.reverse() + return chemin + +def solutions_dijkstra(graph: dict, start: any) -> dict: + """elle renvoie le dictionnaire des noeud et distances grace au chemin + + Args: + graph (dict): le graphe + start (any): le debut + + Returns: + dict: contenant les noeuds et leurs distances + Exemples: + >>> graph = { + ... 'A': [('B', 4), ('C', 2)], + ... 'B': [('C', 5), ('D', 10)], + ... 'C': [('D', 3), ('E', 8)], + ... 'D': [('E', 4), ('F', 11)], + ... 'E': [('G', 6)], + ... 'F': [('G', 2)], + ... 'G': [] + ... } + >>> sol = solutions_dijkstra(graph, 'A') + >>> sol['F'] + {'distance': 16, 'chemin': ['A', 'C', 'D', 'F']} + >>> sol['G'] + {'distance': 15, 'chemin': ['A', 'C', 'D', 'E', 'G']} + """ + distances, predecesseur = dijkstra(graph, start) + solutions = {} + for sommet in graph: + if sommet == start: + chemin = [start] + else: + chemin = reconstruire_chemin(predecesseur, start, sommet) + if not chemin: + chemin = None + solutions[sommet] = {"distance": distances[sommet], "chemin": chemin} + return solutions + if __name__ == "__main__": - distances, predecesseur = dijkstra(graph, 'A') - print("Distances depuis A :", distances) - print("Chemin le plus court de A à G :", chemin(predecesseur, 'A', 'G')) + import doctest + doctest.testmod(verbose=True) + graph = { + 'A': [('B', 4), ('C', 2)], + 'B': [('C', 5), ('D', 10)], + 'C': [('D', 3), ('E', 8)], + 'D': [('E', 4), ('F', 11)], + 'E': [('G', 6)], + 'F': [('G', 2)], + 'G': [] + } + solution = solutions_dijkstra(graph, 'A') + for sommet, info in solution.items(): + print(f"sommet: {sommet} ----- Distance: {info['distance']} ------- chemin: {info['chemin']}") + diff --git a/Dijkstra/fonctions.md b/Dijkstra/fonctions.md new file mode 100644 index 0000000..fd91053 --- /dev/null +++ b/Dijkstra/fonctions.md @@ -0,0 +1,46 @@ +# Dijkstra +## Fonctions +### `dijkstra(graph: dict, start: any) -> tuple` +- **Paramètres :** + - `graph` : Dictionnaire représentant le graphe. Chaque clé est un noeud et sa valeur est une liste de tuples `(voisin, poids)`. + - `start` : Le noeud de départ. +- **Retourne :** + - Un tuple contenant : + - `distances` : Un dictionnaire associant à chaque noeud la distance minimale depuis `start`. + - `predecesseur` : Un dictionnaire associant à chaque noeud son prédécesseur dans le chemin le plus court. + +--- +### `reconstruct_chemin(predecesseur: dict, start: any, end: any) -> list` + +- **Paramètres :** + - `predecesseur` : Dictionnaire des prédécesseurs. + - `start` : Le noeud de départ. + - `end` : Le noeud d'arrivée. +- **Retourne :** + - Une liste représentant le chemin le plus court de `start` à `end`. + Si aucun chemin n'est trouvé, la fonction renvoie une liste vide. + +--- +### `solutions_dijkstra(graph: dict, start: any) -> dict` + +- **Paramètres :** + - `graph` : Dictionnaire représentant le graphe. + - `start` : Le noeud de départ. +- **Retourne :** + - Un dictionnaire où chaque clé est un noeud du graphe et la valeur associée est un sous-dictionnaire contenant : + - `"distance"` : La distance minimale depuis `start`. + - `"chemin"` : Le chemin le plus court sous forme de liste de noeuds None si pas trouver. + +--- +## Utilisation +Le graphe doit être défini sous forme de dictionnaire. Exemple : + ```python +graph = { + 'A': [('B', 4), ('C', 2)], + 'B': [('C', 5), ('D', 10)], + 'C': [('D', 3), ('E', 8)], + 'D': [('E', 4), ('F', 11)], + 'E': [('G', 6)], + 'F': [('G', 2)], + 'G': [] +} diff --git a/graphes/DS_possible/elouan_fare.tar b/graphes/DS_possible/elouan_fare.tar deleted file mode 100644 index 4d6ac82..0000000 Binary files a/graphes/DS_possible/elouan_fare.tar and /dev/null differ diff --git a/graphes/leaudibidon/Water_jug(en_xz).tar b/graphes/leaudibidon/Water_jug(en_xz).tar deleted file mode 100644 index dae43c7..0000000 Binary files a/graphes/leaudibidon/Water_jug(en_xz).tar and /dev/null differ diff --git a/pygame/bouncing_ball_project.zip b/pygame/bouncing_ball_project.zip deleted file mode 100644 index d0b2755..0000000 Binary files a/pygame/bouncing_ball_project.zip and /dev/null differ