本文其实与智能算法无关,这是最优化课程的一个小作业,之所以放在智能算法一栏,是因为这个问题的大部分解法都是智能算法。下面出场的是背包问题的一个变种——带约束的多背包问题,这个问题的精确算法复杂度极高,所以一般退而求其次寻找拥有固定近似比的近似算法,本文求解采取的则是一种基于DP动态规划的近似算法,巧妙的并不是算法本身,算法也不能保证求出最优解,精华在于近似比的证明部分,这一部分证明我参考了相关文献,分享给大家细细品味。
❖ 问题描述
有个物品,每个物品的重量为wi,另有m个背包,每个背包的容量为cj,另外每个物品有放入限制R,则表明物品i可以放入背包j中,则不能,因此R是一个n*m的矩阵。
那么在不违背放入限制规则,同时每个背包内物品不超过相应容量时,如何使背包所装物品的重量最大?上述问题可以用以下数学模型来描述:
❖ 算法描述
-
设置是否放入标记列表
isPut
,初始化为False
-
依次遍历每一个背包,记当前背包的编号为
-
枚举出当前
isPut=False
且R[i][j]=1
的所有物品集item
-
利用DP动态规划求出从
item
中取出物品放入背包的最优解,并将取出物品的isPut
标记为True
(表示前i个物品能放入容量为j背包的最大重量) -
回到step 1求解下一背包
❖ 近似比证明
上述算法求得的目标函数值.
Prove
我们假设上述算法执行的目标结果为T,且背包中的物品重量之和为Wj,而该算法中没有被放置于任何背包的物品集为M,接下来证明其余任何可行解从M中取出放入背包的物品重量总和S≤T。
【反证法】 假设存在一个可行解,使得S>T,那么总有一个背包内的物品重量之和>Wk,即背包k里面存在一个物品集有。注意到DP动态规划求得的是最优解,即Wk是当时剩下物品放入背包k的最优方案,而M中的物品在上述算法中没有被放入任何背包,从而可以将I中物品放入背包获得更优解,矛盾!从而S≤T得证。
另外从M外放入背包的物品总量最多即为T,从而背包总量≤T+S≤2T。
❖ 求解结果
背包 | 放入物品编号 | 背包内物品重量之和 | 背包容量 |
---|---|---|---|
0 | 2, 5, 6, 11, 12, 13 | 866 | 866 |
1 | 3, 14, 16, 19, 20, 22 | 908 | 908 |
2 | 1, 7, 10, 18, 21, 24, 30, 31 | 1361 | 1361 |
3 | 4, 9, 15, 17, 23, 26, 33 | 559 | 559 |
4 | 8, 27, 28, 32, 36, 39, 40, 42, 47, 49 | 1216 | 1216 |
5 | 29, 35, 38, 41, 51, 52 | 983 | 983 |
6 | 25, 34, 37, 53, 54, 55 | 1111 | 1111 |
7 | 71, 72, 80, 83 | 300 | 300 |
8 | 46, 50, 56, 57, 63 | 857 | 857 |
9 | 44, 48, 58, 61, 62, 64, 70 | 1462 | 1462 |
问题Sample下载: 多背包问题.
所有背包均装满,可以确定是最优解。
❖ 总结
事实上根据证明过程可以看出,任意一个能够求出0-1单背包问题最优解的算法都可以替换DP算法达到1/2的下界。同时可以举例证明1/2是最优下界,如背包容量{C+ε,C},物品重量为{ε,C,C+ε}。最优解为2C+ε,而实际运行结果可能为C+ε,两者比值
由此可见,如果将物品重量从高到低排列后再进行DP求解可以优化算法结果。
❖ C ☺ D E
import numpy as np
import pandas as pd
from copy import deepcopy as dp
load = np.array(pd.read_excel("背包问题.xlsx", header=4, usecols=range(1, 11)))
value = np.array(
pd.read_excel("背包问题.xlsx", header=4, usecols=[11]).iloc[:,
0])
size = np.array(
pd.read_excel("背包问题.xlsx", usecols=range(1, 11), nrows=1).iloc[0, :])
isPut = [False] * len(value)
total = []
list = []
for i in range(len(size)):
item = []
for j in range(len(value)):
if not isPut[j] and load[j, i] == 1:
item.append(j)
f = np.zeros([len(item), size[i] + 1])
it = [[[] for a in range(size[i] + 1)] for b in range(len(item))]
for a in range(1, len(item)):
for b in range(value[item[a]], size[i] + 1):
if value[item[a]] + f[a - 1, b - value[item[a]]] > f[a - 1, b]:
f[a, b] = value[item[a]] + f[a - 1, b - value[item[a]]]
it[a][b] = dp(it[a - 1][b - value[item[a]]])
it[a][b].append(item[a])
else:
f[a, b] = f[a - 1, b]
it[a][b] = dp(it[a - 1][b])
for k in it[len(item) - 1][size[i]]:
isPut[k] = True
if len(item) > 0:
total.append(f[len(item) - 1, size[i]])
list.append(it[len(item) - 1][size[i]])
PutIn=pd.DataFrame(columns=range(10),index=range(100))
PutIn.fillna(0,inplace=True)
for i in range(len(list)):
for j in list[i]:
PutIn[i][j]=1
print(PutIn)