我有一组整数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个随机子集并选择最接近的子集来解决这个问题,这个子集的效果比预期的要好但是很慢。我不确定这实际上有多远,但任何有关这方面的见解对我来说都很有趣。
答案 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)
一样糟糕,因为在找到适合的问题之前,你最终可能会查看大部分问题,但是应该很少发生,并且,在同一次运行中,有些应该花费更少的时间,因此整体运行时间可能会更短。