You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何寻找达成多目标值所需的最少集合组合数

多目标集合最少组合数求解问题

我想写一个脚本,用来找达成一组目标值所需的最少集合组合数,但现在遇到了麻烦——能找到的类似问题都是单目标求和,没有多数值目标的情况。

参考集合资源表:

| X | Y | Z
A  | 4       4
B  |     5   5
C  | 4   4
D  | 3   3   3

其中A、B、C、D是不同集合,各自提供不同数量的X、Y、Z资源。

设定目标值为40X、80Y、60Z(允许资源略高于目标,但不能低于),我手动试错找到的最少组合数是21,比如:

  • 0A, 9B, 7C, 5D → 43X, 88Y, 60Z
  • 1A, 8B, 6C, 6D → 46X, 82Y, 62Z

我的问题:

  1. 怎么验证21是不是最小可能的组合数?
  2. 如果不是,该怎么找到组合数更少的方案?

解决方案思路

一、验证21是否为最小值:理论下限+枚举验证

1. 先算理论最小下限

先从单个目标维度和综合维度计算绝对下限,作为验证基准:

  • 单目标下限:Y目标80,单个集合最多提供5(B),80/5=16;Z目标60,单个集合最多提供5(B),60/5=12;X目标40,单个集合最多提供4(A/C),40/4=10。取最大值16,但这只是单维度下限,还要看综合资源。
  • 综合资源下限:总资源需求40+80+60=180,单个集合最大总资源是D的9,180/9=20。这说明理论上最少可能是20(但20个D只能提供60X/60Y/60Z,不够Y的80,所以实际下限肯定高于20)。

你的21只比综合下限多1,直接验证20个集合是否可行即可:
遍历所有满足A+B+C+D=20的非负整数组合,检查是否同时满足:

4A + 0B +4C +3D ≥40
0A +5B +4C +3D ≥80
4A +5B +0C +3D ≥60

可以用代数简化计算:把D=20-A-B-C代入不等式,得到:

  1. A + C -3B ≥ -20
  2. 2B + C -3A ≥20
  3. A +2B -3C ≥0

如果找不到任何非负整数A、B、C满足以上条件,说明20不可能,21就是最小值;反之则20可行,21不是最小。

2. 枚举验证的脚本实现

不用暴力遍历所有可能,用嵌套循环就能高效处理:

# 验证20个集合是否可行
for A in range(0, 21):
    for B in range(0, 21 - A):
        max_C = 20 - A - B
        for C in range(0, max_C + 1):
            D = 20 - A - B - C
            # 检查约束条件
            x_ok = 4*A + 4*C + 3*D >=40
            y_ok =5*B +4*C +3*D >=80
            z_ok =4*A +5*B +3*D >=60
            if x_ok and y_ok and z_ok:
                print(f"找到20个集合的可行解:A={A}, B={B}, C={C}, D={D}")
                exit()
print("20个集合无可行解,21是当前最小值")

二、寻找更优解的通用方法

1. 整数线性规划(ILP)建模

这是多目标覆盖问题的标准解法,直接通过建模求解最小整数解,比手动试错高效得多。用Python的pulp库可以快速实现:

from pulp import LpProblem, LpMinimize, LpVariable, LpInteger

# 创建最小化问题
prob = LpProblem("Minimize_Sets", LpMinimize)

# 定义非负整数变量
A = LpVariable("A", lowBound=0, cat=LpInteger)
B = LpVariable("B", lowBound=0, cat=LpInteger)
C = LpVariable("C", lowBound=0, cat=LpInteger)
D = LpVariable("D", lowBound=0, cat=LpInteger)

# 目标函数:总集合数最小
prob += A + B + C + D, "Total_Sets"

# 添加约束条件
prob += 4*A + 0*B +4*C +3*D >=40, "X_Constraint"
prob += 0*A +5*B +4*C +3*D >=80, "Y_Constraint"
prob += 4*A +5*B +0*C +3*D >=60, "Z_Constraint"

# 求解并输出结果
prob.solve()
print("最小组合数:", int(prob.objective.value()))
for var in prob.variables():
    print(f"{var.name}: {int(var.value())}")

2. 启发式搜索(适合大规模问题)

如果集合种类或目标值规模更大,ILP效率下降,可以用启发式方法:

  • 从理论下限开始,逐个尝试更小的总数,用贪心+回溯的思路:优先选对当前缺口最大的目标贡献最多的集合,再回溯调整组合。
  • 或者用遗传算法、模拟退火等启发式算法,快速逼近最优解。

内容的提问来源于stack exchange,提问作者Rattja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 04:40:23