在总和匹配的两组整数中查找子集的算法

Posted

技术标签:

【中文标题】在总和匹配的两组整数中查找子集的算法【英文标题】:Algorithm to find subset within two sets of integers whose sums match 【发布时间】:2010-10-01 10:02:19 【问题描述】:

我正在寻找一种算法,它可以采用两组整数(正数和负数)并在每组整数中找到总和相同的子集。

这个问题和subset sum problem类似,只是我在两边都找子集。

这是一个例子:

列表 A 4, 5, 9, 10, 1

列表 B 21, 7, -4, 180

所以这里唯一的匹配是: 10、1、4、9 21、7、-4

有谁知道是否有针对此类问题的现有算法?

到目前为止,我唯一的解决方案是一种蛮力方法,它尝试每种组合,但它在指数时间内执行,我不得不严格限制要考虑的元素数量,以避免花费太长时间.

我能想到的唯一其他解决方案是在两个列表上运行阶乘并在那里寻找相等性,但这仍然不是很有效,并且随着列表变得越来越大,需要的时间呈指数增长。

【问题讨论】:

你好,燃烧僧。针对您刚刚删除的问题:iancooper.brinkster.net/Frontpage.aspx 是 .NET 的伦敦用户组。这是 Google 老兄的第一个结果! 【参考方案1】:

别人说的是真的:

    此问题是 NP 完全问题。一个简单的减少是通常的子集和。您可以通过注意仅当 A 的非空子集 (-B) 和为零时,A 的子集与 B 的子集(不是都是空的)之和来证明这一点。

    这个问题只是弱 NP 完全问题,因为它在所涉及的 数字 的大小上是多项式,但在其对数 中被推测为指数.这意味着这个问题比“NP-complete”这个名字所暗示的要容易。

    您应该使用动态规划。

那么我对这次讨论有何贡献?好吧,代码(在 Perl 中):

@a = qw(4 5 9 10 1);
@b = qw(21 7 -4 180);
%a = sums( @a );
%b = sums( @b );
for $m ( keys %a ) 
    next unless exists $b$m;
    next if $m == 0 and (@$a0 == 0 or @$b0 == 0);
    print "sum(@$a$m) = sum(@$b$m)\n";


sub sums 
    my( @a ) = @_;
    my( $a, %a, %b );
    %a = ( 0 => [] );
    while( @a ) 
        %b = %a;
        $a = shift @a;
        for my $m ( keys %a ) 
            $b$m+$a = [@$a$m,$a];
        
    %a = %b;
    
    return %a;

打印出来

sum(4 5 9 10) = sum(21 7)
sum(4 9 10 1) = sum(21 7 -4)

因此,值得注意的是,有不止一种解决方案可以解决您的原始问题!

编辑:用户 itzy 正确地指出这个解决方案是错误的,更糟糕​​的是,在多个方面!!对此我感到非常抱歉,我希望在上面的新代码中解决了这些问题。尽管如此,仍然存在一个问题,即对于任何特定的子集和,它只打印一种可能的解决方案。与之前的问题不同,这些问题是直接错误,我将其归类为故意限制。祝你好运,小心错误!

【讨论】:

我认为这里可能存在错误。如果我使用@a=qw(4 5 10) 和@b=qw(14 15) 运行此代码,则输出为 sum(4 10)=sum(14) 但如果我只是切换@b 的顺序,则为@b=qw(15 14) 那么没有输出。数组需要排序,还是这里有其他问题? 此解决方案还有其他问题。这里有更多讨论:***.com/questions/6444193/… @itzy:你说得对!很抱歉回复延迟——我最近没有在 Stack Overflow 上活跃。感谢您注意到错误!【参考方案2】:

像子集和问题一样,这个问题是 NP完全的,所以它有一个在时间多项式(M)中运行的解,其中M是问题中出现的所有数字的总和实例。您可以通过动态编程来实现这一点。对于每个集合,您可以通过填充二维二进制表来生成所有可能的总和,其中 (k,m) 处的“真”表示可以通过从集合的前 k 个元素中选择一些元素来实现子集总和 m。

您迭代地填充它 - 如果 (k-1,m) 设置为“true”,则将 (k,m) 设置为“true”(显然,如果您可以从 k-1 个元素中获取 m,则可以获取通过不选择第 k 个元素从 k 个元素中获取它)或者如果 (k-1,md) 设置为“true”,其中 d 是集合中第 k 个元素的值(您选择第 k 个元素的情况元素)。

填写表格可以得到最后一列中所有可能的总和(代表整个集合的那一列)。对两组都这样做并找到共同的和。您可以通过反转用于填充表格的过程来回溯表示解决方案的实际子集。

【讨论】:

【参考方案3】:

非常感谢所有快速回复!

动态规划解决方案与我们现在所拥有的详尽方法并没有真正的不同,我想如果我们需要最佳解决方案,我们确实需要考虑所有可能的组合,但是生成这个详尽的总和列表所花费的时间也太长了长.. 做了一个快速测试,为 x 个元素生成所有可能的总和所需的时间很快超过 1 分钟:

11 elements took - 0.015625 seconds
12 elements took - 0.015625 seconds
13 elements took - 0.046875 seconds
14 elements took - 0.109375 seconds
15 elements took - 0.171875 seconds
16 elements took - 0.359375 seconds
17 elements took - 0.765625 seconds
18 elements took - 1.609375 seconds
19 elements took - 3.40625 seconds
20 elements took - 7.15625 seconds
21 elements took - 14.96875 seconds
22 elements took - 31.40625 seconds
23 elements took - 65.875 seconds
24 elements took - 135.953125 seconds
25 elements took - 282.015625 seconds
26 elements took - 586.140625 seconds
27 elements took - 1250.421875 seconds
28 elements took - 2552.53125 seconds
29 elements took - 5264.34375 seconds

对于我们试图解决的业务问题,这不是真的可以接受的。我要回到绘图板上,看看我们是否确实需要知道所有解决方案,或者我们可以只做一个(最小/最大的子集,例如),希望这可以帮助解决问题并使我的算法按预期执行。

同样感谢!

【讨论】:

【参考方案4】:

子集和是 Np 完全的,您可以多项式将问题简化为它,因此您的问题也是 NP 完全的。

【讨论】:

也许你想提到归约:如果 A 和 B 是你解决这个问题的集合,那么在通常的子集和中取 A union (-B) 并且你正在寻找 sum 0。

以上是关于在总和匹配的两组整数中查找子集的算法的主要内容,如果未能解决你的问题,请参考以下文章

在python中查找列表的子集的总和

总和小于 M 的大小为 K 的子集的最大总和

寻找子集组合以实现给定总和同时保持成本最小的算法

查找最大索引 j 的有效算法,使得从索引 i 到 j 的总和小于 k

查找没有溢出的整数数组的总和

什么是KMP算法?KMP算法推导