Пример #1
0
        /// <summary>
        /// Génère une solution vierge
        /// </summary>
        /// <returns>Solution vierge</returns>
        private CVRPSolution generateFirstSolution()
        {
            CVRPSolution sol = (CVRPSolution)_initialSol.copy();                //On copie la solution initiale

            sol.reset(data);                                                    //On réinitialise la solution
            sol.data = data;
            sol.addNewRoute(data);                                              //On ajoute une nouvelle route
            return(sol);
        }
Пример #2
0
        /// <summary>
        /// Démarre la résolution
        /// </summary>
        public void solve()
        {
            majValue = getFixedCost(_initialSol);                                       //On détermine le coût de la solution initiale
            bestSol  = _initialSol;                                                     //On la définit comme meilleure solution
            TreeNode tr     = new TreeNode(generateFirstSolution(), null);              //On initialise l'arbre de recherche
            TreeNode result = solve(tr, 0);                                             //On démarre la résolution

            logger.log(computedSteps + " calculs ont étés réalisés", Message.Level.Information);
            OnSolutionFound(new SolutionFoundEventArgs(bestSol));                       //On déclenche l'évenement correspondant à la résolution
        }
Пример #3
0
        /// <summary>
        /// Retourne une solution initialisée
        /// </summary>
        /// <param name="parser">DataPArser à utiliser</param>
        /// <returns>Solution initialisée</returns>
        private static CVRPSolution _AddVars(DataParser parser)
        {
            CVRPSolution sol = new CVRPSolution(parser);                //On créé une nouvelle instance de la solution

            //On parcours les variables
            for (int i = 0; i < parser.nodeList.Count; i++)
            {
                sol.addVariable(new Variable(i));                           //On ajoute la variable à la solution
            }
            return(sol);
        }
Пример #4
0
        /// <summary>
        /// Retourne la distance associée aux variables fixées de sol
        /// </summary>
        /// <param name="sol">Solution</param>
        /// <returns>Distance</returns>
        private double getFixedCost(CVRPSolution sol)
        {
            double total = 0;

            //On somme les coûts de chaques routes de la solution
            foreach (Route r in sol.routes)
            {
                total += getRouteCost(r);
            }
            return(total);
        }
Пример #5
0
        /// <summary>
        /// Retourne la borne inférieure de la solution par le sismplex
        /// </summary>
        /// <param name="sol">Solution</param>
        /// <returns>Borne inférieure</returns>
        private double evaluateSolutionSimplRelax(CVRPSolution sol)
        {
            double total = getFixedCost(sol);                                                                                   //On calule la distance fixée

            if (sol.freeVars.Count > 1)
            {
                BoundSimplex rbTSPSolver = new BoundSimplex(data, logger, sol);                 //On initilialise le solveur simplex
                total += rbTSPSolver.solve();                                                   //On détermine la borne inférieure des variables libres
            }
            return(total);
        }
Пример #6
0
 /// <summary>
 /// Met à jour le vecteur des coûts minimaux de la solution s
 /// </summary>
 /// <param name="s"></param>
 private void updateMinCost(CVRPSolution s)
 {
     //On bloque la dernière variable devenue intérieure à une route
     if (s.varsIdList.Count > 2)
     {
         for (int i = 0; i < s._minCost.Length; i++)
         {
             s._minCost[i][s.varsIdList[s.varsIdList.Count - 2]] = 0;
         }
     }
 }
Пример #7
0
        /// <summary>
        /// Retourne une solution par ordre d'apparition dans le fichier
        /// </summary>
        /// <param name="parser">DataParser à utiliser</param>
        /// <param name="loadConstraint">Contrainte de chargement</param>
        /// <returns>Solution générée</returns>
        public static CVRPSolution OrderedSolution(DataParser parser, double loadConstraint)
        {
            CVRPSolution sol = _AddVars(parser);                //Génère une instance de la solution

            sol.addNewRoute(parser);                            //Créé une nouvelle route
            //On fixe les variables
            for (int i = 0; i < parser.nodeList.Count; i++)
            {
                sol.setVariable(i, parser, loadConstraint);
            }
            sol.endSolution();                                  //On termine la solution
            return(sol);
        }
Пример #8
0
 /// <summary>
 /// Retourne la borne inférieure de la solution par méthode hybride
 /// </summary>
 /// <param name="sol">Solution</param>
 /// <param name="step">Profondeur d'hybridation</param>
 /// <returns></returns>
 private double evaluateSolutionHybrid(CVRPSolution sol, int step)
 {
     //Si la profondeur dépasse la profondeur d'hybridation
     if (step >= methodSwap)
     {
         updateMinCost(sol);                                     //On met à jour le vecteur des coûts minimaux
         return(evaluateSolutionSimplRelax(sol));                //On utilise le simplex
     }
     else
     {
         return(evaluateSolutionBasic(sol));                             //On utilise la méthode du PPVE
     }
 }
Пример #9
0
 //Constructeur
 public BnBSolverCVRP(Problem pb, CVRPSolution initialSolution, DataParser data, Logger logger, int evalMethod, double loadConstraint, int methodSwap = 0, bool benchmark = false)
 {
     //Attention à donner une solution avec toutes les variables fixées.
     //On copie les arguments
     _pb         = pb;
     this.logger = logger;
     this.data   = data;
     computeMinCost();                   //Calcul le vecteur des coûts minimaux
     _initialSol          = initialSolution;
     _initialSol._minCost = this._minCost;
     this.evalMethod      = evalMethod;
     this.loadConstraint  = loadConstraint;
     this.methodSwap      = methodSwap;
     this.benchmark       = benchmark;
 }
Пример #10
0
        /// <summary>
        /// Retourne la borne inférieure de la solution par la méthode du PPVE
        /// </summary>
        /// <param name="solution">Solution</param>
        /// <returns>Borne inférieure</returns>
        private double evaluateSolutionBasic(CVRPSolution solution)
        {
            double total = getFixedCost(solution);              //On calule la distance fixée

            updateMinCost(solution);                            //On met à jour le vecteur des coûts minimaux
            //On somme les distances minimales des variables libres
            foreach (int i in solution.freeVars)
            {
                total += getMinCost(solution, i);
            }
            if (solution.varsIdList.Count >= 1 && solution.freeVars.Count >= 1)
            {
                total += getMinCost(solution, solution.varsIdList[solution.varsIdList.Count - 1]);
            }
            return(total);
        }
Пример #11
0
        /// <summary>
        /// Détermine la borne inférieure de s
        /// </summary>
        /// <param name="sol">Solution</param>
        /// <param name="step">Profondeur de parcours</param>
        /// <returns>Borne inférieure</returns>
        private double evaluateSolution(CVRPSolution sol, int step)
        {
            switch (evalMethod)
            {
            case 0:
                return(evaluateSolutionBasic(sol));                                             //Méthode PPVE

            case 1:
                return(evaluateSolutionSimplRelax(sol));                                //Méthode simplex

            case 2:
                return(evaluateSolutionHybrid(sol, step));                              //Méthode hybride

            default:
                throw new NotImplementedException();
            }
        }
Пример #12
0
        /// <summary>
        /// Retourne le coût minimum pour varId dans s
        /// </summary>
        /// <param name="s">Solution</param>
        /// <param name="varId">Variable dont on charche la distance minimale</param>
        /// <returns>Distance minimale dans s</returns>
        private double getMinCost(CVRPSolution s, int varId)
        {
            double min = 0;

            for (int i = 0; i < s._minCost[varId].Count; i++)
            {
                if (s._minCost[varId][i] != 0)
                {
                    if (min == 0)
                    {
                        min = s._minCost[varId][i];
                    }
                    else if (min > s._minCost[varId][i])
                    {
                        min = s._minCost[varId][i];
                    }
                }
            }
            return(min);
        }
Пример #13
0
        /// <summary>
        /// Retourne une solution par la méthode des plus prches voisins
        /// </summary>
        /// <param name="parser">DataParser à utiliser</param>
        /// <param name="loadConstraint">Contrainte de chargement</param>
        /// <returns>Solution générée</returns>
        public static CVRPSolution PPVSolution(DataParser parser, double loadConstraint)
        {
            CVRPSolution sol = _AddVars(parser);                                    //Génère une instance de la solution

            sol.addNewRoute(parser);                                                //Créé une nouvelle route
            sol.setVariable(0, parser, loadConstraint);                             //Fixe le dépot dans la route
            double[,] arcWeight = (double[, ])parser.arcWeight.Clone();             //Créé une copie de la matrice des distance
            //On boucle tant que tout les clients ne sont pas servis
            while (sol.freeVarCount != 0)
            {
                int    nearestId = 0;                                                      //On initialise le plus proche voisin
                double distance  = arcWeight[sol.varsIdList[sol.varsIdList.Count - 1], 0]; //On initialise sa distance au dernier point visité
                //On parcours tout les voisins potentiels
                for (int j = 1; j < parser.nodeList.Count; j++)
                {
                    //Si l'arc vers j est réalisable et meilleur que les précédents considérés
                    if (arcWeight[sol.varsIdList[sol.varsIdList.Count - 1], j] != 0 && (distance == 0 || arcWeight[sol.varsIdList[sol.varsIdList.Count - 1], j] < distance))
                    {
                        //On met à jour le plus prche voisin
                        nearestId = j;
                        distance  = arcWeight[sol.varsIdList[sol.varsIdList.Count - 1], j];
                    }
                }
                //Si on revient au dépot, on créé une nouvelle solution
                if (nearestId == 0)
                {
                    sol.endRoute();
                }
                else
                {
                    sol.setVariable(nearestId, parser, loadConstraint);                     //On fixe le plus proche voisin
                }
                //On empêche le plus proche voisin d'être de nouveau disponible
                for (int j = 0; j < parser.nodeList.Count; j++)
                {
                    arcWeight[j, nearestId] = 0;
                }
            }
            sol.endSolution();              //On termine la solution
            return(sol);
        }
Пример #14
0
 /// <summary>
 /// Déclenche l'évenement d'affichage
 /// </summary>
 /// <param name="s">SSolution à afficher</param>
 public void displaySolution(CVRPSolution s)
 {
     OnPartialSolutionFound(new SolutionFoundEventArgs(s));
 }
Пример #15
0
 public SolutionFoundEventArgs(CVRPSolution res)
 {
     result = res;
 }
Пример #16
0
        /// <summary>
        /// Retourne une solution par la méthode de Clarke and Wright
        /// </summary>
        /// <param name="parser">DataParser à utiliser</param>
        /// <param name="loadConstraint">Contrainte de chargement</param>
        /// <returns>Solution générée</returns>
        public static CVRPSolution ClarkeWrightSolution(DataParser parser, double loadConstraint)
        {
            CVRPSolution sol = _AddVars(parser);                //Génère une instance de la solution
            //On initialise la liste des savings
            List <KeyValuePair <Tuple <int, int>, double> > savings = new List <KeyValuePair <Tuple <int, int>, double> >();
            int j;

            //On génère les savings
            for (int i = 1; i < parser.nodeList.Count; i++)
            {
                for (j = 1; j < parser.nodeList.Count - 1; j++)
                {
                    int realJ = j;
                    //On évite l'arc i->i
                    if (j >= i)
                    {
                        realJ++;
                    }
                    savings.Add(new KeyValuePair <Tuple <int, int>, double>(new Tuple <int, int>(i, realJ), parser.arcWeight[0, realJ] + parser.arcWeight[i, 0] - parser.arcWeight[i, realJ]));
                }
            }
            //On trie la liste
            savings.Sort(
                delegate(KeyValuePair <Tuple <int, int>, double> pair1, KeyValuePair <Tuple <int, int>, double> pair2)
            {
                return(pair1.Value.CompareTo(pair2.Value));
            });
            savings.Reverse();                                          //On la renverse (ordre décroissant)
            List <List <int> > routes     = new List <List <int> >();   //Liste des routes de la solution
            List <double>      routeDist  = new List <double>();        //Liste des distances associés à chaques routes
            List <double>      routeCosts = new List <double>();        //Liste des coûts associés à chaques routes

            //On initialise les routes initiales
            for (int i = 1; i < parser.nodeList.Count; i++)
            {
                routes.Add(new List <int>(new int[] { i }));
                routeDist.Add(parser.arcWeight[0, i] + parser.arcWeight[i, 0]);
                routeCosts.Add(parser.nodeList[i].quantity);
            }
            j = 0;
            //Tant que la solution
            while (j < savings.Count - 1 && savings[j].Value >= 0)
            {
                Tuple <int, int> couple = savings[j].Key;                   //On récupère les noeuds associés au saving
                //On vérifie que les deux noeuds peuvent être fusionnés
                object[] res = _areMergeable(couple.Item1, couple.Item2, routes, routeDist, routeCosts, savings[j].Value, loadConstraint);
                //Si ils peuvent être fusionné
                if ((bool)res[0])
                {
                    routes[(int)res[1]]     = routes[(int)res[1]].Concat(routes[(int)res[2]]).ToList(); //On créé la nouvelle route
                    routeDist[(int)res[1]]  = (double)res[4];                                           //On met à jour la distance de la route
                    routeCosts[(int)res[1]] = (double)res[3];                                           //On met à jour le coût de la route
                    routes.RemoveAt((int)res[2]);                                                       //On suppprime l'ancienne route
                    routeDist.RemoveAt((int)res[2]);
                    routeCosts.RemoveAt((int)res[2]);                                                   //On supprime le coût associé
                }
                j++;
            }
            //On créé la solution
            foreach (List <int> r in routes)
            {
                sol.addNewRoute(parser);
                foreach (int node in r)
                {
                    sol.setVariable(node, parser, loadConstraint);
                }
                sol.endSolution();
            }
            return(sol);
        }
Пример #17
0
        /// <summary>
        /// Fonction récursive de résolution
        /// </summary>
        /// <param name="currentNode">Noeud courant de l'arbre</param>
        /// <param name="step">Profondeur du parcours</param>
        /// <returns>Noeud courant</returns>
        private TreeNode solve(TreeNode currentNode, int step)
        {
            //Si la solution est une solution finale
            if (currentNode.sol.isFinalSolution())
            {
                //On al déclare comme finale
                ((CVRPSolution)currentNode.sol).endSolution();
                currentNode.sol.eval = getFixedCost((CVRPSolution)currentNode.sol);
                //Si cette solution est meilleure
                if (majValue > currentNode.sol.eval)
                {
                    //On met à jour la meilleure solution et on l'affiche
                    majValue = currentNode.sol.eval;
                    bestSol  = (CVRPSolution)currentNode.sol;
                    logger.log("Meilleur solution trouvée de coût:" + ((int)Math.Round(currentNode.sol.eval)).ToString(), Message.Level.Warning);
                    computedSteps++;
                    displaySolution(bestSol);
                }
                else
                {
                    currentNode.isLocked = true;                        //On bloque la solution
                    computedSteps++;
                }
                updateBenchmark(step);
                return(currentNode);
            }
            else
            {
                List <TreeNode> nextSol = new List <TreeNode>();                //On initialise la liste des enfants
                for (int i = 0; i < _pb.varCount; i++)
                {
                    //Pour chaque variable en encors libre
                    if (!currentNode.sol.isFixed(i))
                    {
                        CVRPSolution nSol1 = (CVRPSolution)currentNode.sol.copy();                              //On copie la solution
                        nSol1.setVariable(i, data, loadConstraint);                                             //On fixe une nouvelle variable
                        nSol1.eval = evaluateSolution(nSol1, step);                                             //On détermine la borne inférieure
                        //Si la solution est viable
                        //> ou < selon minimisation ou maximisation
                        if (nSol1.eval <= majValue)
                        {
                            nextSol.Add(new TreeNode(nSol1, null));                             //On l'ajoute aux enfants
                        }
                        else
                        {
                            computedSteps++;
                        }
                        //Si on peut obtenir une autre solution en créant une nouvelle route
                        if (nSol1.routes.Count == ((CVRPSolution)currentNode.sol).routes.Count)
                        {
                            CVRPSolution nSol2 = (CVRPSolution)currentNode.sol.copy();
                            nSol2.endRoute();
                            nSol2.setVariable(i, data, loadConstraint);
                            nSol2.eval = evaluateSolution(nSol2, step);
                            //> ou < selon minimisation ou maximisation
                            if (nSol2.eval <= majValue)
                            {
                                nextSol.Add(new TreeNode(nSol2, null));
                            }
                            else
                            {
                                computedSteps++;
                            }
                        }
                    }
                }
                //Si il y a des enfants
                if (nextSol.Count != 0)
                {
                    nextSol.Sort((x, y) => x.sol.eval.CompareTo(y.sol.eval));                           //On les trie selon leur borne inférieure
                    //On parcours les enfants
                    for (int i = 0; i < nextSol.Count; i++)
                    {
                        //Si la solution est toujours viable
                        if (nextSol[i].sol.eval <= majValue)
                        {
                            nextSol[i] = solve(nextSol[i], step + 1);                                   //On poursuit la résolution
                        }
                        else
                        {
                            nextSol[i].isLocked = true;
                            computedSteps++;
                        }
                    }
                    currentNode.childs = nextSol;
                }
                else
                {
                    currentNode.isLocked = true;
                    updateBenchmark(step);
                }

                computedSteps++;
                int locked = 0;
                //On regarde si tout les enfants sont bloquéss
                foreach (TreeNode node in nextSol)
                {
                    if (node.isLocked)
                    {
                        node.Dispose();
                        locked++;
                    }
                }
                //Si ils le sont tous, on bloque la solution
                if (locked == nextSol.Count)
                {
                    currentNode.isLocked = true;
                    nextSol = null;
                }
                return(currentNode);
            }
        }