我有2套,集合A包含一组随机数,而集合B的元素是集合A的子集的总和 .
例如,
A = [8, 9, 15, 15, 33, 36, 39, 45, 46, 60, 68, 73, 80, 92, 96]
B = [183, 36, 231, 128, 137]
我想找到哪个数字是哪个子集的总和与这样的数据 .
S = [[45, 46, 92], [36], [8, 15, 39, 73, 96], [60, 68], [9, 15, 33, 80]]
我能用python编写非常愚蠢的暴力代码 .
class SolvedException(BaseException):
pass
def solve(sums, nums, answer):
num = nums[-1]
for i in range(0, len(sums)):
sumi = sums[i]
if sumi == 0:
continue
elif sumi - num < 0:
continue
answer[i].append(num)
sums[i] = sumi - num
if len(nums) != 1:
solve(sums, nums[:-1], answer)
elif sumi - num == 0:
raise SolvedException(answer)
sums[i] = sumi
answer[i].pop()
try:
solve(B, A, [list() for i in range(0, len(B))])
except SolvedException as e:
print e.args[0]
这段代码适用于小型数据,但计算数据需要数十亿年(有71个数字和10个总和) .
我可以使用一些更好的算法或优化 .
抱歉,我的英文不好,代码也很糟糕 .
编辑:对不起,我意识到我没有准确地描述问题 .
由于 A
中的每个元素都用于制作B的元素, sum(A) == sum(B)
另外,设置 S
must be 分区设置 A
.
2 回答
这被称为子集和问题,并且它是众所周知的NP完全问题 . 所以基本上没有有效的解决方案 . 参见例如https://en.wikipedia.org/wiki/Subset_sum_problem
但是,如果您的数字N不是太大,则使用动态编程的伪多项式算法:您从左到右读取列表A并保留可行且小于N的总和列表 . 如果您知道该数字对于给定的A,这是可行的,你可以很容易地获得A [a]可行的那些 . 因此动态编程 . 它通常足够快,可以解决您在那里出现的尺寸问题 .
这是一个Python快速解决方案:
然后
OP编辑后:
现在我正确地理解了你的问题,我仍然认为你的问题可以有效地解决,前提是你有一个有效的子集求解器:我在B上使用分而治之的解决方案:
将B切成两个近似相等的部分B1和B2
使用子集求和求解器在A中搜索其总和等于sum(B1)的所有子集S.
每个这样的S
:
调用递归求解(S,B1)并求解(A - S,B2)
如果两者都成功,你就有了解决方案
但是,对于我建议的动态编程解决方案,下面的(71,10)问题是遥不可及的 .
顺便说一句,这里是你的问题的快速解决方案,不使用分而治之,但它包含我的动态求解器的正确改编,以获得所有解决方案:
然后:
因此,对于这个大小的问题来说这是非常快的,尽管这里的优化注意到了 .
一个完整的解决方案,可以计算所有方式来完成总计 . 我使用int作为速度和内存使用的特征集:
19='0b10011'
代表[A[0],A[1],A[4]]=[8,9,33]
这里 .用于:
注意,当两个
15
不在同一子集中时,解决方案加倍 .它解决了独特的解决方案问题:
一秒钟不幸的是,它还没有针对(71,10)问题进行优化 .
另一个是纯粹的动态编程精神: