Module mvc.controllers.controller

Le contrôleur pour les différents algorithmes.

On gère ici les interactions entre le modèle et la vue.

Expand source code
#!/usr/bin/python3
# -*- coding: utf8 -*-

# @author : Sébastien LOZANO
"""Le contrôleur pour les différents algorithmes.

On gère ici les interactions entre le modèle et la vue.
"""

# Pour l'aléatoire
import random

# Pour les copies
import copy

# Pour la gestion des imports relatifs
import sys
import os

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(SCRIPT_DIR))

# Imports des classes utiles
from models.noeud import Noeud  # noqa: E402
from models.arc import Arc  # noqa: E402
from models.graphe import Graphe  # noqa: E402
from views.view import Vue  # noqa: E402


class Controleur:
    """Le Controleur pour les différents algorithmes.

    Algorithme1 : Une méthode heuristique de réduction de la dette
    --------------------------------------------------------------
    Calcule les états successifs du graphe par application de la méthode
    heuristique de réduction de la dette.

    Algorithme2 : Modèle de réduction partielle par élimination des cycles
    ----------------------------------------------------------------------
    Calcule les états successifs du graphe par application de la méthode
    de réduction partielle par élimination des cycles.

    Algorithme3 : Algorithme de Klein
    ---------------------------------
    Calcule les états successifs du graphe par application de l'algorithme
    de Klein

    Attributs
    ---------
    graphe : Graphe - Une instance de la classe Graphe
    vue    : Vue    - Une instance de la classe Vue
    """

    def __init__(self, graphe: Graphe, vue: Vue) -> None:
        """Constructeur

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> print(controller.getGraphe())
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
        """  # noqa: E501
        pass

        self.graphe = graphe
        self.vue = vue

    def __str__(self) -> None:
        """Représentation en chaine de caractères d'une instance de la classe Controleur

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> g.removeArc(AB1)
        >>> g2 = copy.deepcopy(g)
        >>> g.removeArc(BC)
        >>> g3 = copy.deepcopy(g)
        >>> vue = Vue(g,[A,5,g2,g3],"heuristique")
        >>> controller = Controleur(g,vue)
        >>> print(controller)
        Graphe du controleur
        --------------------
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        ===================================
        Vue du controleur
        -----------------
        Graphe de la vue          : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        Entreprise choisie        : Noeud(nom=A, voisins=['B', 'C'], nbArcs=4, capital=0)
        Don de la banque des dons : 5
        Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2', 'BC'])
        Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        >>> vue2 = Vue(g,[5,g2,g3],"eliminationCycles")
        >>> controller2 = Controleur(g,vue2)
        >>> print(controller2)
        Graphe du controleur
        --------------------
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        ===================================
        Vue du controleur
        -----------------
        Graphe de la vue            : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        Baisse de la dette globale  : 5
        Graphe étape                : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2', 'BC'])
        Graphe étape                : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        """  # noqa: E501
        pass
        graphe = "Graphe du controleur\n--------------------\n{}\n".format(
            self.getGraphe())
        vue = "Vue du controleur\n-----------------\n{}".format(
            self.getVue())

        return graphe + '===================================\n' + vue

# ================================================================
# =========== GETTERS ============================================
# ================================================================
    def getGraphe(self) -> Graphe:
        """Retourne l'instance du graphe du Controleur.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> print(controller.getGraphe())
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
        """  # noqa : E501
        pass
        return self.graphe

    def getVue(self) -> Vue:
        """Retourne l'instance de la vue du Controleur.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> g.removeArc(AB1)
        >>> g2 = copy.deepcopy(g)
        >>> g.removeArc(BC)
        >>> g3 = copy.deepcopy(g)        
        >>> vue = Vue(g,[A,5,g2,g3],"heuristique")
        >>> controller = Controleur(g,vue)
        >>> print(controller.getVue())
        Graphe de la vue          : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        Entreprise choisie        : Noeud(nom=A, voisins=['B', 'C'], nbArcs=4, capital=0)
        Don de la banque des dons : 5
        Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2', 'BC'])
        Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        """   # noqa : E501
        pass
        return self.vue

# ================================================================
# =========== OUTILS =============================================
# ================================================================

# =========== ALGORITHME 1 : METHODE HEURISTIQUE =================

    def choixPremiereEntreprise(self) -> Noeud:
        """Retourne un Noeud au hasard parmi les Noeuds du graphe.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> # On initialise une graine pour le pseudo-aléatoire afin de pouvoir prévoir l'aléatoire !
        >>> # Ça serait top de pouvoir faire ça pour le loto !
        >>> random.seed(7)
        >>> controller.choixPremiereEntreprise().getNom()
        'B'
        """  # noqa : E501
        pass
        assert self.getGraphe().hasNoeud(), "Le graphe doit avoir des Noeuds."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.getGraphe().hasNoeud():
            raise ValueError("Le graphe doit avoir des Noeuds.")
        else:
            # L'entreprise choisie doit avoir des créanciers !
            entrepriseChoisie = random.choice(self.graphe.getNoeuds())
            while self.getGraphe().getCreanciers(entrepriseChoisie) == []:
                entrepriseChoisie = random.choice(self.graphe.getNoeuds())
        return entrepriseChoisie

    def arcDetteMaxEntreprise(self, noeudCandidat: Noeud) -> Arc:
        """Retourne l'arc correspondant à la plus grosse dette d'une instance de la classe Noeud.
        En cas d'égalité, un des arcs trouvés est choisi au hasard.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud.
        
        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> print(controller.arcDetteMaxEntreprise(A))
        Arc(nom=AB1, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False)
        >>> print(controller.arcDetteMaxEntreprise(B))
        Arc(nom=BC, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False)
        >>> print(controller.arcDetteMaxEntreprise(C))
        Arc(nom=CA1, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False)
        >>> # On ajoute un arc de poids égal à la dette Max actuellle du noeud A
        >>> AB3 = Arc('AB3',A,B,5)
        >>> g.addArc(AB3)
        >>> # On fiche la graine pour pouvoir prévoir le résultat !
        >>> random.seed(7)        
        >>> print(controller.arcDetteMaxEntreprise(A))
        Arc(nom=AB3, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=5, estInverse=False)
        """  # noqa : E501
        pass
        assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
        assert len(self.getGraphe().getNoeuds()) >= 2, "Le graphe doit avoir au moins deux noeuds."  # noqa : E501
        assert len(self.getGraphe().getArcs()) >=1 , "Le graphe doit avoir au moins un arc."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        allOk = True
        if len(self.getGraphe().getNoeuds()) < 2:
            allOk = False
            raise ValueError("Le graphe doit avoir au moins deux noeuds.")
        if len(self.getGraphe().getArcs()) < 1:
            allOk = False
            raise ValueError("Le graphe doit avoir au moins un arc.")
        if allOk:
            creanciers = self.getGraphe().getCreanciers(noeudCandidat)
            arcsNoeud = self.getGraphe().getArcs()
            detteMax = 0
            arcsDetteMax = []
            for arc in arcsNoeud:
                if (arc.getNoeudDebut() == noeudCandidat and
                        arc.getNoeudFin() in creanciers):
                    if arc.poids == detteMax:
                        arcsDetteMax.append(arc)
                    elif arc.poids > detteMax:
                        detteMax = arc.getPoids()
                        arcsDetteMax = []
                        arcsDetteMax.append(arc)
            if arcsDetteMax:
                arcDetteMax = random.choice(arcsDetteMax)
            else:
                arcDetteMax = None
        return arcDetteMax

    def detteMaxEntreprise(self, noeudCandidat: Noeud) -> int:
        """Retourne la plus grosse dette d'une instance de la classe Noeud.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> controller.detteMaxEntreprise(A)
        5
        >>> controller.detteMaxEntreprise(B)
        5
        >>> controller.detteMaxEntreprise(C)
        8
        """
        pass
        assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
        assert len(self.getGraphe().getNoeuds()) >=2 , "Le graphe doit avoir au moins deux noeuds."  # noqa : E501
        assert len(self.getGraphe().getArcs()) >=1 , "Le graphe doit avoir au moins un arc."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        allOk = True
        if len(self.getGraphe().getNoeuds()) < 2:
            allOk = False
            raise ValueError("Le graphe doit avoir au moins deux noeuds.")
        if len(self.getGraphe().getArcs()) < 1:
            allOk = False
            raise ValueError("Le graphe doit avoir au moins un arc.")
        if allOk and self.arcDetteMaxEntreprise(noeudCandidat) is not None:
            return self.arcDetteMaxEntreprise(noeudCandidat).getPoids()

    def reglerDettesEntreprise(self, noeudCandidat: Noeud, don: int) -> None:
        """Modifie le graphe de l'instance en remboursant les dettes du noeudCandidat à l'aide du don.
        On accepte ici les réductions partielles. C'est à dire que si A reçoit 6€ et doit 5€ à B et 4€ à C, 
        A annule sa facture envers B et diminue sa facture envers C.

        Paramètres
        ----------
        noeudCandidat : Noeud  - Une instance de Noeud
        don           : int    - Une somme reçue pour rembourser les creanciers de noeudCandidat

        Remarque
        --------
        Fonction recursive, permettant de faire ruisseler le processus de remboursement pour le noeudCandidat.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> print(f"Graphe initial : {controller.graphe}")
        Graphe initial : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
        >>> controller.reglerDettesEntreprise(A,4)
        >>> # Aucun arc n'est supprimé
        >>> print(f"Graphe si le don est inférieur à la dette max : {controller.graphe}")        
        Graphe si le don est inférieur à la dette max : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
        >>> # Mais le poids de l'arc correspondant à la detteMax est réduit du don
        >>> print(AB1)
        Arc(nom=AB1, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4), poids=1, estInverse=False)
        >>> print(A);print(B);print(C)
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0)
        >>> # À présent la detteMax restante est sur l'arc AC est vaut 3
        >>> controller.reglerDettesEntreprise(A,3)
        >>> print(f"Graphe si le don est égale à la dette max : {controller.graphe}")
        Graphe si le don est égale à la dette max : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'CA1', 'CA2', 'BC'])
        >>> print(A);print(B);print(C)
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=4, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=3)
        >>> # Il reste deux dettes au noeud A si on lui donne suffisamment pour tout rembourser, elle ne doit plus rien
        >>> controller.reglerDettesEntreprise(A,3)
        >>> print(f"Graphe si le don permet de tout rembourser : {controller.graphe}")
        Graphe si le don permet de tout rembourser : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['CA1', 'CA2', 'BC'])
        >>> print(A);print(B);print(C)
        Noeud(nom=A, voisins=['C'], nbArcs=2, capital=0)
        Noeud(nom=B, voisins=['C'], nbArcs=1, capital=7)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=3)
        """  # noqa : E501
        pass
        assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
        assert isinstance(don, int), "don doit être un entier."  # noqa : E501

        detteMax = self.detteMaxEntreprise(noeudCandidat)
        # On initialise le capital à don
        noeudCandidat.setCapital(don)
        if detteMax is not None:
            arcDetteMax = self.arcDetteMaxEntreprise(noeudCandidat)
            noeudFin = arcDetteMax.getNoeudFin()
            if don == detteMax:
                # On met à jour le capital de chaque entreprise
                noeudCandidat.setCapital(noeudCandidat.getCapital() - don)
                noeudFin.setCapital(noeudFin.getCapital() + don)
                # On supprime l'arc
                self.graphe.removeArc(arcDetteMax)
                # On met à jour le don
                don = don - detteMax
            else:
                if don < detteMax:
                    # On met à jour le capital de chaque entreprise
                    noeudCandidat.setCapital(noeudCandidat.getCapital() - don)
                    noeudFin.setCapital(noeudFin.getCapital() + don)
                    # On met à jour l'arc
                    arcDetteMax.setPoids(detteMax-don)
                    # On met à jour le don
                    don = 0
                if don > detteMax:
                    # On met à jour le capital de chaque entreprise
                    noeudCandidat.setCapital(
                        noeudCandidat.getCapital() - detteMax
                    )
                    noeudFin.setCapital(noeudFin.getCapital() + detteMax)
                    # On supprime l'arc
                    self.graphe.removeArc(arcDetteMax)
                    # On appelle recursivement avec les paramètres mis à jour
                    self.reglerDettesEntreprise(noeudCandidat, don - detteMax)

    def genererDonBanque(self, noeudCandidat: Noeud) -> int:
        """Renvoie un don correspondant à la plus forte dette du noeudCandidat.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,7)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> controller.detteMaxEntreprise(A)
        5
        >>> controller.detteMaxEntreprise(B)
        7
        >>> controller.detteMaxEntreprise(C)
        8
        """
        pass
        assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501

        return self.detteMaxEntreprise(noeudCandidat)

    def entrepriseDePlusFortCapital(self) -> Noeud:
        """Renvoie le Noeud de plus fort capital du graphe parmi les noeuds
        ayant des dettes. En cas d'égalité on tire un Noeud au hasard.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> random.seed(7)
        >>> print(controller.entrepriseDePlusFortCapital())
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
        >>> A.setCapital(7)
        >>> print(controller.entrepriseDePlusFortCapital())
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=7)
        >>> C.setCapital(9)
        >>> print(controller.entrepriseDePlusFortCapital())
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=9)
        """
        pass
        assert self.getGraphe().hasNoeud(), "Pour avoir une entreprise de plus fort capital, le graphe doit avoir au moins un Noeud."  # noqa : E501

        noeudsDePlusFortCapital = []
        noeudsDuGraphe = self.getGraphe().getEntreprises()
        arcsDuGraphe = self.getGraphe().getArcs()
        # On ne garde que les noeuds ayant des dettes
        noeudsAyantDesDettes = []
        for arc in arcsDuGraphe:
            for noeud in noeudsDuGraphe:
                if noeud == arc.getNoeudDebut():
                    noeudsAyantDesDettes.append(noeud)
        capitalMax = noeudsAyantDesDettes[0].getCapital()
        noeudsDePlusFortCapital.append(noeudsAyantDesDettes[0])
        for i in range(1, len(noeudsAyantDesDettes)):
            if noeudsAyantDesDettes[i].getCapital() == capitalMax:
                noeudsDePlusFortCapital.append(noeudsAyantDesDettes[i])
            elif noeudsAyantDesDettes[i].getCapital() > capitalMax:
                noeudsDePlusFortCapital = []
                noeudsDePlusFortCapital.append(noeudsAyantDesDettes[i])
                # il faut aussi modifier capitalMax !
                capitalMax = noeudsAyantDesDettes[i].getCapital()
        if noeudsDePlusFortCapital:
            noeudDePlusFortCapital = random.choice(noeudsDePlusFortCapital)

        return noeudDePlusFortCapital

    def lancerMethodeHeuristique(self) -> None:
        """Lance la méthode heuristique de réduction de la dette sur le graphe de l'instance de la classe Controleur.
        
        Puis renvoie les données à la vue sous forme d'une liste de graphes correspondants aux états successifs pendant le
        ruissellement de la réduction de la dette.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> for n in g.getNoeuds():
        ...     print(g.positionNette(n))
        ...
        -1
        2
        -1
        >>> controller.lancerMethodeHeuristique()
        >>> for n in g.getNoeuds():
        ...     print(g.positionNette(n,vue.etapes[0].getNom(),vue.etapes[1]))
        ...
        -1
        2
        -1
        """  # noqa : E501
        pass
        # On factorise une variable pour PEP8
        etapes = self.getVue().getEtapes()
        # On choisit une entreprise au hasard
        premiereEntreprise = self.choixPremiereEntreprise()
        # On génère un don correspondant à sa plus grosse dette
        donPremiereEntreprise = self.genererDonBanque(premiereEntreprise)
        # On recupere la premiere entreprise sélectionnée
        # On fait une copie profonde pour l'affichage de l'état initial
        # On la passe à la vue
        etapes.append(copy.deepcopy(premiereEntreprise))
        # On recupère le don pour le passer à la vue
        etapes.append(donPremiereEntreprise)
        # On initialise le tableau contenant les graphes des états successifs
        # avec le graphe initial - propriété de l'instance de Vue self.vue
        grapheInitial = copy.deepcopy(self.getGraphe())
        # On ajoute le graphe au tableau qui sera passé à la vue
        etapes.append(grapheInitial)
        # On ajoutera la baisse de la dette globale en 3e position du tableau
        detteInitiale = grapheInitial.detteGlobale()
        # On lance le remboursement des dettes de cette premiere entreprise
        self.reglerDettesEntreprise(premiereEntreprise, donPremiereEntreprise)
        # On ajoute le nouveau graphe au tableau qui sera passé à la vue
        grapheSuivant = copy.deepcopy(self.getGraphe())
        etapes.append(grapheSuivant)
        # Pour lé méthode heuristique, les index 0, 1 ,2 sont respectivement :
        #   - la premiere entreprise sélectionnée.
        #   - le don à cette entreprise.
        #   - la baisse de la dette globale obtenue.
        index = 3
        while grapheSuivant.detteGlobale() != etapes[index-1].detteGlobale():
            # On choisit l'entreprise de plus fort capital
            entreprisePlusFortCapital = self.entrepriseDePlusFortCapital()
            # On relance le processus
            self.reglerDettesEntreprise(entreprisePlusFortCapital,
                                        entreprisePlusFortCapital.getCapital())
            # On ajoute le nouveau graphe au tableau qui sera passé à la vue
            grapheSuivant = copy.deepcopy(self.getGraphe())
            if not grapheSuivant.hasArc():
                # On ajoute deux fois le nouveau graphe au tableau qui sera
                # passé à la vue
                # On ajoute deux fois le graphe pour conserver le doublon même
                # si on break le while
                etapes.append(grapheSuivant)
                etapes.append(grapheSuivant)
                break
            else:
                etapes.append(grapheSuivant)
                index += 1
        grapheFinal = self.getGraphe()
        if grapheFinal.hasArc():
            etapes.insert(2, detteInitiale - grapheFinal.detteGlobale())
        else:
            etapes.insert(2, detteInitiale)

# =========== ALGORITHME 2 : ÉLIMINATION DES CYCLES ==============

    def lancerReductionParEliminationDesCycles(self) -> None:
        """Lance la réduction de la dette par élimination des cycles du graphe de l'instance
        de la classe Controleur.

        Puis renvoie les données à la vue sous forme d'une liste de graphes correspondants
        aux états successifs pendant le l'élimination des cycles du graphe.

        Renvoie aussi la baisse de la dette globale obtenue.


        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB5,AB2,AC3 = Arc('AB5',A,B,5),Arc('AB2',A,B,2),Arc('AC3',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA8,CA1 = Arc('CA8',C,A,8),Arc('CA1',C,A,1)
        >>> # arc entre B et C
        >>> BC5 = Arc('BC5',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,CA8,CA1,BC5])
        >>> vue = Vue(g,None,"eliminationCycles")
        >>> controller = Controleur(g,vue)                
        >>> random.seed(1)        
        >>> controller.lancerReductionParEliminationDesCycles()
        >>> for etape in vue.etapes:
        ...     print(etape)
        ...
        21
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB5', 'AB2', 'AC3', 'CA8', 'CA1', 'BC5'])
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'A-3->C', 'C-4->A'])
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'C-1->A'])
        """  # noqa : E501
        pass
        # On factorise une variable pour PEP8
        etapes = self.getVue().getEtapes()
        # On initialise le tableau contenant les graphes des états successifs
        # avec le graphe initial - propriété de l'instance de Vue self.vue
        # On ajoutera la baisse de la dette globale en debut du tableau
        detteInitiale = self.getGraphe().detteGlobale()
        # On ajoute le graphe initial au tableau qui sera passé à la vue
        etapes.append(copy.deepcopy(self.getGraphe()))
        # On simplifie le graphe
        self.getGraphe().simplification()
        # On ajoute le graphe simplifié aux étapes
        etapes.append(copy.deepcopy(self.getGraphe()))
        # Tant qu'il y a au moins un cycle dans le graphe
        # On réitère le processus
        while self.getGraphe().hasCycle():
            # On choisit un noeud initial qui présente un cycle
            # au hasard parmi les noeuds présentant un cycle
            noeudsPresentantUnCycle = []
            for noeud in self.getGraphe().getNoeuds():
                if (self.getGraphe().hasCycleFromNoeud(noeud) and
                        noeud not in noeudsPresentantUnCycle):
                    noeudsPresentantUnCycle.append(noeud)
            noeudAleatoire = random.choice(noeudsPresentantUnCycle)
            # On lance l'élimination d'un cycle de ce noeud
            self.getGraphe().eliminerCycleFromNoeud(noeudAleatoire)
            # On ajoute le graphe obtenu après élimination du cycle
            etapes.append(copy.deepcopy(self.getGraphe()))
        grapheFinal = self.getGraphe()
        # On ajoute la baisse globale obtenue
        etapes.insert(0, detteInitiale - grapheFinal.detteGlobale())

# =========== ALGORITHME 3 : ÉALGORITHME DE KLEIN ================

    def lancerAlgorithmeDeKlein(self) -> None:
        """Lance la réduction de la dette en appliquant l'algorithme de Klein
        sur le graphe de l'instance de la classe Controleur.

        Puis renvoie les données à la vue sous forme d'une liste de graphes
        correspondants aux états successifs pendant la modification du flot.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> # arc entre B et C
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,CA8,CA1,BC5])
        >>> vue = Vue(g,None,"klein")
        >>> controller = Controleur(g,vue)
        >>> g.simplification()
        >>> random.seed(4)
        >>> controller.lancerAlgorithmeDeKlein()
        >>> for etape in vue.etapes:
        ...     print(etape)
        ...
        21
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A', 'C-0->A', 'C-0->B', 'A-0->C'])
        Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'A-3->C', 'B-0->C', 'C-4->A', 'B-5->A', 'C-0->A', 'C-5->B', 'A-5->C'])
        Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'A-0->C', 'B-0->C', 'C-1->A', 'B-5->A', 'C-3->A', 'C-5->B', 'A-8->C'])
        Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'C-1->A'])
        """ # noqa : E501
        pass

        # On factorise une variable pour PEP8
        etapes = self.getVue().getEtapes()
        graphe = self.getGraphe()
        # On initialise le tableau contenant les graphes des états successifs
        # avec le graphe initial - propriété de l'instance de Vue self.vue
        # On ajoutera la baisse de la dette globale en debut du tableau
        detteInitiale = graphe.detteGlobale()
        # On ajoute le graphe au tableau qui sera passé à la vue
        etapes.append(copy.deepcopy(graphe))
        # On simplifie le graphe s'il ne l'est pas
        if not graphe.estSimplifie():
            graphe.simplification()
            # On ajoute le graphe simplifié aux étapes
            etapes.append(copy.deepcopy(graphe))
        # On ajoute les arcs inverses de poids nul
        graphe.addArcsInversesNuls()
        # On ajoute le graphe avec les arcs inverses nuls aux étapes
        etapes.append(copy.deepcopy(graphe))
        # Pour tous les noeuds du graphe
        # On élimine tous les cycles partant de ce noeud
        # IL FAUDRAIT POUVOIR CHOISIR L'ORDRE DE TRAITEMENT DES NOEUDS
        # OU ALORS LES TRAITER AU HASARD POUR VOIR LA RÉDUCTION DE LA DETTE

        # On mélange la liste des noeuds du graphe afin de ne pas toujours
        # avoir le même ordre de traitement
        noeudsDuGrapheMelanges = graphe.getNoeuds()
        random.shuffle(noeudsDuGrapheMelanges)
        for noeud in noeudsDuGrapheMelanges:
            while graphe.kleinAllArcsCyclesFromNoeud(noeud) != []:
                # On lance l'élimination d'un cycle de ce noeud
                graphe.kleinModifierCycleFromNoeud(noeud)
                # On ajoute le graphe obtenu après modification du flot
                if not graphe.hasArc():
                    etapes.append(copy.deepcopy(graphe))
                    break
                else:
                    etapes.append(copy.deepcopy(graphe))

        # On supprime les arcs nuls
        graphe.kleinDeleteDettesNulles()
        # On supprime les arcs inverses
        graphe.kleinDeleteArcsInverses()
        # On ajoute le graphe final
        etapes.append(copy.deepcopy(graphe))
        grapheFinal = graphe
        # On ajoute la baisse obtenue
        etapes.insert(0, detteInitiale - grapheFinal.detteGlobale())

# ================================================================
# =========== METHODES NON UTILISÉES =============================
# ================================================================

    def sameCycles(self, cycle1: list, cycle2: list) -> bool:
        """Renvoie un booléen selon que cycle1 et cycle2 sont constitués
        des mêmes arcs avec les mêmes poids ou non

        Paramètres
        ----------
        cycle1 : list - Un cycle d'arcs du graphe
        cycle2 : list - Un autre cycle d'arcs du graphe
s
        Remarques
        ---------
        - Deux listes peuvent avoir les mêmes instances de la classe Arc
        mais avec des poids différents, il ne suffit donc pas de comparer
        les instances constituant les listes d'arcs.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB7 = Arc('A-7->B',A,B,7)
        >>> # arcs arrivant sur A
        >>> CA9 = Arc('C-9->A',C,A,9)
        >>> # arc entre B et C
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB7,CA9,BC5])
        >>> vue = Vue(g,None,"klein")
        >>> controller = Controleur(g,vue)
        """
        pass
        assert isinstance(cycle1, list), "cycle1 doit être une liste"
        assert isinstance(cycle2, list), "cycle2 doit être une liste"
        for element in cycle1:
            assert isinstance(element, Arc), " cycle1 doit être une liste d'arcs" # noqa : E501
        for element in cycle2:
            assert isinstance(element, Arc), " cycle2 doit être une liste d'arcs" # noqa : E501

        sameCycles = True
        # Pour être identiques, les cycles ont la même taille
        if (len(cycle1) != len(cycle2)):
            sameCycles = False
        else:
            # Les arcs peuvent être à des positions distinctes
            # dans le tableau des arcs !!!!
            # Il faudrait peut être gérer ça ...
            taille = len(cycle1)
            for i in range(taille):
                if cycle1[i].getPoids() != cycle2[i].getPoids():
                    sameCycles = False

        return sameCycles


if __name__ == "__main__":
    import doctest
    doctest.testmod()

Classes

class Controleur (graphe: models.graphe.Graphe, vue: views.view.Vue)

Le Controleur pour les différents algorithmes.

Algorithme1 : Une méthode heuristique de réduction de la dette

Calcule les états successifs du graphe par application de la méthode heuristique de réduction de la dette.

Algorithme2 : Modèle de réduction partielle par élimination des cycles

Calcule les états successifs du graphe par application de la méthode de réduction partielle par élimination des cycles.

Algorithme3 : Algorithme de Klein

Calcule les états successifs du graphe par application de l'algorithme de Klein

Attributs

graphe : Graphe - Une instance de la classe Graphe vue : Vue - Une instance de la classe Vue

Constructeur

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> vue = Vue(g)
>>> controller = Controleur(g,vue)
>>> print(controller.getGraphe())
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
Expand source code
class Controleur:
    """Le Controleur pour les différents algorithmes.

    Algorithme1 : Une méthode heuristique de réduction de la dette
    --------------------------------------------------------------
    Calcule les états successifs du graphe par application de la méthode
    heuristique de réduction de la dette.

    Algorithme2 : Modèle de réduction partielle par élimination des cycles
    ----------------------------------------------------------------------
    Calcule les états successifs du graphe par application de la méthode
    de réduction partielle par élimination des cycles.

    Algorithme3 : Algorithme de Klein
    ---------------------------------
    Calcule les états successifs du graphe par application de l'algorithme
    de Klein

    Attributs
    ---------
    graphe : Graphe - Une instance de la classe Graphe
    vue    : Vue    - Une instance de la classe Vue
    """

    def __init__(self, graphe: Graphe, vue: Vue) -> None:
        """Constructeur

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> print(controller.getGraphe())
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
        """  # noqa: E501
        pass

        self.graphe = graphe
        self.vue = vue

    def __str__(self) -> None:
        """Représentation en chaine de caractères d'une instance de la classe Controleur

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> g.removeArc(AB1)
        >>> g2 = copy.deepcopy(g)
        >>> g.removeArc(BC)
        >>> g3 = copy.deepcopy(g)
        >>> vue = Vue(g,[A,5,g2,g3],"heuristique")
        >>> controller = Controleur(g,vue)
        >>> print(controller)
        Graphe du controleur
        --------------------
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        ===================================
        Vue du controleur
        -----------------
        Graphe de la vue          : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        Entreprise choisie        : Noeud(nom=A, voisins=['B', 'C'], nbArcs=4, capital=0)
        Don de la banque des dons : 5
        Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2', 'BC'])
        Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        >>> vue2 = Vue(g,[5,g2,g3],"eliminationCycles")
        >>> controller2 = Controleur(g,vue2)
        >>> print(controller2)
        Graphe du controleur
        --------------------
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        ===================================
        Vue du controleur
        -----------------
        Graphe de la vue            : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        Baisse de la dette globale  : 5
        Graphe étape                : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2', 'BC'])
        Graphe étape                : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        """  # noqa: E501
        pass
        graphe = "Graphe du controleur\n--------------------\n{}\n".format(
            self.getGraphe())
        vue = "Vue du controleur\n-----------------\n{}".format(
            self.getVue())

        return graphe + '===================================\n' + vue

# ================================================================
# =========== GETTERS ============================================
# ================================================================
    def getGraphe(self) -> Graphe:
        """Retourne l'instance du graphe du Controleur.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> print(controller.getGraphe())
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
        """  # noqa : E501
        pass
        return self.graphe

    def getVue(self) -> Vue:
        """Retourne l'instance de la vue du Controleur.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> g.removeArc(AB1)
        >>> g2 = copy.deepcopy(g)
        >>> g.removeArc(BC)
        >>> g3 = copy.deepcopy(g)        
        >>> vue = Vue(g,[A,5,g2,g3],"heuristique")
        >>> controller = Controleur(g,vue)
        >>> print(controller.getVue())
        Graphe de la vue          : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        Entreprise choisie        : Noeud(nom=A, voisins=['B', 'C'], nbArcs=4, capital=0)
        Don de la banque des dons : 5
        Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2', 'BC'])
        Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
        """   # noqa : E501
        pass
        return self.vue

# ================================================================
# =========== OUTILS =============================================
# ================================================================

# =========== ALGORITHME 1 : METHODE HEURISTIQUE =================

    def choixPremiereEntreprise(self) -> Noeud:
        """Retourne un Noeud au hasard parmi les Noeuds du graphe.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> # On initialise une graine pour le pseudo-aléatoire afin de pouvoir prévoir l'aléatoire !
        >>> # Ça serait top de pouvoir faire ça pour le loto !
        >>> random.seed(7)
        >>> controller.choixPremiereEntreprise().getNom()
        'B'
        """  # noqa : E501
        pass
        assert self.getGraphe().hasNoeud(), "Le graphe doit avoir des Noeuds."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        if not self.getGraphe().hasNoeud():
            raise ValueError("Le graphe doit avoir des Noeuds.")
        else:
            # L'entreprise choisie doit avoir des créanciers !
            entrepriseChoisie = random.choice(self.graphe.getNoeuds())
            while self.getGraphe().getCreanciers(entrepriseChoisie) == []:
                entrepriseChoisie = random.choice(self.graphe.getNoeuds())
        return entrepriseChoisie

    def arcDetteMaxEntreprise(self, noeudCandidat: Noeud) -> Arc:
        """Retourne l'arc correspondant à la plus grosse dette d'une instance de la classe Noeud.
        En cas d'égalité, un des arcs trouvés est choisi au hasard.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud.
        
        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> print(controller.arcDetteMaxEntreprise(A))
        Arc(nom=AB1, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False)
        >>> print(controller.arcDetteMaxEntreprise(B))
        Arc(nom=BC, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False)
        >>> print(controller.arcDetteMaxEntreprise(C))
        Arc(nom=CA1, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False)
        >>> # On ajoute un arc de poids égal à la dette Max actuellle du noeud A
        >>> AB3 = Arc('AB3',A,B,5)
        >>> g.addArc(AB3)
        >>> # On fiche la graine pour pouvoir prévoir le résultat !
        >>> random.seed(7)        
        >>> print(controller.arcDetteMaxEntreprise(A))
        Arc(nom=AB3, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=5, estInverse=False)
        """  # noqa : E501
        pass
        assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
        assert len(self.getGraphe().getNoeuds()) >= 2, "Le graphe doit avoir au moins deux noeuds."  # noqa : E501
        assert len(self.getGraphe().getArcs()) >=1 , "Le graphe doit avoir au moins un arc."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        allOk = True
        if len(self.getGraphe().getNoeuds()) < 2:
            allOk = False
            raise ValueError("Le graphe doit avoir au moins deux noeuds.")
        if len(self.getGraphe().getArcs()) < 1:
            allOk = False
            raise ValueError("Le graphe doit avoir au moins un arc.")
        if allOk:
            creanciers = self.getGraphe().getCreanciers(noeudCandidat)
            arcsNoeud = self.getGraphe().getArcs()
            detteMax = 0
            arcsDetteMax = []
            for arc in arcsNoeud:
                if (arc.getNoeudDebut() == noeudCandidat and
                        arc.getNoeudFin() in creanciers):
                    if arc.poids == detteMax:
                        arcsDetteMax.append(arc)
                    elif arc.poids > detteMax:
                        detteMax = arc.getPoids()
                        arcsDetteMax = []
                        arcsDetteMax.append(arc)
            if arcsDetteMax:
                arcDetteMax = random.choice(arcsDetteMax)
            else:
                arcDetteMax = None
        return arcDetteMax

    def detteMaxEntreprise(self, noeudCandidat: Noeud) -> int:
        """Retourne la plus grosse dette d'une instance de la classe Noeud.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> controller.detteMaxEntreprise(A)
        5
        >>> controller.detteMaxEntreprise(B)
        5
        >>> controller.detteMaxEntreprise(C)
        8
        """
        pass
        assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
        assert len(self.getGraphe().getNoeuds()) >=2 , "Le graphe doit avoir au moins deux noeuds."  # noqa : E501
        assert len(self.getGraphe().getArcs()) >=1 , "Le graphe doit avoir au moins un arc."  # noqa : E501

        # Ne pas utiliser les assertions pour valider les données
        # car elles peuvent être désactivées.
        # On lève donc une erreur en cas de souci
        allOk = True
        if len(self.getGraphe().getNoeuds()) < 2:
            allOk = False
            raise ValueError("Le graphe doit avoir au moins deux noeuds.")
        if len(self.getGraphe().getArcs()) < 1:
            allOk = False
            raise ValueError("Le graphe doit avoir au moins un arc.")
        if allOk and self.arcDetteMaxEntreprise(noeudCandidat) is not None:
            return self.arcDetteMaxEntreprise(noeudCandidat).getPoids()

    def reglerDettesEntreprise(self, noeudCandidat: Noeud, don: int) -> None:
        """Modifie le graphe de l'instance en remboursant les dettes du noeudCandidat à l'aide du don.
        On accepte ici les réductions partielles. C'est à dire que si A reçoit 6€ et doit 5€ à B et 4€ à C, 
        A annule sa facture envers B et diminue sa facture envers C.

        Paramètres
        ----------
        noeudCandidat : Noeud  - Une instance de Noeud
        don           : int    - Une somme reçue pour rembourser les creanciers de noeudCandidat

        Remarque
        --------
        Fonction recursive, permettant de faire ruisseler le processus de remboursement pour le noeudCandidat.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> print(f"Graphe initial : {controller.graphe}")
        Graphe initial : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
        >>> controller.reglerDettesEntreprise(A,4)
        >>> # Aucun arc n'est supprimé
        >>> print(f"Graphe si le don est inférieur à la dette max : {controller.graphe}")        
        Graphe si le don est inférieur à la dette max : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
        >>> # Mais le poids de l'arc correspondant à la detteMax est réduit du don
        >>> print(AB1)
        Arc(nom=AB1, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4), poids=1, estInverse=False)
        >>> print(A);print(B);print(C)
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0)
        >>> # À présent la detteMax restante est sur l'arc AC est vaut 3
        >>> controller.reglerDettesEntreprise(A,3)
        >>> print(f"Graphe si le don est égale à la dette max : {controller.graphe}")
        Graphe si le don est égale à la dette max : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'CA1', 'CA2', 'BC'])
        >>> print(A);print(B);print(C)
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=4, capital=0)
        Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=3)
        >>> # Il reste deux dettes au noeud A si on lui donne suffisamment pour tout rembourser, elle ne doit plus rien
        >>> controller.reglerDettesEntreprise(A,3)
        >>> print(f"Graphe si le don permet de tout rembourser : {controller.graphe}")
        Graphe si le don permet de tout rembourser : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['CA1', 'CA2', 'BC'])
        >>> print(A);print(B);print(C)
        Noeud(nom=A, voisins=['C'], nbArcs=2, capital=0)
        Noeud(nom=B, voisins=['C'], nbArcs=1, capital=7)
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=3)
        """  # noqa : E501
        pass
        assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
        assert isinstance(don, int), "don doit être un entier."  # noqa : E501

        detteMax = self.detteMaxEntreprise(noeudCandidat)
        # On initialise le capital à don
        noeudCandidat.setCapital(don)
        if detteMax is not None:
            arcDetteMax = self.arcDetteMaxEntreprise(noeudCandidat)
            noeudFin = arcDetteMax.getNoeudFin()
            if don == detteMax:
                # On met à jour le capital de chaque entreprise
                noeudCandidat.setCapital(noeudCandidat.getCapital() - don)
                noeudFin.setCapital(noeudFin.getCapital() + don)
                # On supprime l'arc
                self.graphe.removeArc(arcDetteMax)
                # On met à jour le don
                don = don - detteMax
            else:
                if don < detteMax:
                    # On met à jour le capital de chaque entreprise
                    noeudCandidat.setCapital(noeudCandidat.getCapital() - don)
                    noeudFin.setCapital(noeudFin.getCapital() + don)
                    # On met à jour l'arc
                    arcDetteMax.setPoids(detteMax-don)
                    # On met à jour le don
                    don = 0
                if don > detteMax:
                    # On met à jour le capital de chaque entreprise
                    noeudCandidat.setCapital(
                        noeudCandidat.getCapital() - detteMax
                    )
                    noeudFin.setCapital(noeudFin.getCapital() + detteMax)
                    # On supprime l'arc
                    self.graphe.removeArc(arcDetteMax)
                    # On appelle recursivement avec les paramètres mis à jour
                    self.reglerDettesEntreprise(noeudCandidat, don - detteMax)

    def genererDonBanque(self, noeudCandidat: Noeud) -> int:
        """Renvoie un don correspondant à la plus forte dette du noeudCandidat.

        Paramètres
        ----------
        noeudCandidat : Noeud - Une instance de la classe Noeud

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,7)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> controller.detteMaxEntreprise(A)
        5
        >>> controller.detteMaxEntreprise(B)
        7
        >>> controller.detteMaxEntreprise(C)
        8
        """
        pass
        assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501

        return self.detteMaxEntreprise(noeudCandidat)

    def entrepriseDePlusFortCapital(self) -> Noeud:
        """Renvoie le Noeud de plus fort capital du graphe parmi les noeuds
        ayant des dettes. En cas d'égalité on tire un Noeud au hasard.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> random.seed(7)
        >>> print(controller.entrepriseDePlusFortCapital())
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
        >>> A.setCapital(7)
        >>> print(controller.entrepriseDePlusFortCapital())
        Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=7)
        >>> C.setCapital(9)
        >>> print(controller.entrepriseDePlusFortCapital())
        Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=9)
        """
        pass
        assert self.getGraphe().hasNoeud(), "Pour avoir une entreprise de plus fort capital, le graphe doit avoir au moins un Noeud."  # noqa : E501

        noeudsDePlusFortCapital = []
        noeudsDuGraphe = self.getGraphe().getEntreprises()
        arcsDuGraphe = self.getGraphe().getArcs()
        # On ne garde que les noeuds ayant des dettes
        noeudsAyantDesDettes = []
        for arc in arcsDuGraphe:
            for noeud in noeudsDuGraphe:
                if noeud == arc.getNoeudDebut():
                    noeudsAyantDesDettes.append(noeud)
        capitalMax = noeudsAyantDesDettes[0].getCapital()
        noeudsDePlusFortCapital.append(noeudsAyantDesDettes[0])
        for i in range(1, len(noeudsAyantDesDettes)):
            if noeudsAyantDesDettes[i].getCapital() == capitalMax:
                noeudsDePlusFortCapital.append(noeudsAyantDesDettes[i])
            elif noeudsAyantDesDettes[i].getCapital() > capitalMax:
                noeudsDePlusFortCapital = []
                noeudsDePlusFortCapital.append(noeudsAyantDesDettes[i])
                # il faut aussi modifier capitalMax !
                capitalMax = noeudsAyantDesDettes[i].getCapital()
        if noeudsDePlusFortCapital:
            noeudDePlusFortCapital = random.choice(noeudsDePlusFortCapital)

        return noeudDePlusFortCapital

    def lancerMethodeHeuristique(self) -> None:
        """Lance la méthode heuristique de réduction de la dette sur le graphe de l'instance de la classe Controleur.
        
        Puis renvoie les données à la vue sous forme d'une liste de graphes correspondants aux états successifs pendant le
        ruissellement de la réduction de la dette.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
        >>> # arc entre B et C
        >>> BC = Arc('BC',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
        >>> vue = Vue(g)
        >>> controller = Controleur(g,vue)
        >>> for n in g.getNoeuds():
        ...     print(g.positionNette(n))
        ...
        -1
        2
        -1
        >>> controller.lancerMethodeHeuristique()
        >>> for n in g.getNoeuds():
        ...     print(g.positionNette(n,vue.etapes[0].getNom(),vue.etapes[1]))
        ...
        -1
        2
        -1
        """  # noqa : E501
        pass
        # On factorise une variable pour PEP8
        etapes = self.getVue().getEtapes()
        # On choisit une entreprise au hasard
        premiereEntreprise = self.choixPremiereEntreprise()
        # On génère un don correspondant à sa plus grosse dette
        donPremiereEntreprise = self.genererDonBanque(premiereEntreprise)
        # On recupere la premiere entreprise sélectionnée
        # On fait une copie profonde pour l'affichage de l'état initial
        # On la passe à la vue
        etapes.append(copy.deepcopy(premiereEntreprise))
        # On recupère le don pour le passer à la vue
        etapes.append(donPremiereEntreprise)
        # On initialise le tableau contenant les graphes des états successifs
        # avec le graphe initial - propriété de l'instance de Vue self.vue
        grapheInitial = copy.deepcopy(self.getGraphe())
        # On ajoute le graphe au tableau qui sera passé à la vue
        etapes.append(grapheInitial)
        # On ajoutera la baisse de la dette globale en 3e position du tableau
        detteInitiale = grapheInitial.detteGlobale()
        # On lance le remboursement des dettes de cette premiere entreprise
        self.reglerDettesEntreprise(premiereEntreprise, donPremiereEntreprise)
        # On ajoute le nouveau graphe au tableau qui sera passé à la vue
        grapheSuivant = copy.deepcopy(self.getGraphe())
        etapes.append(grapheSuivant)
        # Pour lé méthode heuristique, les index 0, 1 ,2 sont respectivement :
        #   - la premiere entreprise sélectionnée.
        #   - le don à cette entreprise.
        #   - la baisse de la dette globale obtenue.
        index = 3
        while grapheSuivant.detteGlobale() != etapes[index-1].detteGlobale():
            # On choisit l'entreprise de plus fort capital
            entreprisePlusFortCapital = self.entrepriseDePlusFortCapital()
            # On relance le processus
            self.reglerDettesEntreprise(entreprisePlusFortCapital,
                                        entreprisePlusFortCapital.getCapital())
            # On ajoute le nouveau graphe au tableau qui sera passé à la vue
            grapheSuivant = copy.deepcopy(self.getGraphe())
            if not grapheSuivant.hasArc():
                # On ajoute deux fois le nouveau graphe au tableau qui sera
                # passé à la vue
                # On ajoute deux fois le graphe pour conserver le doublon même
                # si on break le while
                etapes.append(grapheSuivant)
                etapes.append(grapheSuivant)
                break
            else:
                etapes.append(grapheSuivant)
                index += 1
        grapheFinal = self.getGraphe()
        if grapheFinal.hasArc():
            etapes.insert(2, detteInitiale - grapheFinal.detteGlobale())
        else:
            etapes.insert(2, detteInitiale)

# =========== ALGORITHME 2 : ÉLIMINATION DES CYCLES ==============

    def lancerReductionParEliminationDesCycles(self) -> None:
        """Lance la réduction de la dette par élimination des cycles du graphe de l'instance
        de la classe Controleur.

        Puis renvoie les données à la vue sous forme d'une liste de graphes correspondants
        aux états successifs pendant le l'élimination des cycles du graphe.

        Renvoie aussi la baisse de la dette globale obtenue.


        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB5,AB2,AC3 = Arc('AB5',A,B,5),Arc('AB2',A,B,2),Arc('AC3',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA8,CA1 = Arc('CA8',C,A,8),Arc('CA1',C,A,1)
        >>> # arc entre B et C
        >>> BC5 = Arc('BC5',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,CA8,CA1,BC5])
        >>> vue = Vue(g,None,"eliminationCycles")
        >>> controller = Controleur(g,vue)                
        >>> random.seed(1)        
        >>> controller.lancerReductionParEliminationDesCycles()
        >>> for etape in vue.etapes:
        ...     print(etape)
        ...
        21
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB5', 'AB2', 'AC3', 'CA8', 'CA1', 'BC5'])
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'A-3->C', 'C-4->A'])
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'C-1->A'])
        """  # noqa : E501
        pass
        # On factorise une variable pour PEP8
        etapes = self.getVue().getEtapes()
        # On initialise le tableau contenant les graphes des états successifs
        # avec le graphe initial - propriété de l'instance de Vue self.vue
        # On ajoutera la baisse de la dette globale en debut du tableau
        detteInitiale = self.getGraphe().detteGlobale()
        # On ajoute le graphe initial au tableau qui sera passé à la vue
        etapes.append(copy.deepcopy(self.getGraphe()))
        # On simplifie le graphe
        self.getGraphe().simplification()
        # On ajoute le graphe simplifié aux étapes
        etapes.append(copy.deepcopy(self.getGraphe()))
        # Tant qu'il y a au moins un cycle dans le graphe
        # On réitère le processus
        while self.getGraphe().hasCycle():
            # On choisit un noeud initial qui présente un cycle
            # au hasard parmi les noeuds présentant un cycle
            noeudsPresentantUnCycle = []
            for noeud in self.getGraphe().getNoeuds():
                if (self.getGraphe().hasCycleFromNoeud(noeud) and
                        noeud not in noeudsPresentantUnCycle):
                    noeudsPresentantUnCycle.append(noeud)
            noeudAleatoire = random.choice(noeudsPresentantUnCycle)
            # On lance l'élimination d'un cycle de ce noeud
            self.getGraphe().eliminerCycleFromNoeud(noeudAleatoire)
            # On ajoute le graphe obtenu après élimination du cycle
            etapes.append(copy.deepcopy(self.getGraphe()))
        grapheFinal = self.getGraphe()
        # On ajoute la baisse globale obtenue
        etapes.insert(0, detteInitiale - grapheFinal.detteGlobale())

# =========== ALGORITHME 3 : ÉALGORITHME DE KLEIN ================

    def lancerAlgorithmeDeKlein(self) -> None:
        """Lance la réduction de la dette en appliquant l'algorithme de Klein
        sur le graphe de l'instance de la classe Controleur.

        Puis renvoie les données à la vue sous forme d'une liste de graphes
        correspondants aux états successifs pendant la modification du flot.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
        >>> # arcs arrivant sur A
        >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
        >>> # arc entre B et C
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB5,AB2,AC3,CA8,CA1,BC5])
        >>> vue = Vue(g,None,"klein")
        >>> controller = Controleur(g,vue)
        >>> g.simplification()
        >>> random.seed(4)
        >>> controller.lancerAlgorithmeDeKlein()
        >>> for etape in vue.etapes:
        ...     print(etape)
        ...
        21
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
        Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A', 'C-0->A', 'C-0->B', 'A-0->C'])
        Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'A-3->C', 'B-0->C', 'C-4->A', 'B-5->A', 'C-0->A', 'C-5->B', 'A-5->C'])
        Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'A-0->C', 'B-0->C', 'C-1->A', 'B-5->A', 'C-3->A', 'C-5->B', 'A-8->C'])
        Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'C-1->A'])
        """ # noqa : E501
        pass

        # On factorise une variable pour PEP8
        etapes = self.getVue().getEtapes()
        graphe = self.getGraphe()
        # On initialise le tableau contenant les graphes des états successifs
        # avec le graphe initial - propriété de l'instance de Vue self.vue
        # On ajoutera la baisse de la dette globale en debut du tableau
        detteInitiale = graphe.detteGlobale()
        # On ajoute le graphe au tableau qui sera passé à la vue
        etapes.append(copy.deepcopy(graphe))
        # On simplifie le graphe s'il ne l'est pas
        if not graphe.estSimplifie():
            graphe.simplification()
            # On ajoute le graphe simplifié aux étapes
            etapes.append(copy.deepcopy(graphe))
        # On ajoute les arcs inverses de poids nul
        graphe.addArcsInversesNuls()
        # On ajoute le graphe avec les arcs inverses nuls aux étapes
        etapes.append(copy.deepcopy(graphe))
        # Pour tous les noeuds du graphe
        # On élimine tous les cycles partant de ce noeud
        # IL FAUDRAIT POUVOIR CHOISIR L'ORDRE DE TRAITEMENT DES NOEUDS
        # OU ALORS LES TRAITER AU HASARD POUR VOIR LA RÉDUCTION DE LA DETTE

        # On mélange la liste des noeuds du graphe afin de ne pas toujours
        # avoir le même ordre de traitement
        noeudsDuGrapheMelanges = graphe.getNoeuds()
        random.shuffle(noeudsDuGrapheMelanges)
        for noeud in noeudsDuGrapheMelanges:
            while graphe.kleinAllArcsCyclesFromNoeud(noeud) != []:
                # On lance l'élimination d'un cycle de ce noeud
                graphe.kleinModifierCycleFromNoeud(noeud)
                # On ajoute le graphe obtenu après modification du flot
                if not graphe.hasArc():
                    etapes.append(copy.deepcopy(graphe))
                    break
                else:
                    etapes.append(copy.deepcopy(graphe))

        # On supprime les arcs nuls
        graphe.kleinDeleteDettesNulles()
        # On supprime les arcs inverses
        graphe.kleinDeleteArcsInverses()
        # On ajoute le graphe final
        etapes.append(copy.deepcopy(graphe))
        grapheFinal = graphe
        # On ajoute la baisse obtenue
        etapes.insert(0, detteInitiale - grapheFinal.detteGlobale())

# ================================================================
# =========== METHODES NON UTILISÉES =============================
# ================================================================

    def sameCycles(self, cycle1: list, cycle2: list) -> bool:
        """Renvoie un booléen selon que cycle1 et cycle2 sont constitués
        des mêmes arcs avec les mêmes poids ou non

        Paramètres
        ----------
        cycle1 : list - Un cycle d'arcs du graphe
        cycle2 : list - Un autre cycle d'arcs du graphe
s
        Remarques
        ---------
        - Deux listes peuvent avoir les mêmes instances de la classe Arc
        mais avec des poids différents, il ne suffit donc pas de comparer
        les instances constituant les listes d'arcs.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB7 = Arc('A-7->B',A,B,7)
        >>> # arcs arrivant sur A
        >>> CA9 = Arc('C-9->A',C,A,9)
        >>> # arc entre B et C
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB7,CA9,BC5])
        >>> vue = Vue(g,None,"klein")
        >>> controller = Controleur(g,vue)
        """
        pass
        assert isinstance(cycle1, list), "cycle1 doit être une liste"
        assert isinstance(cycle2, list), "cycle2 doit être une liste"
        for element in cycle1:
            assert isinstance(element, Arc), " cycle1 doit être une liste d'arcs" # noqa : E501
        for element in cycle2:
            assert isinstance(element, Arc), " cycle2 doit être une liste d'arcs" # noqa : E501

        sameCycles = True
        # Pour être identiques, les cycles ont la même taille
        if (len(cycle1) != len(cycle2)):
            sameCycles = False
        else:
            # Les arcs peuvent être à des positions distinctes
            # dans le tableau des arcs !!!!
            # Il faudrait peut être gérer ça ...
            taille = len(cycle1)
            for i in range(taille):
                if cycle1[i].getPoids() != cycle2[i].getPoids():
                    sameCycles = False

        return sameCycles

Methods

def arcDetteMaxEntreprise(self, noeudCandidat: models.noeud.Noeud) ‑> models.arc.Arc

Retourne l'arc correspondant à la plus grosse dette d'une instance de la classe Noeud. En cas d'égalité, un des arcs trouvés est choisi au hasard.

Paramètres

noeudCandidat : Noeud - Une instance de la classe Noeud.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> vue = Vue(g)
>>> controller = Controleur(g,vue)
>>> print(controller.arcDetteMaxEntreprise(A))
Arc(nom=AB1, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False)
>>> print(controller.arcDetteMaxEntreprise(B))
Arc(nom=BC, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False)
>>> print(controller.arcDetteMaxEntreprise(C))
Arc(nom=CA1, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False)
>>> # On ajoute un arc de poids égal à la dette Max actuellle du noeud A
>>> AB3 = Arc('AB3',A,B,5)
>>> g.addArc(AB3)
>>> # On fiche la graine pour pouvoir prévoir le résultat !
>>> random.seed(7)        
>>> print(controller.arcDetteMaxEntreprise(A))
Arc(nom=AB3, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=5, estInverse=False)
Expand source code
def arcDetteMaxEntreprise(self, noeudCandidat: Noeud) -> Arc:
    """Retourne l'arc correspondant à la plus grosse dette d'une instance de la classe Noeud.
    En cas d'égalité, un des arcs trouvés est choisi au hasard.

    Paramètres
    ----------
    noeudCandidat : Noeud - Une instance de la classe Noeud.
    
    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> vue = Vue(g)
    >>> controller = Controleur(g,vue)
    >>> print(controller.arcDetteMaxEntreprise(A))
    Arc(nom=AB1, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False)
    >>> print(controller.arcDetteMaxEntreprise(B))
    Arc(nom=BC, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False)
    >>> print(controller.arcDetteMaxEntreprise(C))
    Arc(nom=CA1, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False)
    >>> # On ajoute un arc de poids égal à la dette Max actuellle du noeud A
    >>> AB3 = Arc('AB3',A,B,5)
    >>> g.addArc(AB3)
    >>> # On fiche la graine pour pouvoir prévoir le résultat !
    >>> random.seed(7)        
    >>> print(controller.arcDetteMaxEntreprise(A))
    Arc(nom=AB3, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=5, estInverse=False)
    """  # noqa : E501
    pass
    assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
    assert len(self.getGraphe().getNoeuds()) >= 2, "Le graphe doit avoir au moins deux noeuds."  # noqa : E501
    assert len(self.getGraphe().getArcs()) >=1 , "Le graphe doit avoir au moins un arc."  # noqa : E501

    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    allOk = True
    if len(self.getGraphe().getNoeuds()) < 2:
        allOk = False
        raise ValueError("Le graphe doit avoir au moins deux noeuds.")
    if len(self.getGraphe().getArcs()) < 1:
        allOk = False
        raise ValueError("Le graphe doit avoir au moins un arc.")
    if allOk:
        creanciers = self.getGraphe().getCreanciers(noeudCandidat)
        arcsNoeud = self.getGraphe().getArcs()
        detteMax = 0
        arcsDetteMax = []
        for arc in arcsNoeud:
            if (arc.getNoeudDebut() == noeudCandidat and
                    arc.getNoeudFin() in creanciers):
                if arc.poids == detteMax:
                    arcsDetteMax.append(arc)
                elif arc.poids > detteMax:
                    detteMax = arc.getPoids()
                    arcsDetteMax = []
                    arcsDetteMax.append(arc)
        if arcsDetteMax:
            arcDetteMax = random.choice(arcsDetteMax)
        else:
            arcDetteMax = None
    return arcDetteMax
def choixPremiereEntreprise(self) ‑> models.noeud.Noeud

Retourne un Noeud au hasard parmi les Noeuds du graphe.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> vue = Vue(g)
>>> controller = Controleur(g,vue)
>>> # On initialise une graine pour le pseudo-aléatoire afin de pouvoir prévoir l'aléatoire !
>>> # Ça serait top de pouvoir faire ça pour le loto !
>>> random.seed(7)
>>> controller.choixPremiereEntreprise().getNom()
'B'
Expand source code
def choixPremiereEntreprise(self) -> Noeud:
    """Retourne un Noeud au hasard parmi les Noeuds du graphe.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> vue = Vue(g)
    >>> controller = Controleur(g,vue)
    >>> # On initialise une graine pour le pseudo-aléatoire afin de pouvoir prévoir l'aléatoire !
    >>> # Ça serait top de pouvoir faire ça pour le loto !
    >>> random.seed(7)
    >>> controller.choixPremiereEntreprise().getNom()
    'B'
    """  # noqa : E501
    pass
    assert self.getGraphe().hasNoeud(), "Le graphe doit avoir des Noeuds."  # noqa : E501

    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    if not self.getGraphe().hasNoeud():
        raise ValueError("Le graphe doit avoir des Noeuds.")
    else:
        # L'entreprise choisie doit avoir des créanciers !
        entrepriseChoisie = random.choice(self.graphe.getNoeuds())
        while self.getGraphe().getCreanciers(entrepriseChoisie) == []:
            entrepriseChoisie = random.choice(self.graphe.getNoeuds())
    return entrepriseChoisie
def detteMaxEntreprise(self, noeudCandidat: models.noeud.Noeud) ‑> int

Retourne la plus grosse dette d'une instance de la classe Noeud.

Paramètres

noeudCandidat : Noeud - Une instance de la classe Noeud.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> vue = Vue(g)
>>> controller = Controleur(g,vue)
>>> controller.detteMaxEntreprise(A)
5
>>> controller.detteMaxEntreprise(B)
5
>>> controller.detteMaxEntreprise(C)
8
Expand source code
def detteMaxEntreprise(self, noeudCandidat: Noeud) -> int:
    """Retourne la plus grosse dette d'une instance de la classe Noeud.

    Paramètres
    ----------
    noeudCandidat : Noeud - Une instance de la classe Noeud.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> vue = Vue(g)
    >>> controller = Controleur(g,vue)
    >>> controller.detteMaxEntreprise(A)
    5
    >>> controller.detteMaxEntreprise(B)
    5
    >>> controller.detteMaxEntreprise(C)
    8
    """
    pass
    assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
    assert len(self.getGraphe().getNoeuds()) >=2 , "Le graphe doit avoir au moins deux noeuds."  # noqa : E501
    assert len(self.getGraphe().getArcs()) >=1 , "Le graphe doit avoir au moins un arc."  # noqa : E501

    # Ne pas utiliser les assertions pour valider les données
    # car elles peuvent être désactivées.
    # On lève donc une erreur en cas de souci
    allOk = True
    if len(self.getGraphe().getNoeuds()) < 2:
        allOk = False
        raise ValueError("Le graphe doit avoir au moins deux noeuds.")
    if len(self.getGraphe().getArcs()) < 1:
        allOk = False
        raise ValueError("Le graphe doit avoir au moins un arc.")
    if allOk and self.arcDetteMaxEntreprise(noeudCandidat) is not None:
        return self.arcDetteMaxEntreprise(noeudCandidat).getPoids()
def entrepriseDePlusFortCapital(self) ‑> models.noeud.Noeud

Renvoie le Noeud de plus fort capital du graphe parmi les noeuds ayant des dettes. En cas d'égalité on tire un Noeud au hasard.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> vue = Vue(g)
>>> controller = Controleur(g,vue)
>>> random.seed(7)
>>> print(controller.entrepriseDePlusFortCapital())
Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
>>> A.setCapital(7)
>>> print(controller.entrepriseDePlusFortCapital())
Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=7)
>>> C.setCapital(9)
>>> print(controller.entrepriseDePlusFortCapital())
Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=9)
Expand source code
def entrepriseDePlusFortCapital(self) -> Noeud:
    """Renvoie le Noeud de plus fort capital du graphe parmi les noeuds
    ayant des dettes. En cas d'égalité on tire un Noeud au hasard.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> vue = Vue(g)
    >>> controller = Controleur(g,vue)
    >>> random.seed(7)
    >>> print(controller.entrepriseDePlusFortCapital())
    Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
    >>> A.setCapital(7)
    >>> print(controller.entrepriseDePlusFortCapital())
    Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=7)
    >>> C.setCapital(9)
    >>> print(controller.entrepriseDePlusFortCapital())
    Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=9)
    """
    pass
    assert self.getGraphe().hasNoeud(), "Pour avoir une entreprise de plus fort capital, le graphe doit avoir au moins un Noeud."  # noqa : E501

    noeudsDePlusFortCapital = []
    noeudsDuGraphe = self.getGraphe().getEntreprises()
    arcsDuGraphe = self.getGraphe().getArcs()
    # On ne garde que les noeuds ayant des dettes
    noeudsAyantDesDettes = []
    for arc in arcsDuGraphe:
        for noeud in noeudsDuGraphe:
            if noeud == arc.getNoeudDebut():
                noeudsAyantDesDettes.append(noeud)
    capitalMax = noeudsAyantDesDettes[0].getCapital()
    noeudsDePlusFortCapital.append(noeudsAyantDesDettes[0])
    for i in range(1, len(noeudsAyantDesDettes)):
        if noeudsAyantDesDettes[i].getCapital() == capitalMax:
            noeudsDePlusFortCapital.append(noeudsAyantDesDettes[i])
        elif noeudsAyantDesDettes[i].getCapital() > capitalMax:
            noeudsDePlusFortCapital = []
            noeudsDePlusFortCapital.append(noeudsAyantDesDettes[i])
            # il faut aussi modifier capitalMax !
            capitalMax = noeudsAyantDesDettes[i].getCapital()
    if noeudsDePlusFortCapital:
        noeudDePlusFortCapital = random.choice(noeudsDePlusFortCapital)

    return noeudDePlusFortCapital
def genererDonBanque(self, noeudCandidat: models.noeud.Noeud) ‑> int

Renvoie un don correspondant à la plus forte dette du noeudCandidat.

Paramètres

noeudCandidat : Noeud - Une instance de la classe Noeud

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,7)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> vue = Vue(g)
>>> controller = Controleur(g,vue)
>>> controller.detteMaxEntreprise(A)
5
>>> controller.detteMaxEntreprise(B)
7
>>> controller.detteMaxEntreprise(C)
8
Expand source code
def genererDonBanque(self, noeudCandidat: Noeud) -> int:
    """Renvoie un don correspondant à la plus forte dette du noeudCandidat.

    Paramètres
    ----------
    noeudCandidat : Noeud - Une instance de la classe Noeud

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,7)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> vue = Vue(g)
    >>> controller = Controleur(g,vue)
    >>> controller.detteMaxEntreprise(A)
    5
    >>> controller.detteMaxEntreprise(B)
    7
    >>> controller.detteMaxEntreprise(C)
    8
    """
    pass
    assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501

    return self.detteMaxEntreprise(noeudCandidat)
def getGraphe(self) ‑> models.graphe.Graphe

Retourne l'instance du graphe du Controleur.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> vue = Vue(g)
>>> controller = Controleur(g,vue)
>>> print(controller.getGraphe())
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
Expand source code
def getGraphe(self) -> Graphe:
    """Retourne l'instance du graphe du Controleur.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> vue = Vue(g)
    >>> controller = Controleur(g,vue)
    >>> print(controller.getGraphe())
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
    """  # noqa : E501
    pass
    return self.graphe
def getVue(self) ‑> views.view.Vue

Retourne l'instance de la vue du Controleur.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> g.removeArc(AB1)
>>> g2 = copy.deepcopy(g)
>>> g.removeArc(BC)
>>> g3 = copy.deepcopy(g)        
>>> vue = Vue(g,[A,5,g2,g3],"heuristique")
>>> controller = Controleur(g,vue)
>>> print(controller.getVue())
Graphe de la vue          : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
Entreprise choisie        : Noeud(nom=A, voisins=['B', 'C'], nbArcs=4, capital=0)
Don de la banque des dons : 5
Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2', 'BC'])
Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
Expand source code
def getVue(self) -> Vue:
    """Retourne l'instance de la vue du Controleur.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> g.removeArc(AB1)
    >>> g2 = copy.deepcopy(g)
    >>> g.removeArc(BC)
    >>> g3 = copy.deepcopy(g)        
    >>> vue = Vue(g,[A,5,g2,g3],"heuristique")
    >>> controller = Controleur(g,vue)
    >>> print(controller.getVue())
    Graphe de la vue          : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
    Entreprise choisie        : Noeud(nom=A, voisins=['B', 'C'], nbArcs=4, capital=0)
    Don de la banque des dons : 5
    Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2', 'BC'])
    Graphe étape              : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'AC', 'CA1', 'CA2'])
    """   # noqa : E501
    pass
    return self.vue
def lancerAlgorithmeDeKlein(self) ‑> None

Lance la réduction de la dette en appliquant l'algorithme de Klein sur le graphe de l'instance de la classe Controleur.

Puis renvoie les données à la vue sous forme d'une liste de graphes correspondants aux états successifs pendant la modification du flot.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> # arcs arrivant sur A
>>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> # arc entre B et C
>>> BC5 = Arc('B-5->C',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,CA8,CA1,BC5])
>>> vue = Vue(g,None,"klein")
>>> controller = Controleur(g,vue)
>>> g.simplification()
>>> random.seed(4)
>>> controller.lancerAlgorithmeDeKlein()
>>> for etape in vue.etapes:
...     print(etape)
...
21
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A', 'C-0->A', 'C-0->B', 'A-0->C'])
Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'A-3->C', 'B-0->C', 'C-4->A', 'B-5->A', 'C-0->A', 'C-5->B', 'A-5->C'])
Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'A-0->C', 'B-0->C', 'C-1->A', 'B-5->A', 'C-3->A', 'C-5->B', 'A-8->C'])
Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'C-1->A'])
Expand source code
def lancerAlgorithmeDeKlein(self) -> None:
    """Lance la réduction de la dette en appliquant l'algorithme de Klein
    sur le graphe de l'instance de la classe Controleur.

    Puis renvoie les données à la vue sous forme d'une liste de graphes
    correspondants aux états successifs pendant la modification du flot.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
    >>> # arc entre B et C
    >>> BC5 = Arc('B-5->C',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,CA8,CA1,BC5])
    >>> vue = Vue(g,None,"klein")
    >>> controller = Controleur(g,vue)
    >>> g.simplification()
    >>> random.seed(4)
    >>> controller.lancerAlgorithmeDeKlein()
    >>> for etape in vue.etapes:
    ...     print(etape)
    ...
    21
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A', 'C-0->A', 'C-0->B', 'A-0->C'])
    Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'A-3->C', 'B-0->C', 'C-4->A', 'B-5->A', 'C-0->A', 'C-5->B', 'A-5->C'])
    Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'A-0->C', 'B-0->C', 'C-1->A', 'B-5->A', 'C-3->A', 'C-5->B', 'A-8->C'])
    Graphe(nom=graphe, noeuds=['C', 'B', 'A'], arcs=['A-2->B', 'C-1->A'])
    """ # noqa : E501
    pass

    # On factorise une variable pour PEP8
    etapes = self.getVue().getEtapes()
    graphe = self.getGraphe()
    # On initialise le tableau contenant les graphes des états successifs
    # avec le graphe initial - propriété de l'instance de Vue self.vue
    # On ajoutera la baisse de la dette globale en debut du tableau
    detteInitiale = graphe.detteGlobale()
    # On ajoute le graphe au tableau qui sera passé à la vue
    etapes.append(copy.deepcopy(graphe))
    # On simplifie le graphe s'il ne l'est pas
    if not graphe.estSimplifie():
        graphe.simplification()
        # On ajoute le graphe simplifié aux étapes
        etapes.append(copy.deepcopy(graphe))
    # On ajoute les arcs inverses de poids nul
    graphe.addArcsInversesNuls()
    # On ajoute le graphe avec les arcs inverses nuls aux étapes
    etapes.append(copy.deepcopy(graphe))
    # Pour tous les noeuds du graphe
    # On élimine tous les cycles partant de ce noeud
    # IL FAUDRAIT POUVOIR CHOISIR L'ORDRE DE TRAITEMENT DES NOEUDS
    # OU ALORS LES TRAITER AU HASARD POUR VOIR LA RÉDUCTION DE LA DETTE

    # On mélange la liste des noeuds du graphe afin de ne pas toujours
    # avoir le même ordre de traitement
    noeudsDuGrapheMelanges = graphe.getNoeuds()
    random.shuffle(noeudsDuGrapheMelanges)
    for noeud in noeudsDuGrapheMelanges:
        while graphe.kleinAllArcsCyclesFromNoeud(noeud) != []:
            # On lance l'élimination d'un cycle de ce noeud
            graphe.kleinModifierCycleFromNoeud(noeud)
            # On ajoute le graphe obtenu après modification du flot
            if not graphe.hasArc():
                etapes.append(copy.deepcopy(graphe))
                break
            else:
                etapes.append(copy.deepcopy(graphe))

    # On supprime les arcs nuls
    graphe.kleinDeleteDettesNulles()
    # On supprime les arcs inverses
    graphe.kleinDeleteArcsInverses()
    # On ajoute le graphe final
    etapes.append(copy.deepcopy(graphe))
    grapheFinal = graphe
    # On ajoute la baisse obtenue
    etapes.insert(0, detteInitiale - grapheFinal.detteGlobale())
def lancerMethodeHeuristique(self) ‑> None

Lance la méthode heuristique de réduction de la dette sur le graphe de l'instance de la classe Controleur.

Puis renvoie les données à la vue sous forme d'une liste de graphes correspondants aux états successifs pendant le ruissellement de la réduction de la dette.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> vue = Vue(g)
>>> controller = Controleur(g,vue)
>>> for n in g.getNoeuds():
...     print(g.positionNette(n))
...
-1
2
-1
>>> controller.lancerMethodeHeuristique()
>>> for n in g.getNoeuds():
...     print(g.positionNette(n,vue.etapes[0].getNom(),vue.etapes[1]))
...
-1
2
-1
Expand source code
def lancerMethodeHeuristique(self) -> None:
    """Lance la méthode heuristique de réduction de la dette sur le graphe de l'instance de la classe Controleur.
    
    Puis renvoie les données à la vue sous forme d'une liste de graphes correspondants aux états successifs pendant le
    ruissellement de la réduction de la dette.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> vue = Vue(g)
    >>> controller = Controleur(g,vue)
    >>> for n in g.getNoeuds():
    ...     print(g.positionNette(n))
    ...
    -1
    2
    -1
    >>> controller.lancerMethodeHeuristique()
    >>> for n in g.getNoeuds():
    ...     print(g.positionNette(n,vue.etapes[0].getNom(),vue.etapes[1]))
    ...
    -1
    2
    -1
    """  # noqa : E501
    pass
    # On factorise une variable pour PEP8
    etapes = self.getVue().getEtapes()
    # On choisit une entreprise au hasard
    premiereEntreprise = self.choixPremiereEntreprise()
    # On génère un don correspondant à sa plus grosse dette
    donPremiereEntreprise = self.genererDonBanque(premiereEntreprise)
    # On recupere la premiere entreprise sélectionnée
    # On fait une copie profonde pour l'affichage de l'état initial
    # On la passe à la vue
    etapes.append(copy.deepcopy(premiereEntreprise))
    # On recupère le don pour le passer à la vue
    etapes.append(donPremiereEntreprise)
    # On initialise le tableau contenant les graphes des états successifs
    # avec le graphe initial - propriété de l'instance de Vue self.vue
    grapheInitial = copy.deepcopy(self.getGraphe())
    # On ajoute le graphe au tableau qui sera passé à la vue
    etapes.append(grapheInitial)
    # On ajoutera la baisse de la dette globale en 3e position du tableau
    detteInitiale = grapheInitial.detteGlobale()
    # On lance le remboursement des dettes de cette premiere entreprise
    self.reglerDettesEntreprise(premiereEntreprise, donPremiereEntreprise)
    # On ajoute le nouveau graphe au tableau qui sera passé à la vue
    grapheSuivant = copy.deepcopy(self.getGraphe())
    etapes.append(grapheSuivant)
    # Pour lé méthode heuristique, les index 0, 1 ,2 sont respectivement :
    #   - la premiere entreprise sélectionnée.
    #   - le don à cette entreprise.
    #   - la baisse de la dette globale obtenue.
    index = 3
    while grapheSuivant.detteGlobale() != etapes[index-1].detteGlobale():
        # On choisit l'entreprise de plus fort capital
        entreprisePlusFortCapital = self.entrepriseDePlusFortCapital()
        # On relance le processus
        self.reglerDettesEntreprise(entreprisePlusFortCapital,
                                    entreprisePlusFortCapital.getCapital())
        # On ajoute le nouveau graphe au tableau qui sera passé à la vue
        grapheSuivant = copy.deepcopy(self.getGraphe())
        if not grapheSuivant.hasArc():
            # On ajoute deux fois le nouveau graphe au tableau qui sera
            # passé à la vue
            # On ajoute deux fois le graphe pour conserver le doublon même
            # si on break le while
            etapes.append(grapheSuivant)
            etapes.append(grapheSuivant)
            break
        else:
            etapes.append(grapheSuivant)
            index += 1
    grapheFinal = self.getGraphe()
    if grapheFinal.hasArc():
        etapes.insert(2, detteInitiale - grapheFinal.detteGlobale())
    else:
        etapes.insert(2, detteInitiale)
def lancerReductionParEliminationDesCycles(self) ‑> None

Lance la réduction de la dette par élimination des cycles du graphe de l'instance de la classe Controleur.

Puis renvoie les données à la vue sous forme d'une liste de graphes correspondants aux états successifs pendant le l'élimination des cycles du graphe.

Renvoie aussi la baisse de la dette globale obtenue.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB5,AB2,AC3 = Arc('AB5',A,B,5),Arc('AB2',A,B,2),Arc('AC3',A,C,3)
>>> # arcs arrivant sur A
>>> CA8,CA1 = Arc('CA8',C,A,8),Arc('CA1',C,A,1)
>>> # arc entre B et C
>>> BC5 = Arc('BC5',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,CA8,CA1,BC5])
>>> vue = Vue(g,None,"eliminationCycles")
>>> controller = Controleur(g,vue)                
>>> random.seed(1)        
>>> controller.lancerReductionParEliminationDesCycles()
>>> for etape in vue.etapes:
...     print(etape)
...
21
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB5', 'AB2', 'AC3', 'CA8', 'CA1', 'BC5'])
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'A-3->C', 'C-4->A'])
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'C-1->A'])
Expand source code
def lancerReductionParEliminationDesCycles(self) -> None:
    """Lance la réduction de la dette par élimination des cycles du graphe de l'instance
    de la classe Controleur.

    Puis renvoie les données à la vue sous forme d'une liste de graphes correspondants
    aux états successifs pendant le l'élimination des cycles du graphe.

    Renvoie aussi la baisse de la dette globale obtenue.


    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB5,AB2,AC3 = Arc('AB5',A,B,5),Arc('AB2',A,B,2),Arc('AC3',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA8,CA1 = Arc('CA8',C,A,8),Arc('CA1',C,A,1)
    >>> # arc entre B et C
    >>> BC5 = Arc('BC5',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB5,AB2,AC3,CA8,CA1,BC5])
    >>> vue = Vue(g,None,"eliminationCycles")
    >>> controller = Controleur(g,vue)                
    >>> random.seed(1)        
    >>> controller.lancerReductionParEliminationDesCycles()
    >>> for etape in vue.etapes:
    ...     print(etape)
    ...
    21
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB5', 'AB2', 'AC3', 'CA8', 'CA1', 'BC5'])
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'A-3->C', 'C-4->A'])
    Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'C-1->A'])
    """  # noqa : E501
    pass
    # On factorise une variable pour PEP8
    etapes = self.getVue().getEtapes()
    # On initialise le tableau contenant les graphes des états successifs
    # avec le graphe initial - propriété de l'instance de Vue self.vue
    # On ajoutera la baisse de la dette globale en debut du tableau
    detteInitiale = self.getGraphe().detteGlobale()
    # On ajoute le graphe initial au tableau qui sera passé à la vue
    etapes.append(copy.deepcopy(self.getGraphe()))
    # On simplifie le graphe
    self.getGraphe().simplification()
    # On ajoute le graphe simplifié aux étapes
    etapes.append(copy.deepcopy(self.getGraphe()))
    # Tant qu'il y a au moins un cycle dans le graphe
    # On réitère le processus
    while self.getGraphe().hasCycle():
        # On choisit un noeud initial qui présente un cycle
        # au hasard parmi les noeuds présentant un cycle
        noeudsPresentantUnCycle = []
        for noeud in self.getGraphe().getNoeuds():
            if (self.getGraphe().hasCycleFromNoeud(noeud) and
                    noeud not in noeudsPresentantUnCycle):
                noeudsPresentantUnCycle.append(noeud)
        noeudAleatoire = random.choice(noeudsPresentantUnCycle)
        # On lance l'élimination d'un cycle de ce noeud
        self.getGraphe().eliminerCycleFromNoeud(noeudAleatoire)
        # On ajoute le graphe obtenu après élimination du cycle
        etapes.append(copy.deepcopy(self.getGraphe()))
    grapheFinal = self.getGraphe()
    # On ajoute la baisse globale obtenue
    etapes.insert(0, detteInitiale - grapheFinal.detteGlobale())
def reglerDettesEntreprise(self, noeudCandidat: models.noeud.Noeud, don: int) ‑> None

Modifie le graphe de l'instance en remboursant les dettes du noeudCandidat à l'aide du don. On accepte ici les réductions partielles. C'est à dire que si A reçoit 6€ et doit 5€ à B et 4€ à C, A annule sa facture envers B et diminue sa facture envers C.

Paramètres

noeudCandidat : Noeud - Une instance de Noeud don : int - Une somme reçue pour rembourser les creanciers de noeudCandidat

Remarque

Fonction recursive, permettant de faire ruisseler le processus de remboursement pour le noeudCandidat.

Tests

>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> vue = Vue(g)
>>> controller = Controleur(g,vue)
>>> print(f"Graphe initial : {controller.graphe}")
Graphe initial : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
>>> controller.reglerDettesEntreprise(A,4)
>>> # Aucun arc n'est supprimé
>>> print(f"Graphe si le don est inférieur à la dette max : {controller.graphe}")        
Graphe si le don est inférieur à la dette max : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
>>> # Mais le poids de l'arc correspondant à la detteMax est réduit du don
>>> print(AB1)
Arc(nom=AB1, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4), poids=1, estInverse=False)
>>> print(A);print(B);print(C)
Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4)
Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0)
>>> # À présent la detteMax restante est sur l'arc AC est vaut 3
>>> controller.reglerDettesEntreprise(A,3)
>>> print(f"Graphe si le don est égale à la dette max : {controller.graphe}")
Graphe si le don est égale à la dette max : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'CA1', 'CA2', 'BC'])
>>> print(A);print(B);print(C)
Noeud(nom=A, voisins=['B', 'C'], nbArcs=4, capital=0)
Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4)
Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=3)
>>> # Il reste deux dettes au noeud A si on lui donne suffisamment pour tout rembourser, elle ne doit plus rien
>>> controller.reglerDettesEntreprise(A,3)
>>> print(f"Graphe si le don permet de tout rembourser : {controller.graphe}")
Graphe si le don permet de tout rembourser : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['CA1', 'CA2', 'BC'])
>>> print(A);print(B);print(C)
Noeud(nom=A, voisins=['C'], nbArcs=2, capital=0)
Noeud(nom=B, voisins=['C'], nbArcs=1, capital=7)
Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=3)
Expand source code
def reglerDettesEntreprise(self, noeudCandidat: Noeud, don: int) -> None:
    """Modifie le graphe de l'instance en remboursant les dettes du noeudCandidat à l'aide du don.
    On accepte ici les réductions partielles. C'est à dire que si A reçoit 6€ et doit 5€ à B et 4€ à C, 
    A annule sa facture envers B et diminue sa facture envers C.

    Paramètres
    ----------
    noeudCandidat : Noeud  - Une instance de Noeud
    don           : int    - Une somme reçue pour rembourser les creanciers de noeudCandidat

    Remarque
    --------
    Fonction recursive, permettant de faire ruisseler le processus de remboursement pour le noeudCandidat.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
    >>> # arcs arrivant sur A
    >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
    >>> # arc entre B et C
    >>> BC = Arc('BC',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB1,AB2,AC,CA1,CA2,BC])
    >>> vue = Vue(g)
    >>> controller = Controleur(g,vue)
    >>> print(f"Graphe initial : {controller.graphe}")
    Graphe initial : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
    >>> controller.reglerDettesEntreprise(A,4)
    >>> # Aucun arc n'est supprimé
    >>> print(f"Graphe si le don est inférieur à la dette max : {controller.graphe}")        
    Graphe si le don est inférieur à la dette max : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'AC', 'CA1', 'CA2', 'BC'])
    >>> # Mais le poids de l'arc correspondant à la detteMax est réduit du don
    >>> print(AB1)
    Arc(nom=AB1, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4), poids=1, estInverse=False)
    >>> print(A);print(B);print(C)
    Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
    Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4)
    Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0)
    >>> # À présent la detteMax restante est sur l'arc AC est vaut 3
    >>> controller.reglerDettesEntreprise(A,3)
    >>> print(f"Graphe si le don est égale à la dette max : {controller.graphe}")
    Graphe si le don est égale à la dette max : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB1', 'AB2', 'CA1', 'CA2', 'BC'])
    >>> print(A);print(B);print(C)
    Noeud(nom=A, voisins=['B', 'C'], nbArcs=4, capital=0)
    Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=4)
    Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=3)
    >>> # Il reste deux dettes au noeud A si on lui donne suffisamment pour tout rembourser, elle ne doit plus rien
    >>> controller.reglerDettesEntreprise(A,3)
    >>> print(f"Graphe si le don permet de tout rembourser : {controller.graphe}")
    Graphe si le don permet de tout rembourser : Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['CA1', 'CA2', 'BC'])
    >>> print(A);print(B);print(C)
    Noeud(nom=A, voisins=['C'], nbArcs=2, capital=0)
    Noeud(nom=B, voisins=['C'], nbArcs=1, capital=7)
    Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=3)
    """  # noqa : E501
    pass
    assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud."  # noqa : E501
    assert isinstance(don, int), "don doit être un entier."  # noqa : E501

    detteMax = self.detteMaxEntreprise(noeudCandidat)
    # On initialise le capital à don
    noeudCandidat.setCapital(don)
    if detteMax is not None:
        arcDetteMax = self.arcDetteMaxEntreprise(noeudCandidat)
        noeudFin = arcDetteMax.getNoeudFin()
        if don == detteMax:
            # On met à jour le capital de chaque entreprise
            noeudCandidat.setCapital(noeudCandidat.getCapital() - don)
            noeudFin.setCapital(noeudFin.getCapital() + don)
            # On supprime l'arc
            self.graphe.removeArc(arcDetteMax)
            # On met à jour le don
            don = don - detteMax
        else:
            if don < detteMax:
                # On met à jour le capital de chaque entreprise
                noeudCandidat.setCapital(noeudCandidat.getCapital() - don)
                noeudFin.setCapital(noeudFin.getCapital() + don)
                # On met à jour l'arc
                arcDetteMax.setPoids(detteMax-don)
                # On met à jour le don
                don = 0
            if don > detteMax:
                # On met à jour le capital de chaque entreprise
                noeudCandidat.setCapital(
                    noeudCandidat.getCapital() - detteMax
                )
                noeudFin.setCapital(noeudFin.getCapital() + detteMax)
                # On supprime l'arc
                self.graphe.removeArc(arcDetteMax)
                # On appelle recursivement avec les paramètres mis à jour
                self.reglerDettesEntreprise(noeudCandidat, don - detteMax)
def sameCycles(self, cycle1: list, cycle2: list) ‑> bool

Renvoie un booléen selon que cycle1 et cycle2 sont constitués des mêmes arcs avec les mêmes poids ou non

    Paramètres
    ----------
    cycle1 : list - Un cycle d'arcs du graphe
    cycle2 : list - Un autre cycle d'arcs du graphe

s Remarques --------- - Deux listes peuvent avoir les mêmes instances de la classe Arc mais avec des poids différents, il ne suffit donc pas de comparer les instances constituant les listes d'arcs.

    Tests
    -----
    >>> g = Graphe('graphe')
    >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
    >>> # arcs partant de A
    >>> AB7 = Arc('A-7->B',A,B,7)
    >>> # arcs arrivant sur A
    >>> CA9 = Arc('C-9->A',C,A,9)
    >>> # arc entre B et C
    >>> BC5 = Arc('B-5->C',B,C,5)
    >>> g.setNoeuds([A,B,C])
    >>> g.setArcs([AB7,CA9,BC5])
    >>> vue = Vue(g,None,"klein")
    >>> controller = Controleur(g,vue)
Expand source code
    def sameCycles(self, cycle1: list, cycle2: list) -> bool:
        """Renvoie un booléen selon que cycle1 et cycle2 sont constitués
        des mêmes arcs avec les mêmes poids ou non

        Paramètres
        ----------
        cycle1 : list - Un cycle d'arcs du graphe
        cycle2 : list - Un autre cycle d'arcs du graphe
s
        Remarques
        ---------
        - Deux listes peuvent avoir les mêmes instances de la classe Arc
        mais avec des poids différents, il ne suffit donc pas de comparer
        les instances constituant les listes d'arcs.

        Tests
        -----
        >>> g = Graphe('graphe')
        >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
        >>> # arcs partant de A
        >>> AB7 = Arc('A-7->B',A,B,7)
        >>> # arcs arrivant sur A
        >>> CA9 = Arc('C-9->A',C,A,9)
        >>> # arc entre B et C
        >>> BC5 = Arc('B-5->C',B,C,5)
        >>> g.setNoeuds([A,B,C])
        >>> g.setArcs([AB7,CA9,BC5])
        >>> vue = Vue(g,None,"klein")
        >>> controller = Controleur(g,vue)
        """
        pass
        assert isinstance(cycle1, list), "cycle1 doit être une liste"
        assert isinstance(cycle2, list), "cycle2 doit être une liste"
        for element in cycle1:
            assert isinstance(element, Arc), " cycle1 doit être une liste d'arcs" # noqa : E501
        for element in cycle2:
            assert isinstance(element, Arc), " cycle2 doit être une liste d'arcs" # noqa : E501

        sameCycles = True
        # Pour être identiques, les cycles ont la même taille
        if (len(cycle1) != len(cycle2)):
            sameCycles = False
        else:
            # Les arcs peuvent être à des positions distinctes
            # dans le tableau des arcs !!!!
            # Il faudrait peut être gérer ça ...
            taille = len(cycle1)
            for i in range(taille):
                if cycle1[i].getPoids() != cycle2[i].getPoids():
                    sameCycles = False

        return sameCycles