Programmation d'entiers Python - méthode de branche et de liaison

        La méthode branch and bound peut être utilisée pour résoudre des problèmes de programmation en entiers purs ou en entiers mixtes. Proposé par Land Doig et Dakin et al. au début des années 1960. Parce que cette méthode est flexible et facile à résoudre par ordinateur, c'est maintenant une méthode importante pour résoudre la programmation en nombres entiers. Il a été appliqué avec succès pour résoudre le problème du calendrier de production, le problème du voyageur de commerce, le problème de l'emplacement de l'usine, le problème du sac à dos et le problème de l'allocation.


méthode de branchement et de liaison

1. Définition

        Une recherche systématique est correctement effectuée sur tous les espaces de solutions réalisables d'un problème d'optimisation contraint (dont les solutions réalisables sont finies), qui est le contenu du branchement et de la délimitation. Habituellement, l'ensemble de l'espace de solutions réalisables est divisé à plusieurs reprises en sous-ensembles de plus en plus petits, ce qui est appelé branchement ; et une limite inférieure de l'objectif (pour le problème des minima) est calculée pour l'ensemble de solutions dans chaque sous-ensemble, ce qui est appelé délimitation. Après chaque branche, les sous-ensembles dont les limites dépassent la valeur cible de l'ensemble de solutions réalisables connu ne seront plus ramifiés, de sorte que de nombreux sous-ensembles peuvent être ignorés, ce que l'on appelle l'élagage. C'est l'idée principale de la méthode branch and bound.

        Il existe un problème de programmation entière maximisant A, et sa programmation linéaire correspondante est le problème B. En partant de la résolution du problème B, si sa solution optimale ne remplit pas les conditions entières de A, alors la fonction objectif optimale de B doit être la fonction objectif optimale de A. La borne supérieure de la fonction objectif optimale de ^ {*} est notée \overline{z} , et la valeur de la fonction objectif de toute solution réalisable de A de ^ {*}sera une borne inférieure \ souligner {z}de . La méthode branch and bound est une méthode de division de la région réalisable de B en sous-régions. Diminuez \overline{z}et \ souligner {z}, et finalement trouvez de ^ {*}. Utilisez maintenant l'exemple suivant pour illustrer :

2. Explication des exemples

Exemple 3 Résolvez le programme entier suivant :

 \begin{aligned} &\operatorname{Max} \quad z=40 x_{1}+90 x_{2} \\ &\left\{\begin{array}{l} 9 x_{1}+7 x_{ 2} \leq 56 \\ 7 x_{1}+20 x_{2} \leq 70 \\ x_{1}, x_{2} \geq 0 \ \end{tableau}\right.  \end{aligné}et est un entier 

        (i) Sans tenir compte de la restriction entière, c'est-à-dire en résolvant la programmation linéaire B correspondante, la solution optimale est :

L'utilisation de la table python simplex donne :

        objctive        |                   355.88                   |
        solution        |  4.81  |  1.82  |   0    |   0    |

La solution optimale est :x_{1}=4,81, x_{2}=1,82, z=355,88

                        On peut voir qu'il ne satisfait pas la condition d'entier. A ce moment Avecest la UN limite supérieure de la valeur optimale de la fonction objectif du problème, notée . Et  est évidemment une solution réalisable entière du problème , à ce moment , est une borne inférieure de , notée par , c'est - à - dire .de ^ {*}\overline{z}x_{1}=0,x_{2}=0UNz = 0de ^ {*}\ souligner {z}0\leq z^{*}\leq 356

        (ii) Parce que x_{1},x_{2}les actuels sont tous des non-entiers, ils ne répondent pas aux exigences d'entier, alors choisissez l'un d'entre eux pour créer une branche. Soit 1 x choisi pour le branchement et divise l'ensemble des possibles en 2 sous-ensembles :

x_{1}\leq [4.81]=4,x_{1}\geq [4.81]+1=5

Puisqu'il n'y a pas d'entiers entre 4 et 5, les solutions entières pour ces deux sous-ensembles doivent être en accord avec les solutions entières de l'ensemble faisable d'origine. Cette étape s'appelle la ramification. La planification et les solutions de ces deux sous-ensembles sont les suivantes :

InterrogationB_{1} :

\begin{aligned} &\quad \operatorname{Max} \quad z=40 x_{1}+90 x_{2} \\ &\left\{\begin{array}{l} 9 x_{1}+7 x_{2} \leq 56 \\ 7 x_{1}+20 x_{2} \leq 70 \\ 0 \leq x_{1} \leq 4, x_{2} \geq 0 \end{tableau}\right .  \end{aligné}

La solution optimale est :x_{2}=4.0,x_{2}=2.1,z_{1}=349

InterrogationB_{2} :

\begin{aligned} & \quad \text { Max } \quad z=40 x_{1}+90 x_{2} \\ &\left\{\begin{array}{l} 9 x_{1}+7 x_{2} \leq 56 \\ 7 x_{1}+20 x_{2} \leq 70 \\ x_{1} \geq 5, x_{2} \geq 0 \end{tableau}\right.  \end{aligné}

 La solution optimale est :x_{2}=5,0,x_{2}=1,57,z_{1}=341,4

 Redéfinir :0\leq z^{*}\leq 349

        (iii) Branchez le problème B1 pour obtenir les problèmes B11 et B12, et leurs solutions optimales sont

\begin{tableau}{ll} B_{11} : & x_{1}=4, x_{2}=2, z_{11}=340 \\ B_{12} : & x_{1}=1,43, \ mathrm{x}_{2}=3.00, z_{12}=327.14 \end{tableau}

Rebond : 340\leq z^{*}\leq 341, et B_{12}taillera.

        (iv B_{2}) Branchez le problème pour obtenir le problème B_{21}et B_{22}, et leurs solutions optimales sont

B_{21} :x_{1}=5,44,x_{2}=1,00,z_{22}=308.

                                        ​​​​​​​Aucune  B_{22} :  solution envisageable.

Sera B_{21},B_{22} taillé.

On peut donc conclure que la solution optimale au problème initial est :

x_{1}=4,x_{2}=2,z^{*}=340
 

3. Processus de modélisation mathématique :

La méthode branch and bound résout le problème de programmation en nombres entiers (maximisation) comme suit :

Initialement, le problème de programmation en nombres entiers à résoudre est appelé problème A, et le problème de programmation linéaire correspondant est appelé problème B.

La résolution du problème B peut conduire à l'une des situations suivantes :

(a) Si B n'a pas de solution réalisable, alors A n'a pas non plus de solution réalisable, alors arrêtez.

(b) B a une solution optimale et satisfait la condition entière du problème A, la solution optimale de B est la solution optimale de A, puis stop.

(c) B a une solution optimale, mais ne satisfait pas la condition entière du problème A, enregistrez sa valeur de fonction objectif \overline{z}.

Utilisez l'observation pour trouver une solution entière réalisable au problème A. Il est généralement conseillé d' x_{j}=0,j=1,...,n,  essayer de trouver sa valeur de fonction objectif et de l'enregistrer sous la forme \ souligner {z}. En utilisant  de ^ {*}​​​​​​​essayez de trouver la valeur de sa fonction objectif et enregistrez-la comme \underline{z}\leq z^{*}\leq \overline{z}itérante.

processus de modélisation

        La première étape : branchez, choisissez une variable x_{j}qui ne satisfait pas la condition d'entier dans la solution optimale de B, et sa valeur est représentée b_{j}par [  b_{j}] pour représenter b_{j}le plus grand entier inférieur à . Construire deux contraintes

 x_{j}\leq [b_{j}]etx_{j}\geq [b_{j}]+1

Ajoutez ces deux contraintes au problème B respectivement, et trouvez deux problèmes de programmation successeurs B_{1}et B_{2}. Résolvez ces deux problèmes successeurs sans tenir compte de la condition entière.

        Délimitation, prenez chaque problème suivant comme une branche pour indiquer le résultat de la solution, et trouvez celui avec la plus grande valeur de la fonction objectif optimale comme nouvelle borne supérieure parmi les résultats des solutions d'autres problèmes \overline{z}. À partir de chaque branche qui a rempli la condition d'entier, découvrez la valeur maximale de la fonction objectif en tant que nouvelle borne inférieure \ souligner {z}, s'il n'y a pas d'effet, elle \ souligner {z}restera inchangée .

         Étape 2 : Comparer et élaguer. Si une fonction objectif optimale de chaque branche est inférieure à z, élaguer cette branche, c'est-à-dire qu'elle ne sera pas prise en compte à l'avenir. S'il est supérieur à \ souligner {z}et ne remplit pas la condition d'entier, répétez la première étape. jusqu'à ce que vous obteniez de ^ {*}= à la \ souligner {z}fin. pour obtenir la solution entière optimale x_{j}^{*}, j=1,...,n.

 

 

 4. Mise en œuvre de la programmation

Utilisez python pour implémenter l'algorithme de branchement et de liaison :

from scipy.optimize import linprog
import numpy as np
from math import floor, ceil
import copy


class Node(object):
    def __init__(self, x_bounds=[], freeze_var_list=[], index=0, upper_or_lower=0):
        self._x_bounds = x_bounds
        self._freeze_var_list = freeze_var_list
        self._index = index
        self._upper_or_lower = upper_or_lower

        print("创建节点: {}".format(index))
        print('')

    def freeze_var(self, index, val):
        self._x_bounds[index][0] = val
        self._x_bounds[index][1] = val
        self._freeze_var_list.append(index)

    def set_lp_res(self, res):
        self._res = res
        s = ""
        for l in range(len(self._res['x'])):
            if l in self._freeze_var_list:
                s += "[" + str(self._res['x'][l]) + "]"
            else:
                s += " " + str(self._res['x'][l])
        print("x: ", s)

    def check_integer_val_solved(self, m):
        return True if m == len(self._freeze_var_list) else False


class BbAlgorithm(object):
    def __init__(self, c, a_ub, b_ub, x_b, integer_val):
        self.c = c
        self.a_ub = a_ub
        self.b_ub = b_ub
        self.x_b = x_b
        self._integer_val = integer_val
        self.best_solution = float('inf')
        self.best_node = None
        self.nodes = []
        self.nodes_solution = []

    def solve_lp(self, cur_x_b):
        return linprog(self.c, A_ub=self.a_ub, b_ub=self.b_ub, bounds=cur_x_b)

    def check_fessible(self, res):
        if res['status'] == 0:
            return True
        elif res['status'] == 2:
            return False
        else:
            raise ("问题无界")

    def add_node(self, node):
        res = self.solve_lp(node._x_bounds)
        if self.check_fessible(res) and res['fun'] < self.best_solution:
            node.set_lp_res(res)
            self.nodes_solution.append(res['fun'])
            self.nodes.append(node)
            if node.check_integer_val_solved(len(self._integer_val)):
                self.best_solution = res['fun']
                self.best_node = node
                print("----------------当前解决方案-------------------")
                print("x: ", node._res['x'])
                print("z: ", node._res['fun'])
                print("---------------------------------------------------\n")
            print("==> 将节点添加到树列表: ", node._index)
            print("==> 当前节点: ", self.nodes_solution)
            print("")
            return True
        else:
            print("==> 节点不可行: ", node._index)
            print("==> 当前节点: ", self.nodes_solution)
            print("")
            return False

    def del_higher_val_node(self, z_s):
        del_list = []
        for i in range(len(self.nodes_solution)):
            if self.nodes_solution[i] >= z_s:
                del_list.append(i)
        s = ""
        for i in del_list:
            s += " " + str(self.nodes[i]._index)
        print("删除节点: ", s)
        self.nodes = list(np.delete(self.nodes, del_list))
        self.nodes_solution = list(np.delete(self.nodes_solution, del_list))
        print("当前节点: ", self.nodes_solution)
        print("")

    def del_item(self, index):
        print("删除节点: ", self.nodes[index]._index)
        self.nodes = list(np.delete(self.nodes, index))
        self.nodes_solution = list(np.delete(self.nodes_solution, index))
        print("当前节点: ", self.nodes_solution)
        print("")

    def check_bounds(self, temp_x_b, index, u_or_l):
        if u_or_l == 1:
            if self.x_b[index][0] is not None and temp_x_b[index][0] is None:
                return False
            elif self.x_b[index][0] is None and temp_x_b[index][0] is not None:
                return True
            elif self.x_b[index][0] is not None and temp_x_b[index][0] is not None:
                return False if(self.x_b[index][0] > temp_x_b[index][0]) else True
        elif u_or_l == 2:
            if self.x_b[index][1] is not None and temp_x_b[index][1] is None:
                return False
            elif self.x_b[index][1] is None and temp_x_b[index][1] is not None:
                return True
            elif self.x_b[index][1] is not None and temp_x_b[index][1] is not None:
                return False if(self.x_b[index][1] < temp_x_b[index][1]) else True
        else:
            print("界限误差")
            exit()

    def run(self):
        print("####################### 开始 B & B #####################\n")
        node_count = 0
        node = Node(copy.deepcopy(self.x_b), [], node_count)
        node_count += 1
        res = self.solve_lp(self.x_b)

        lower = floor(res['x'][self._integer_val[0]])
        upper = lower + 1

        lower_node = Node(copy.deepcopy(self.x_b), [], node_count, 1)
        lower_node.freeze_var(self._integer_val[0], lower)
        self.add_node(lower_node)
        node_count += 1

        upper_node = Node(copy.deepcopy(self.x_b), [], node_count, 2)
        upper_node.freeze_var(self._integer_val[0], upper)
        self.add_node(upper_node)
        node_count += 1

        while len(self.nodes) > 0:
            index = np.argmin(self.nodes_solution)
            x_b = self.nodes[index]._x_bounds
            freeze_list = self.nodes[index]._freeze_var_list
            res = self.nodes[index]._res
            freeze_var_index = len(freeze_list)

            lower = floor(res['x'][self._integer_val[freeze_var_index]])
            upper = lower + 1
            lower_node = Node(copy.deepcopy(x_b), copy.deepcopy(freeze_list), node_count, 1)
            lower_node.freeze_var(self._integer_val[freeze_var_index], lower)
            self.add_node(lower_node)
            node_count += 1

            upper_node = Node(copy.deepcopy(x_b), copy.deepcopy(freeze_list), node_count, 2)
            upper_node.freeze_var(self._integer_val[freeze_var_index], upper)
            self.add_node(upper_node)
            node_count += 1

            self.del_item(index)
            self.del_higher_val_node(self.best_solution)
            print("############################################################")
        print("")
        print("######################### 最佳解决方案 #######################")
        print(self.best_node._res)


if __name__ == "__main__":
    integer_val = [0,1]
    c = [-40, -90]
    A = [[9, 7], [7, 20]]
    b = [56,70]
    x_bounds = [[0, None] for _ in range(len(c))]
    bb_algorithm = BbAlgorithm(c, A, b, x_bounds, integer_val)
    bb_algorithm.run()

 Résultat de sortie :

####################### 开始 B & B #####################

创建节点: 0

创建节点: 1

x:  [4.0] 2.0999999999901706
==> 将节点添加到树列表:  1
==> 当前节点:  [-348.99999999911535]

创建节点: 2

x:  [5.0] 1.5714285714280996
==> 将节点添加到树列表:  2
==> 当前节点:  [-348.99999999911535, -341.4285714285289]

创建节点: 3

x:  [4.0][2.0]
----------------当前解决方案-------------------
x:  [4. 2.]
z:  -340.0
---------------------------------------------------

==> 将节点添加到树列表:  3
==> 当前节点:  [-348.99999999911535, -341.4285714285289, -340.0]

创建节点: 4

==> 节点不可行:  4
==> 当前节点:  [-348.99999999911535, -341.4285714285289, -340.0]

删除节点:  1
当前节点:  [-341.4285714285289, -340.0]

删除节点:   3
当前节点:  [-341.4285714285289]

############################################################
创建节点: 5

==> 节点不可行:  5
==> 当前节点:  [-341.4285714285289]

创建节点: 6

==> 节点不可行:  6
==> 当前节点:  [-341.4285714285289]

删除节点:  2
当前节点:  []

删除节点:  
当前节点:  []

############################################################

######################### 最佳解决方案 #######################
     con: array([], dtype=float64)
     fun: -340.0
 message: 'The solution was determined in presolve as there are no non-trivial constraints.'
     nit: 0
   slack: array([6., 2.])
  status: 0
 success: True
       x: array([4., 2.])

Je suppose que tu aimes

Origine blog.csdn.net/qq_21402983/article/details/126388515
conseillé
Classement