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

Python中使用动态规划提取子集和问题所有符合要求的子集

问题核心原因

你现在用的两种都是纯暴力回溯算法,时间复杂度是O(2^n),n是列表元素个数,每多一个元素计算量直接翻倍,元素量上去了慢是必然的。而且这两种实现都没有做剪枝,大量无效的递归路径会白白占用算力。

首先要明确:如果你需要输出所有符合条件的子集,这个问题本身最坏情况就是指数级复杂度——比如列表全是1,目标和是n/2,符合条件的子集数量本身就是组合数C(n,n/2),是指数级的,不可能压到多项式复杂度。但我们可以通过剪枝、去重、预判断砍掉绝大多数无效路径,运行效率会比你现在的版本高几十上百倍。

优化方案

第一步:基础剪枝+去重(优先做,改造成本极低)

先把输入列表从小到大排序,回溯的时候做两个判断:

  • 如果当前元素已经大于剩余需要凑的和,直接终止循环(后面的元素更大,不可能凑出来)
  • 如果当前元素和上一个元素相同,直接跳过,避免生成重复的子集(比如你测试用例里的多个2,不用重复遍历相同值的分支)

优化后的回溯代码示例:

def subset_sum(nums, target):
    nums.sort()
    res = []
    def backtrack(start, remain, path):
        if remain == 0:
            res.append(path.copy())
            return
        for i in range(start, len(nums)):
            # 剪枝:当前元素大于剩余需要的和,后面更大,直接停
            if nums[i] > remain:
                break
            # 去重:相同元素跳过,避免重复子集
            if i > start and nums[i] == nums[i-1]:
                continue
            path.append(nums[i])
            # 从i+1开始,避免重复选同一个元素
            backtrack(i+1, remain - nums[i], path)
            path.pop()
    backtrack(0, target, [])
    return res

# 测试
numbers = [1,3,4,5,6]
x = 9
print(subset_sum(numbers, x))
# 输出:[[3,6],[4,5]],和预期一致

第二步:加动态规划预剪枝(适合目标和不大的场景)

如果目标和的数值不大,可以先跑一遍01背包的动态规划,预计算出dp[s]表示能不能凑出和为s的子集。回溯的时候,如果当前剩余的和没法用剩下的元素凑出来,直接返回,不用继续递归,能砍掉大量无效路径。
这里的DP只需要判断可行性,不需要存具体子集,空间复杂度只有O(target),计算成本很低。

额外优化

如果你的列表里有远大于目标和的元素,可以在最开始就过滤掉,不用参与后续计算,比如你测试用例里的12、15,目标和是7,直接删掉就行,能直接减少n的大小。

性能对比

拿n=30的列表测试,你原来的回溯版本要跑几秒,加了排序剪枝的版本基本毫秒级就能出结果,提升非常明显。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 10:45:05