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 est notée
, et la valeur de la fonction objectif de toute solution réalisable de A
sera une borne inférieure
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
et
, et finalement trouvez
. Utilisez maintenant l'exemple suivant pour illustrer :
2. Explication des exemples
Exemple 3 Résolvez le programme entier suivant :
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 :
On peut voir qu'il ne satisfait pas la condition d'entier. A ce moment est la
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 .
(ii) Parce que 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 :
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 :
Interrogation :
La solution optimale est :
Interrogation :
La solution optimale est :
Redéfinir :
(iii) Branchez le problème B1 pour obtenir les problèmes B11 et B12, et leurs solutions optimales sont
Rebond : , et
taillera.
(iv ) Branchez le problème pour obtenir le problème
et
, et leurs solutions optimales sont
.
Aucune solution envisageable.
Sera taillé.
On peut donc conclure que la solution optimale au problème initial est :
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 .
Utilisez l'observation pour trouver une solution entière réalisable au problème A. Il est généralement conseillé d' essayer de trouver sa valeur de fonction objectif et de l'enregistrer sous la forme
. En utilisant
essayez de trouver la valeur de sa fonction objectif et enregistrez-la comme
itérante.
processus de modélisation
La première étape : branchez, choisissez une variable qui ne satisfait pas la condition d'entier dans la solution optimale de B, et sa valeur est représentée
par [
] pour représenter
le plus grand entier inférieur à . Construire deux contraintes
et
Ajoutez ces deux contraintes au problème B respectivement, et trouvez deux problèmes de programmation successeurs et
. 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 . À 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
, s'il n'y a pas d'effet, elle
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 à et ne remplit pas la condition d'entier, répétez la première étape. jusqu'à ce que vous obteniez
= à la
fin. pour obtenir la solution entière optimale
,
.
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.])