El método de bifurcación y acotación se puede utilizar para resolver problemas de programación de enteros puros o enteros mixtos. Propuesto por Land Doig y Dakin et al., a principios de la década de 1960. Debido a que este método es flexible y fácil de resolver por computadora, ahora es un método importante para resolver la programación entera. Se ha aplicado con éxito para resolver el problema del programa de producción, el problema del viajante de comercio, el problema de ubicación de la fábrica, el problema de la mochila y el problema de asignación.
método de ramificación y límite
1. Definición
Una búsqueda sistemática se realiza correctamente en todos los espacios de soluciones factibles de un problema de optimización con restricciones (cuyas soluciones factibles son finitas), que es el contenido de la ramificación y la delimitación. Por lo general, todo el espacio de soluciones factibles se divide repetidamente en subconjuntos cada vez más pequeños, lo que se denomina ramificación; y se calcula un límite inferior del objetivo (para el problema de mínimos) para el conjunto de soluciones en cada subconjunto, lo que se denomina delimitación. Después de cada bifurcación, aquellos subconjuntos cuyos límites excedan el valor objetivo del conjunto de soluciones factibles conocido no se bifurcarán más, por lo que se pueden ignorar muchos subconjuntos, lo que se denomina poda. Esta es la idea principal del método branch andbound.
Existe un problema A de programación entera maximizadora, y su correspondiente programación lineal es el problema B. Partiendo de resolver el problema B, si su solución óptima no cumple las condiciones enteras de A, entonces la función objetivo óptima de B debe ser la función objetivo óptima de A. El límite superior de la función objetivo óptima se denota como , y el valor de la función objetivo de cualquier solución factible de A será un límite inferior de . El método de ramificación y límite es un método para dividir la región factible de B en subregiones. Gradualmente disminuya y aumente , y finalmente encuentre . Ahora use el siguiente ejemplo para ilustrar:
2. Explicación de ejemplos
Ejemplo 3 Resuelve el siguiente programa entero:
y es un entero
(i) Despreciando la restricción de enteros, es decir, resolviendo la correspondiente programación lineal B, la solución óptima es:
El uso de la tabla simplex de python produce:
objctive | 355.88 |
solution | 4.81 | 1.82 | 0 | 0 |
La solución óptima es:
Se puede observar que no cumple la condición de número entero. En este momento es el límite superior del valor óptimo de la función objetivo del problema, denotado como . Y obviamente es una solución entera factible del problema, en este momento , es un límite inferior de, denotado por , es decir .
(ii) Debido a que los actuales son todos no enteros, no cumplen con los requisitos de enteros, así que elija uno de ellos para ramificar. Sea 1 x elegido para la ramificación y divida el conjunto factible en 2 subconjuntos:
Dado que no hay números enteros entre 4 y 5, las soluciones enteras para estos dos subconjuntos deben coincidir con las soluciones enteras originales del conjunto factible. Este paso se llama ramificación. La planificación y soluciones de estos dos subconjuntos son las siguientes:
Pregunta :
La solución óptima es:
Pregunta :
La solución óptima es:
Redefinir:
(iii) Ramifique el problema B1 para obtener los problemas B11 y B12, y sus soluciones óptimas son
Rebote: , y podaremos.
(iv ) Ramifica el problema para obtener el problema y , y sus soluciones óptimas son
.
Ninguna solución factible.
Será podado.
Por lo que se puede concluir que la solución óptima al problema original es:
3. Proceso de modelado matemático:
El método de bifurcación y acotación resuelve el problema de programación entera (maximización) de la siguiente manera:
Inicialmente, el problema de programación entera a resolver se llama problema A, y el problema de programación lineal correspondiente se llama problema B.
Resolver el problema B puede producir una de las siguientes situaciones:
(a) Si B no tiene una solución factible, entonces A tampoco tiene una solución factible, entonces deténgase.
(b) B tiene una solución óptima y cumple la condición de número entero del problema A, la solución óptima de B es la solución óptima de A, entonces deténgase.
(c) B tiene una solución óptima, pero no cumple la condición de número entero del problema A, registre el valor de su función objetivo .
Usa la observación para encontrar una solución entera factible para el problema A. Por lo general, es recomendable tratar de encontrar el valor de su función objetivo y registrarlo como . Usando trate de encontrar el valor de su función objetivo y regístrelo como iteración.
proceso de modelado
El primer paso: rama, elige una variable que no cumpla la condición de entero en la solución óptima de B, y su valor se representa por [ ] para representar el entero más grande menor que . Construir dos restricciones
y
Agregue estas dos restricciones al problema B respectivamente y encuentre dos problemas de programación sucesores y . Resuelva estos dos problemas de sucesores sin considerar la condición de número entero.
Delimitación, toma cada problema subsiguiente como una rama para indicar el resultado de la solución, y encuentra el que tiene el mayor valor de la función objetivo óptima como el nuevo límite superior entre los resultados de las soluciones de otros problemas . De cada rama que haya cumplido la condición de número entero, encuentre el valor máximo de la función objetivo como el nuevo límite inferior , si no hay efecto, permanecerá sin cambios .
Paso 2: Comparar y podar Si alguna función objetivo óptima de cada rama es menor que z, podar esta rama, es decir, no se considerará en el futuro. Si es mayor que y no cumple la condición de número entero, repita el primer paso. hasta que obtengas = al final. para obtener la solución entera óptima , .
4. Implementación de la programación
Use python para implementar el algoritmo de ramificación y límite:
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()
Resultado de salida:
####################### 开始 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.])