跳到正文
格致开物MATHWIKI

集合打包问题

集合打包问题(set packing problem)从给定子集族中选取若干集合,要求任意两个选中集合不共享元素,并使选中集合的总收益最大。集合中的元素可以表示时间片、机场停机位或不可同时分配给两项任务的资源;“打包”在这里指选择互不冲突的资源组合,而非把物品装进有容量的箱子。

设资源全集为 U={1,2,3},四个可选任务为 A={1,2},vA=7,B={1},vB=4,C={2},vC=4,D={3},vD=2. 先挑收益最大的A,再挑不冲突的D,收益只有九。改选B、C、D,收益为十;单项收益较高并不等于组合收益最高。

关联矩阵与冲突图

令 aij=1 表示资源 i 属于任务集合 Sj,否则为零。对二元选择变量 xj,模型为 max⁡∑j=1nvjxj,∑j=1naijxj≤1(i∈U),xj∈{0,1}. 若两项选中任务有公共元素,该行的和至少为二,违反约束;反之,每行至多为一,就没有元素被两项同时占用。因而矩阵约束与两两不交完全等价。主例三行分别为 xA+xB≤1、xA+xC≤1、xD≤1。[1]

还可以把每项任务画成一个顶点,集合相交时连边。选中任务必须构成冲突图的独立集,即不选中任何一条边的两个端点。这给出集合打包与加权独立集问题的对应,但把图画出来本身并不构成求最优算法。

冲突图中A与B、C分别相连,D无冲突;最优选择B,C,D,收益4+4+2=10
每条连边表示不能同时选择。B与C虽都和A冲突,彼此并不冲突;这种结构导致逐项最大收益贪心失败。

最优解的两分支证明

若选A,则不能再选B或C,最多加D,收益不超过九。若不选A,B、C、D相互不交,收益非负,所以全部选入得到十,且这支没有更好的组合。这两个分支覆盖全部可能,故十为全局最优。这类逐分支证明也是分支定界法的基本思路。

以下小实例程序先把子集转为位掩码,随后枚举任务选择。已占用元素与新集合的掩码按位与为零,恰好表示不相交;只有通过该检查才合并资源。

Python 3
from itertools import product
import math

def set_packing(universe, subsets, values):
    u, sets, v = tuple(universe), tuple(map(frozenset, subsets)), tuple(values)
    if len(set(u)) != len(u) or len(sets) != len(v):
        raise ValueError("distinct universe and matching data required")
    index = {x:i for i,x in enumerate(u)}
    if any(not s <= set(u) for s in sets):
        raise ValueError("unknown resource")
    if any(not math.isfinite(x) for x in v):
        raise ValueError("finite values required")
    masks = [sum(1 << index[x] for x in s) for s in sets]
    best, chosen = 0, ()
    for bits in product((0,1), repeat=len(sets)):
        used, total, selected = 0, 0, []
        for j, x in enumerate(bits):
            if x:
                if used & masks[j]:
                    break
                used |= masks[j]; total += v[j]; selected.append(j)
        else:
            if total > best:
                best, chosen = total, tuple(selected)
    return best, chosen

print(set_packing([1,2,3], [{1,2},{1},{2},{3}], [7,4,4,2]))
# (10, (1, 2, 3))

每个二元组合被检查一次,任何冲突组合必在首次重复占用时被排除;其余组合恰好可行,因此返回值精确。令 M=∑j|Sj| 为候选集合元素出现总数。当前校验为每个候选重建全集集合,连同掩码转换的预处理时间为 O(M+n|U|)。若暂把位掩码运算视为单位成本,随后枚举时间为 O(n2n);保存复制后的子集、掩码及当前选择,额外空间为 O(M+n+|U|);资源很多时还应计入大整数按位操作的位复杂度。

变式。 把B收益改为三,则A+D和B+C+D都为九,最优解不唯一。若所有收益非正,允许不选任务时空集合最优;若要求每个资源恰好被使用一次,约束应改成等式,那是集合划分问题。至于至少覆盖一次的集合覆盖问题,其可行方向又与这里不同,三者不能只更换名称而复用同一组不等式。

参考文献

  1. ↑ Dan A. Iancu,Stanford CME307,Optimization, Lectures 7—8: Integer Programming (2024),§1.5,第5—6页,集合覆盖、打包与划分的关联矩阵模型。