多维背包问题
多维背包问题(multidimensional knapsack problem)要求选择一批不可拆分的物品,使总收益最大,同时不超过多种资源的容量。这里的“维”指资源约束数量,例如重量、体积与预算,并不一定指物品在空间中的几何维度。[1]
一辆车的重量与体积容量都为五。四件货A、B、C、D的“重量、体积、收益”依次为 、、、。若只限制重量,B+C可得十五,但体积为七,不能装车;若同时检查两项资源,C+D恰好用满五、五,收益十四。
多条资源约束
对 件物品与 种资源,令 为物品 消耗的第 种资源, 为相同单位的容量, 为收益。模型为 每一行可以有自己的单位,但同一行内消耗与容量必须一致。重量和体积不能直接相加成为一个物理量;加权合成一条约束可以提供某种松弛界,通常不能代替全部原约束。
主例中可行的双件组合只有A+B与C+D,收益分别十二、十四;其余双件至少一种资源超限。任意三件的重量和或体积和也会超限,四件更不行。单件最大收益为九,所以C+D的十四就是全局最优。
二维整数容量的递推
容量及消耗为非负整数时,定义 为只用前 件、两种容量分别不超过 的最优收益。最后一件只有不选或选入两种情况,把两种候选收益分别记为 : 第二候选 只在两种剩余容量非负时存在,初值为 。任何合法选择必归入一支,而每支最优子集接上当前决策仍合法,因此按物品数归纳可证明递推。
代码用记忆化保存实际访问的状态,再比较相邻层值回溯选择。输入消耗按“每件一个二元组”给出;收益允许负值,空选择仍合法。
from functools import lru_cache
import math
def knapsack_2d(resources, values, capacity):
a, value, cap = tuple(map(tuple, resources)), tuple(values), tuple(capacity)
if len(cap) != 2 or any(type(b) is not int or b < 0 for b in cap):
raise ValueError("two nonnegative integer capacities required")
if len(a) != len(value) or any(len(row) != 2 for row in a):
raise ValueError("matching two-resource items required")
if any(type(x) is not int or x < 0 for row in a for x in row):
raise ValueError("nonnegative integer resource use required")
if any(not math.isfinite(x) for x in value):
raise ValueError("finite values required")
@lru_cache(None)
def f(i, u, v):
if i == 0:
return 0
best = f(i-1, u, v)
r, s = a[i-1]
if r <= u and s <= v:
best = max(best, value[i-1] + f(i-1, u-r, v-s))
return best
u, v = cap
best, chosen = f(len(a), u, v), []
for i in range(len(a), 0, -1):
if f(i,u,v) > f(i-1,u,v):
chosen.append(i-1); u -= a[i-1][0]; v -= a[i-1][1]
return best, tuple(reversed(chosen))
print(knapsack_2d([(4,1),(1,4),(3,3),(2,2)], [6,6,9,5], (5,5)))
# (14, (2, 3))
固定两维时最多有 个状态,每状态常数次转移,故时间、记忆表空间均为 。这是对容量数值的伪多项式界;容量写成二进制后的位数很小时,数值仍可能很大。维数继续增加时,容量网格的乘积会迅速膨胀。
变式。 体积容量降为四、重量仍为五。A+B与C+D都因体积五失效,其余双件也不可行,最优单选C,收益九。若某件消耗为零、收益为正,它应被选一次,代码通过物品层数递减保证不会无限重复;若允许每件无限次选择,则变成另一种整数背包,递推不能照搬。
参考文献
- ↑ Google OR-Tools开发者,knapsack_solver.h原作者接口说明,文件开头Multi-dimensional knapsack problems:d条容量约束和二元选择;本篇二维递推独立推导。