装箱问题
装箱问题(bin packing problem)把给定物品全部放进容量相同的箱子,要求使用的箱子最少。每件物品不可拆分,箱内总大小不能超过容量。一维装箱只追踪一个可相加的资源;二维矩形排版还涉及形状、旋转及不重叠约束,不能只凭面积和直接套用一维模型。[1]
有六件大小为 的物品,箱容量十。总大小二十,任何方案至少需两箱。若把六和三先放一箱、五和两个二放一箱,最后一个二将不得不单独开箱;而改成 与 就刚好两箱。安排顺序造成的零碎空间,可能比总体容量更关键。
归箱模型与容量下界
对 件物品,容量 ,大小满足 。至多开 个箱;二元变量 表示物品归箱, 表示开箱: 变量均为零一。将容量约束相加,立即得到体积下界 主例下界二,与明确构造的两箱方案一致,因此无需继续搜索即可确认最优。体积下界并不总能达到,例如三个大小六的物品、容量十,每箱只能放一件,实际需三箱而下界仅二。
首次适应递减及不变量
首次适应算法依次处理物品:检查已有箱子,放入第一个还有足够空间的箱;都放不下才新开箱。先按大小递减排序后执行,称首次适应递减(FFD)。每次只在剩余容量足够时放入,故“已装物品各出现一次且每箱未超容”始终保持;最终得到可行上界,不保证恰好最优。[2]
下面返回每箱的原物品下标,以便区分大小相同的不同物品。输入只接受正整数大小及整数容量,空物品表返回空箱列表。
def first_fit_decreasing(sizes, capacity):
s = tuple(sizes)
if type(capacity) is not int or capacity <= 0:
raise ValueError("positive integer capacity required")
if any(type(x) is not int or not 0 < x <= capacity for x in s):
raise ValueError("positive integer sizes no larger than capacity required")
bins, free = [], []
for i in sorted(range(len(s)), key=lambda i: (-s[i], i)):
for j in range(len(bins)):
if s[i] <= free[j]:
bins[j].append(i); free[j] -= s[i]
break
else:
bins.append([i]); free.append(capacity-s[i])
return bins
sizes = [6,5,3,2,2,2]
print([[sizes[i] for i in b] for b in first_fit_decreasing(sizes,10)])
# [[6, 3], [5, 2, 2], [2]]
排序需 ,逐物品扫描至多 箱使总时间为 ,保存箱内下标及剩余容量需 空间。
还能给首次适应一个保守但完整的保证。设最后用了 个箱,任取前后两箱。后箱的第一件物品当时放不进前箱,所以前箱当时的装载加该件大小大于 ;最终两箱装载之和更大于 。对全部箱对求和,每箱载重出现 次,因此 整数性给 。一箱情形也满足此界。这条较粗保证不需要排序,当然也适用于FFD;它不是FFD所能达到的最精细理论界。
变式。 三件大小六、容量十时,FFD和最优都用三箱。若一个物品大于容量,模型不可行,代码会明确拒绝;不能让算法自动开一个超容量箱子来掩盖数据错误。固定箱数后只挑收益最大的部分物品,属于多背包问题,目标与“全部装完、最少开箱”不同。
参考文献
- ↑ Google OR-Tools,The Bin Packing Problem,问题定义、逐物品归箱与容量模型。
- ↑ João Pedro Pedroso、Abdur Rais、Mikio Kubo、Masakazu Muramatsu,Mathematical Optimization: Bin packing and cutting stock problems,The Bin Packing Problem,FFD定义;本篇反例、性能界证明及实现独立编写。