diff --git a/graphes/leaudibidon/.gitignore b/graphes/leaudibidon/.gitignore new file mode 100644 index 0000000..f104652 --- /dev/null +++ b/graphes/leaudibidon/.gitignore @@ -0,0 +1 @@ +*.py diff --git a/graphes/leaudibidon/Water_Jug.py b/graphes/leaudibidon/Water_Jug.py deleted file mode 100644 index 68bda87..0000000 --- a/graphes/leaudibidon/Water_Jug.py +++ /dev/null @@ -1,277 +0,0 @@ -#!/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) # 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] 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]