已知:运送一批物品,质量和需求数量如下:
质量: 26, 30, 40, 56, 60, 76, 80, 86, 90, 100, 110, 116, 120, 176, 180, 200
数量: 2, 10, 8, 16, 80, 6, 32, 4, 36, 4, 10, 8, 18, 40, 16, 8
求解:最小需要承重为600的背包多少个?
解答:
import numpy as np
import pulp as lp
def integer_program(weights, nums, max_weight):
"""
整数规划
input:
weights: 物品质量集
nums: 物品需求数量集
max_weight: 背包承重
output:
单个背包装入数量集
"""
prob = lp.LpProblem("The GY Problem", lp.LpMaximize)
x = []
for i, n in enumerate(nums):
x.append(lp.LpVariable("x_%03d" % i, lowBound=0, upBound=n, cat="Integer"))
prob += lp.lpSum([weights[i] * x[i] for i in range(len(x))]), "Total weight"
prob += lp.lpSum([weights[i] * x[i] for i in range(len(x))]) <= max_weight, "Max weight"
prob.solve()
# print(prob)
# print("\tStatus:", lp.LpStatus[prob.status])
return np.array([v.varValue for v in prob.variables()]).astype("int32")
# 初始化变量
weights_array = np.array(
[26, 30, 40, 56, 60, 76, 80, 86, 90, 100, 110, 116, 120, 176, 180, 200])
nums_array = np.array(
[2, 10, 8, 16, 80, 6, 32, 4, 36, 4, 10, 8, 18, 40, 16, 8])
bag_weight = 600
# 理论最少背包数
bags_num_min = np.ceil(np.sum(weights_array * nums_array) / bag_weight)
print("理论最少", bags_num_min)
# 贪心算法
bags_num = 0
bag_nums_list = [] # 每个背包装入物品方案集合
while np.any(nums_array > 0):
nums_array_ = integer_program(weights_array, nums_array, bag_weight)
nums_array -= nums_array_
bags_num += 1
bag_nums_list.append(nums_array_)
print("实际最少", bags_num)
理论最少 49.0
实际最少 51
局部最优,未必是全局最优
from copy import deepcopy
import numpy as np
import pulp as lp
def bag_program(weights, nums, max_weight):
"""
动态规划,获取所有可能的背包装满方案
input:
weights: lengths array
nums: numbers array
max_weight: pipe length
output:
program
"""
weight_num = [[]] * max_weight
for il, l in enumerate(weights):
for i in range(max_weight):
if weight_num[i]:
if i + l < max_weight:
vals = deepcopy(weight_num[i])
vals_ = []
for val in vals:
val[il] += 1
if val[il] <= nums[il]:
vals_.append(val)
tmp = deepcopy(weight_num[i + l])
tmp.extend(vals_)
weight_num[i + l] = tmp
val = np.zeros(len(weights))
for i in range(l, max_weight + 1, l):
val[il] += 1
if val[il] > nums[il]:
break
tmp = deepcopy(weight_num[i - 1])
tmp.append(deepcopy(val))
weight_num[i - 1] = tmp
return weight_num[-weights[0]:]
def integer_program(num_arrs, nums):
"""
input:
num_arrs: num set
nums: minimize num
output:
result
"""
num_arrays_list = []
for vals in num_arrs:
num_arrays_list.extend(vals)
nums_mat = np.array(num_arrays_list)
print("\t", nums_mat.shape)
A = nums_mat.T
b = nums
prob = lp.LpProblem("The GY Problem", lp.LpMinimize)
x = [lp.LpVariable("x_%06d" % i, lowBound=0, cat="Integer")
for i in range(A.shape[1])]
prob += lp.lpSum(x), "Total Number"
for i in range(len(b)):
prob += lp.lpSum([A[i][j] * x[j] for j in range(A.shape[1])]) >= b[i], "lb%04d" % weights_array[i]
prob.solve()
print("\tStatus:", lp.LpStatus[prob.status])
res = []
for v in prob.variables():
if v.varValue:
res.append((A[:, int(v.name[2:])], v.varValue))
return res
# 初始化变量
weights_array = np.array(
[26, 30, 40, 56, 60, 76, 80, 86, 90, 100, 110, 116, 120, 176, 180, 200])
nums_array = np.array(
[2, 10, 8, 16, 80, 6, 32, 4, 36, 4, 10, 8, 18, 40, 16, 8])
bag_weight = 600
# 动态规划获取所有可能的背包装满方案(不能再放入任一物品)
weight_num_arrays = bag_program(weights_array, nums_array, bag_weight)
# 理论最少背包数
bags_num_min = np.ceil(sum(weights_array * nums_array) / bag_weight)
print("理论最少", bags_num_min)
# 迭代进行整数规划
# 迭代?方案数量较多,未知数多,求解慢
# 从较少方案集入手,慢慢增加方案集,直至获得最优解
for i in range(1, len(weight_num_arrays) + 1):
print(i)
# 方案数量为0,与上次迭代没变化
if not weight_num_arrays[-i]:
continue
result = integer_program(weight_num_arrays[-i:], nums_array)
bags_num = sum([n[-1] for n in result])
print("\t", bags_num)
# 当达到理论最小数量时,已获得最优解决方案
if bags_num == bags_num_min:
break
print("实际最少", sum([n for _, n in result]))
# # 承重验证
# np.all([sum(v * weights_array) <= bag_weight for v,_ in result])
# # 数量验证
# np.all(np.sum(np.array([v * n for v, n in result]), axis=0) - nums_array >= 0)
理论最少 49.0
1
(7122, 16)
Status: Optimal
50.0
2
3
(16896, 16)
Status: Optimal
49.0
实际最少 49.0
全局最优,求解慢