From ae64e478506eb573cb2c94a5c0687848bd19e09a Mon Sep 17 00:00:00 2001 From: Spectre Date: Tue, 21 Jan 2025 15:36:32 +0100 Subject: [PATCH] Enfin finis ce DM! --- graphes/leaudibidon/.gitignore | 1 - graphes/leaudibidon/Water_jug.py | 299 ++++++++++++++++++++++++++++++ graphes/leaudibidon/main.pu | 31 ---- graphes/leaudibidon/main.py | 300 +++---------------------------- 4 files changed, 326 insertions(+), 305 deletions(-) delete mode 100644 graphes/leaudibidon/.gitignore create mode 100644 graphes/leaudibidon/Water_jug.py delete mode 100644 graphes/leaudibidon/main.pu diff --git a/graphes/leaudibidon/.gitignore b/graphes/leaudibidon/.gitignore deleted file mode 100644 index f104652..0000000 --- a/graphes/leaudibidon/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.py diff --git a/graphes/leaudibidon/Water_jug.py b/graphes/leaudibidon/Water_jug.py new file mode 100644 index 0000000..02bd3fb --- /dev/null +++ b/graphes/leaudibidon/Water_jug.py @@ -0,0 +1,299 @@ +#!/usr/bin/python3 +# -*- Coding: utf-8 -*- + +from typing import Deque + + +class Graphe_Oriente(object): + """ + Classe des Graphes Orientés (GO). + + Les graphes sont représentés par + - une liste de sommets + - leur liste d'adjacence + + Attributs (publics) + - sommets : liste des sommets + - voisins : dictionnaire des listes d'adjacence + + Méthodes (publiques) + - ajoute_sommet : ajout d'un sommet + - ajoute_arcs : ajout d'un arc + """ + + def __init__(self): + self.sommets = list() + self.voisins = dict() + return None + + def ajoute_sommet(self, s): + """ + Ajoute le sommet s à l'instance de GO. + + :param s data: etiquette du sommet + """ + + self.sommets.append(s) + self.voisins[s] = list() + return None + + def ajoute_arc(self, origine, extremite): + """ + Ajoute l'arc origine -> extremite à l'instance de GO. + + :param self Graphe_Oriente: instance à laquelle ajouter un arc + :param origine data: un sommet de l'instance, origine de l'arc + :param extremite data: un sommet de l'instance, extremité de l'arc + + :return None: + + :effet de bord: Modification de l'instance + + :pré-condition: les sommets doivent exister + """ + assert origine in self.sommets, "{} inconnu".format(origine) + assert extremite in self.sommets, "{} inconnu".format(extremite) + self.voisins[origine].append(extremite) + return None + + +def construire_chemins(graphe, depart): + """ + Construit tous les cheminement du graphe de depart donné + + :param graphe GO: graphe à parcourir + :param depart data: sommet de départ dans le graphe + + :return resultat dict: dictionnaire + - clef : sommet atteint + - valeur : tuple (longueur itinéraire, sommet prédécesseur) + + :effet de bord: Aucun + """ + resultat = dict() + file = [depart] + resultat[depart] = (0, None) + while len(file) > 0: + sommet = file.pop(0) + for voisin in graphe.voisins[sommet]: + if voisin not in resultat: + distance = resultat[sommet][0] + 1 + resultat[voisin] = (distance, sommet) + file.append(voisin) + + return resultat + + +def reconstruire_chemin_vers(dico_chemins, *arrivee): + """ + Remonte tout l'itinéraire depuis un sommet fixé + vers un ou des sommets du graphe + + :param dico_chemins dict: table des longueurs/prédécessurs + :param *arrivee: nombre quelconque de sommet à atteindre + (si vide, on considère tous les sommets) + :return resultat list: liste des chemins ie des listes de sommets traversés + """ + chemins = list() + cibles = arrivee + if len(cibles) == 0: + return list(dico_chemins.keys()) + for sommet in cibles: + b = [] + a = sommet + while a is not None: + b.insert(0, a) + a = dico_chemins[a][1] + chemins.append(b) + return chemins + + +def affichage_chemin(chemin): + """ + Produit le texte d'affichage d'un chemin + + :param chemin list: liste des sommets constituant le chemin + :return string: chemin mis en forme pour affichage + """ + long = len(chemin) - 1 + return "{} etapes :\n".format(long) + "\n\t-> ".join( + [str(sommet) for sommet in chemin] + ) + + +# DEFINITION DES OPERATIONS DE TRANSITION + + +def vider(numero, etat): + """ + Vide un bidon + + :param numero INT: index du bidon + :param etat tuple: etat des bidons avant l'opération + :return tuple: nouvel etat aprés opération + :effet de bord: aucun + """ + index_b = list(etat) + index_b[numero] = 0 + return tuple(index_b) + + +def remplir(numero, etat, capacites): + """ + Remplit un bidon + + :param numero INT: index du bidon + :param etat tuple: etat des bidons avant l'opération + :param capacites tuple: capacités des bidons + :return tuple: nouvel etat aprés opération + :effet de bord: aucun + """ + cap = list(capacites) + index_b = list(etat) + index_b[numero] = cap[numero] + return tuple(index_b) + + +def transvaser(source, destination, etat, capacites): + """ + Transvase un bidon dans un autre + + :param origine int: index du bidon source + :param destination int: index du bidon destination + :param etas tuple: etat des bidons avant l'opération + :param capacites tuple: capacités des bidons + :return tuple: nouvel etat aprés opération + :effet de bord: aucun + """ + transfer_amount = min( + list(etat)[source], list(capacites)[destination] - list(etat)[destination] + ) + new_state = list(etat) + + new_state[source] -= transfer_amount + new_state[destination] += transfer_amount + return tuple(new_state) + + +# FONCTION LIEES AU GRAPHE DU WATER_JUG + +# Pour construire les etats +def produit_cartesien(*listes): + """ + Concatène les tuples issus des différentes listes + + :param *listes: un nombre quelconque de listes de tuples + :return list: une liste des tuples concaténés + + Exemple + -------- + >>> produit_cartesien([(1,2), (3, 4)], [(5, 6), (7, 8,)]) + [(1, 2, 5, 6), (1, 2, 7, 8), (3, 4, 5, 6), (3, 4, 7, 8)] + """ + if listes == None: + return [] + if len(listes) == 1: + return listes[0] + result = [] + for elt in listes[0]: + for tuples in produit_cartesien(*listes[1:]): + result.append(elt + tuples) + return result + + +def creer_water_jug(*capacites): + """ + Création du graphe de Water Jug en fonction des capacités des bidons + + :param *capacites: capacités des bidons disponibles + :return resultat GO: le graphe orienté du W_J + :pre-condition: au moins 2 bidons + + :effet de bord: Aucun + """ + nb_bidons = len(capacites) + assert nb_bidons >= 2, "Pas assez de bidons" + resultat = Graphe_Oriente() + # CREATION DES SOMMETS + etats_marginaux = [ + [(contenu,) for contenu in range(1 + capacite)] for capacite in capacites + ] + etats_systeme = produit_cartesien(*etats_marginaux) + for etat in etats_systeme: + resultat.ajoute_sommet(etat) + # CREATION DES TRANSITIONS + for sommet in resultat.sommets: + voisins = list() + # VIDER + for bidon in range(nb_bidons): + voisin = vider(bidon, sommet) + if voisin != sommet and not (voisin in voisins): + voisins.append(voisin) + # REMPLIR + for bidon in range(nb_bidons): + voisin = remplir(bidon, sommet, capacites) + if voisin != sommet and not (voisin in voisins): + voisins.append(voisin) + # TRANSVASER + for origine in range(nb_bidons): + for destination in range(nb_bidons): + if origine != destination: + voisin = transvaser(origine, destination, sommet, capacites) + if voisin != sommet and not (voisin in voisins): + voisins.append(voisin) + # CREATION LISTES ADJACENCE + for voisin in voisins: + resultat.ajoute_arc(sommet, voisin) + return resultat + + +def atteindre(quantite, graphe_water_jug, depart=None, plus_court=False): + """ + Résolution du problème pour une quantite donnée + + :param quantite int: la quantité à mesurer + :param graphe_water_jug GO: le graphe de la situation + :param depart tuple: etat initial, sommet d'origine dans le graphe + (bidons tous vides si depart est None, ie non fourni) + :param plus_court bool: si vrai, seul le(s) plus court(s) chemin(s) + est (sont) retenu(s) + + :return list: la liste des tuples (sommet arrivee, nb d'etapes, ititneraire) ok + """ + if depart is None: + nb_bidons = len(graphe_water_jug.sommets[0]) + depart = tuple([0 for i in range(nb_bidons)]) + chemins = construire_chemins(graphe_water_jug, depart) + resultat = list() + for sommet in chemins: + if quantite in sommet: + longueur, predecesseur = chemins[sommet] + chemin = reconstruire_chemin_vers(chemins, sommet).pop() + index = len(resultat) + resultat.append((sommet, longueur, chemin)) + while index > 0: + if resultat[index - 1][1] < longueur: + break + else: + resultat[index], resultat[index - 1] = ( + resultat[index - 1], + resultat[index], + ) + index -= 1 + if len(resultat) < 2 or not (plus_court): + return resultat + mini = resultat[0][1] + return [element for element in resultat if element[1] == mini] + + +def main(): + solutions = atteindre(4, creer_water_jug(3,5)) + + for (sommet_final, nb_etapes, chemin) in solutions: + print(f"sommet atteint: {sommet_final}, en {nb_etapes} etapes") + print(affichage_chemin(chemin)) + print("-----------------------------------------") + + +if __name__ == "__main__": + main() diff --git a/graphes/leaudibidon/main.pu b/graphes/leaudibidon/main.pu deleted file mode 100644 index fdff01c..0000000 --- a/graphes/leaudibidon/main.pu +++ /dev/null @@ -1,31 +0,0 @@ -class leaudibidon(object): - - def __init__(self,capacity): - self.capacity = capacity - self.quantity = 0 - - def fullfill_b5(b5,b3): - b5.quantity = b5.capacity - print("Fullfill b5") - - def fullfill_b3(b5,b3): - b3.quantity = b3.capacity - print("Fullfill b3") - - def void_b5(b5,b3): - b5.quantity = 0 - print("void b5") - - - def void_b3(b5,b3): - b3.quantity = 0 - print("void b3") - - def transfer_b5_b3(b5,b3): - transfer_amount = min(b5.quantity, b3.capacity - b3.quantity) - b5.quantity, b3.quantity = b5.quantity - transfer_amount, b3.quantity + transfer_amount - - - def transfer_b3_b5(b5,b3): - transfer_amount = min(b3.quantity, b5.capacity - b5.quantity) - b5.quantity, b3.quantity = b5.quantity + transfer_amount, b3.quantity - transfer_amount diff --git a/graphes/leaudibidon/main.py b/graphes/leaudibidon/main.py index 68bda87..fdff01c 100644 --- a/graphes/leaudibidon/main.py +++ b/graphes/leaudibidon/main.py @@ -1,277 +1,31 @@ -#!/usr/bin/python3 -# -*- Coding: utf-8 -*- +class leaudibidon(object): -from typing import Deque + def __init__(self,capacity): + self.capacity = capacity + self.quantity = 0 + + def fullfill_b5(b5,b3): + b5.quantity = b5.capacity + print("Fullfill b5") + + def fullfill_b3(b5,b3): + b3.quantity = b3.capacity + print("Fullfill b3") + + def void_b5(b5,b3): + b5.quantity = 0 + print("void b5") -class Graphe_Oriente(object): - """ - Classe des Graphes Orientés (GO). - - Les graphes sont représentés par - - une liste de sommets - - leur liste d'adjacence + def void_b3(b5,b3): + b3.quantity = 0 + print("void b3") - Attributs (publics) - - sommets : liste des sommets - - voisins : dictionnaire des listes d'adjacence - - Méthodes (publiques) - - ajoute_sommet : ajout d'un sommet - - ajoute_arcs : ajout d'un arc - """ - - def __init__(self): - self.sommets = list() - self.voisins = dict() - return None - - def ajoute_sommet(self, s): - """ - Ajoute le sommet s à l'instance de GO. - - :param s data: etiquette du sommet - """ - - self.sommets.append(s) - self.voisins[s] = list() - return None - - def ajoute_arc(self, origine, extremite): - """ - Ajoute l'arc origine -> extremite à l'instance de GO. - - :param self Graphe_Oriente: instance à laquelle ajouter un arc - :param origine data: un sommet de l'instance, origine de l'arc - :param extremite data: un sommet de l'instance, extremité de l'arc - - :return None: - - :effet de bord: Modification de l'instance - - :pré-condition: les sommets doivent exister - """ - assert origine in self.sommets, "{} inconnu".format(origine) - assert extremite in self.sommets, "{} inconnu".format(extremite) - self.voisins[origine].append(extremite) - return None - -def construire_chemins(graphe, depart): - """ - Construit tous les cheminement du graphe de depart donné - - :param graphe GO: graphe à parcourir - :param depart data: sommet de départ dans le graphe - - :return resultat dict: dictionnaire - - clef : sommet atteint - - valeur : tuple (longueur itinéraire, sommet prédécesseur) - - :effet de bord: Aucun - """ - resultat = dict() - file = [depart] - resultat[depart] = (0,None) # Ajout du départ qui n'a aucun predecesseur - while len(file) == 0: - sommet = file.pop(0) - for voisin in graphe.voisins[sommet]: - if voisin not in resultat: - distance = resultat[sommet][0] + 1 # resultat[sommet][0] donne la distance a partir du sommet donner et +1 pour l sommet d'après - resultat[voisin] = (distance,sommet) - file.append(voisin) - - return resultat - - - -def reconstruire_chemin_vers(dico_chemins, *arrivee): - """ - Remonte tout l'itinéraire depuis un sommet fixé - vers un ou des sommets du graphe - - :param dico_chemins dict: table des longueurs/prédécessurs - :param *arrivee: nombre quelconque de sommet à atteindre - (si vide, on considère tous les sommets) - :return resultat list: liste des chemins ie des listes de sommets traversés - """ - chemins = list() - cibles = [arrivee] - if not cibles: - return dico_chemins.keys() - for sommet in cibles: - sommet - #todo - return [] - -def affichage_chemin(chemin): - """ - Produit le texte d'affichage d'un chemin - - :param chemin list: liste des sommets constituant le chemin - :return string: chemin mis en forme pour affichage - """ - long = len(chemin)-1 - return '{} etapes :\n'.format(long)+'\n\t-> '.join([str(sommet) for sommet in chemin]) - -# DEFINITION DES OPERATIONS DE TRANSITION - -def vider(numero, etat): - """ - Vide un bidon - - :param numero INT: index du bidon - :param etat tuple: etat des bidons avant l'opération - :return tuple: nouvel etat aprés opération - :effet de bord: aucun - """ - index_b = list(etat) - index_b[numero] = 0 - return tuple(index_b) -def remplir(numero, etat, capacites): - """ - Remplit un bidon - - :param numero INT: index du bidon - :param etat tuple: etat des bidons avant l'opération - :param capacites tuple: capacités des bidons - :return tuple: nouvel etat aprés opération - :effet de bord: aucun - """ - cap = list(capacites) - index_b = list(etat) - index_b[numero] = cap[numero] - return tuple(index_b) - - -def transvaser(source, destination, etat, capacites): - """ - Transvase un bidon dans un autre - - :param origine int: index du bidon source - :param destination int: index du bidon destination - :param etas tuple: etat des bidons avant l'opération - :param capacites tuple: capacités des bidons - :return tuple: nouvel etat aprés opération - :effet de bord: aucun - """ - transfer_amount = min(list(etat)[source], list(capacites)[destination] - list(etat)[destination]) - new_state = list(etat) - - new_state[source] -= transfer_amount - new_state[destination] += transfer_amount - return tuple(new_state) -# FONCTION LIEES AU GRAPHE DU WATER_JUG - -# Pour construire les etats -def produit_cartesien(*listes): - """ - Concatène les tuples issus des différentes listes - - :param *listes: un nombre quelconque de listes de tuples - :return list: une liste des tuples concaténés - - Exemple - -------- - >>> produit_cartesien([(1,2), (3, 4)], [(5, 6), (7, 8,)]) - [(1, 2, 5, 6), (1, 2, 7, 8), (3, 4, 5, 6), (3, 4, 7, 8)] - """ - if listes == None: - return [] - if len(listes) == 1: - return listes[0] - result = [] - for elt in listes[0]: - for tuples in produit_cartesien(*listes[1:]): - result.append(elt + tuples) - return result - -def creer_water_jug(*capacites): - """ - Création du graphe de Water Jug en fonction des capacités des bidons - - :param *capacites: capacités des bidons disponibles - :return resultat GO: le graphe orienté du W_J - :pre-condition: au moins 2 bidons - - :effet de bord: Aucun - """ - nb_bidons = len(capacites) - print(len(capacites)) - assert nb_bidons >= 2, "Pas assez de bidons" - resultat = Graphe_Oriente() - # CREATION DES SOMMETS - etats_marginaux = [[(contenu,) for contenu in range(1+capacite)] for capacite in capacites] - etats_systeme = produit_cartesien(*etats_marginaux) - for etat in etats_systeme: - resultat.ajoute_sommet(etat) - # CREATION DES TRANSITIONS - for sommet in resultat.sommets: - voisins = list() - # VIDER - for bidon in range(nb_bidons): - voisin = vider(bidon, sommet) - if voisin!=sommet and not(voisin in voisins): - voisins.append(voisin) - # REMPLIR - for bidon in range(nb_bidons): - voisin = remplir(bidon, sommet, capacites) - if voisin != sommet and not(voisin in voisins): - voisins.append(voisin) - # TRANSVASER - for origine in range(nb_bidons): - for destination in range(nb_bidons): - if origine != destination: - voisin = transvaser(origine, destination, sommet, capacites) - if voisin != sommet and not(voisin in voisins): - voisins.append(voisin) - # CREATION LISTES ADJACENCE - for voisin in voisins: - resultat.ajoute_arc(sommet, voisin) - return resultat - -def atteindre(quantite, graphe_water_jug, depart = None, plus_court=False): - """ - Résolution du problème pour une quantite donnée - - :param quantite int: la quantité à mesurer - :param graphe_water_jug GO: le graphe de la situation - :param depart tuple: etat initial, sommet d'origine dans le graphe - (bidons tous vides si depart est None, ie non fourni) - :param plus_court bool: si vrai, seul le(s) plus court(s) chemin(s) - est (sont) retenu(s) - - :return list: la liste des tuples (sommet arrivee, nb d'etapes, ititneraire) ok - """ - if depart is None: - nb_bidons = len(graphe_water_jug.sommets[0]) - depart = tuple([0 for i in range(nb_bidons)]) - chemins = construire_chemins(graphe_water_jug, depart) - resultat = list() - for sommet in chemins: - if quantite in sommet: - longueur, predecesseur = chemins[sommet] - chemin = reconstruire_chemin_vers(chemins, sommet).pop() - index = len(resultat) - resultat.append((sommet, longueur, chemin)) - while index > 0 : - if resultat[index-1][1]