给定目标总和和一组整数,找到添加到该目标的最接近的数字子集

时间:2013-10-24 16:57:21

标签: algorithm optimization set mathematical-optimization packing

我有一组整数M和一个目标和k。我想找到M的子集,当它们加在一起时最接近k而不会过去。

例如:

M = {1, 3, 5, 5, 14}

k = 12

answer = {1, 5, 5}

because 1 + 5 + 5 = 11 and there is no way to make 12.

我有附加约束,该子集最多可包含4个元素。

在我的应用程序中,| M |的大小可以很大(大约数千个元素)。如果无法在合理的时间内找到最佳答案,我对至少能给出“好”答案的解决方案感兴趣。

现在我正在通过生成10,000个随机子集并选择最接近的子集来解决这个问题,这个子集的效果比预期的要好但是很慢。我不确定这实际上有多远,但任何有关这方面的见解对我来说都很有趣。

3 个答案:

答案 0 :(得分:14)

由于您可以选择的项目数量有限制,因此可以使用相当简单的算法来完成。

该算法在“世代”中产生可能的总和。一代中的每个元素都包含一个表示总和的数字,以及M中用于构建该总和的N元组索引。

第零代是空的;生成X+1是通过遍历代X,并将M的元素添加到该代的每个值,并记录下一代X+1的总和来生成的。{ / p>

在计算总和之前,请检查其N元组是否存在您要添加的数字的索引。如果它在那里,请跳过该号码。接下来,检查总和:如果它已经存在于X+1总和中,则忽略它;否则,记录新的总和,以及新的N元组索引(附加你从一代X添加到N元组的数字的索引)。

以下是这对您的输入有用的方法:

第0代:空

第1代:

 1 - {0}
 3 - {1}
 5 - {2}
14 - {4}

第2代:

 4 - {0, 1}
 6 - {0, 2}
 8 - {1, 2}
10 - {2, 3}
15 - {0, 4}
17 - {1, 4}
19 - {2, 4}

第3代:

 9 - {0, 1, 2}
11 - {0, 2, 3}
13 - {1, 2, 3}
18 - {0, 1, 4}
20 - {0, 2, 4}
22 - {1, 2, 4}
24 - {2, 3, 4}

第4代:

14 - {0, 1, 2, 3}
23 - {0, 1, 2, 4}
25 - {0, 2, 3, 4}
27 - {1, 2, 3, 4}

现在,您可以在四代中搜索最接近目标号码k的数字。

答案 1 :(得分:2)

如果目标总和k不是太大,请查看http://en.wikipedia.org/wiki/Subset_sum_problem#Pseudo-polynomial_time_dynamic_programming_solution - 您可以使用它来创建一个位图,告诉您可以使用您的子集生成哪些数字。然后在位图中选择最接近的数字k。

答案 2 :(得分:2)

将问题分成4个部分:

  • Sum恰好包含1个元素

    只需循环查找并找到不大于目标的最高值。

  • Sum恰好包含2个元素

    使用双for循环查找不大于目标的最大总和。

  • Sum包含3个元素(类似于3SUM

    对元素进行排序

    使用双循环并对目标进行二分搜索减去两个值,寻找较小的值以找到不大于目标的最大总和。

  • Sum恰好包含4个元素

    对元素进行排序(已完成)

    使用double for循环生成2个元素的所有总和。

    现在,对于每个这样的总和,对目标的总和进行二元搜索,寻找较小的值,直到找到一个不包含该总和构成的值的值。

    有关类似问题(确切的总和)使用此方法的代码,请参阅this

平均病例运行时间(?)= O(n + n^2 + n^2 log n + n^2 log n) = O(n^2 log n)

确定最后一个问题的运行时间有点困难,在最坏的情况下可能与O(n^4 log n)一样糟糕,因为在找到适合的问题之前,你最终可能会查看大部分问题,但是应该很少发生,并且,在同一次运行中,有些应该花费更少的时间,因此整体运行时间可能会更短。