鉴于
一组数字n[1], n[2], n[3], .... n[x]
还有一个数字M
我想找到
的最佳组合n[a] + n[b] + n[c] + ... + n[?] >= M
组合应该达到达到或超过M所需的最小值,没有其他组合可以产生更好的结果。
将在PHP中执行此操作,因此可以使用PHP库。如果没有,只需一般算法即可。谢谢!
答案 0 :(得分:4)
这看起来像是一个经典的Dynamic Programming问题(也有其他答案表明它与0-1背包和子集和问题的相似性)。整个事情归结为一个简单的选择:对于列表中的每个元素,我们是否在总和中使用它。我们可以编写一个简单的递归函数来计算答案:
f(index,target_sum)=
0 if target_sum<=0 (i.e. we don't need to add anymore)
infinity if target_sum>0 and index is past the length of n (i.e. we have run out of numbers to add)
min( f(index+1,target_sum), f(index+1,target_sum-n[index])+n[index] ) otherwise (i.e. we explore two choices - 1. take the current number 2. skip over the current number and take their minimum)
由于这个函数有重叠的子问题(它一遍又一遍地探讨相同的子问题),所以最好用缓存来记忆函数,以保存之前已经计算过的值。
这是Python中的代码:
#! /usr/bin/env python
INF=10**9 # a large enough number of your choice
def min_sum(numbers,index,M, cache):
if M<=0: # we have reached or gone past our target, no need to add any more
return 0
elif len(numbers)==index: # we have run out of numbers, solution not possible
return INF
elif (index,M) in cache: # have been here before, just return the value we found earlier
return cache[(index,M)]
else:
answer=min(
min_sum(numbers,index+1,M,cache), # skip over this value
min_sum(numbers,index+1,M-numbers[index],cache)+numbers[index] # use this value
)
cache[(index,M)]=answer # store the answer so we can reuse it if needed
return answer
if __name__=='__main__':
data=[10,6,3,100]
M=11
print min_sum(data,0,M,{})
此解决方案仅返回最小总和,而不是用于制作它的实际元素。您可以轻松扩展这个想法,将其添加到您的解决方案中。
答案 1 :(得分:2)
我认为greedy algorithm方法可行。 你期望在集合中有多少个数字?如果它相当低,你可以尝试backtrack,但我不推荐它用于大型套装。
答案 2 :(得分:1)
这种问题称为二进制linear programming(整数线性编程的一种特殊情况)。它是着名的NP难题 - 即通常无法有效解决。
然而,存在良好的解决方案,包括商业和免费使用,例如您可以从程序中调用的开源解算器lpsolve。
:编辑:老答案是下铺。我把这些因素搞糊涂了。答案 3 :(得分:1)
pseudocode:
list<set> results;
int m;
list<int> a;
// ...
a.sort();
for each i in [0..a.size]
f(i, empty_set);
// ...
void f(int ind, set current_set)
{
current_set.add(a[ind]);
if (current_set.sum > m)
{
results.add(current_set);
}
else
{
for (int i=ind + 1; i<a.size; ++i)
{
f(i, current_set); // pass set by value
// if previous call reached solution, no need to continue
if (a[ind] + current_set.sum) > m
break;
}
}
}
// choose whatever "best" result you need from results, the one
// with the lowest sum or lowest number of elements
答案 4 :(得分:0)
我认为这是NP完全的(不可能找到一种快速的方法一般)。你问这个问题的方式让我想到用一个从M稳定增加的目标整数求解连续的Subset sum problems。
在您的示例中,没有已知的方法来确定是否可以到达M.只有蛮力的解决方案会告诉你这是不可能的,只有这样才能检查M + 1.
但是,您可能希望查看DP solution,因为这至少可以让您了解如何解决问题(尽管速度很慢)。还有一个approximate解决方案,如果您的号码很小,这将是正确的。 使用此功能。最后,值得指出问题的大小是以位数来衡量的,而不是以集合的大小(或总和)来衡量。
如上所述,这与Knapsack problem。
有关答案 5 :(得分:0)
这个问题几乎就是subset sum problem,它与背包问题有关但不同,并且是NP完整的。但是,如果所有数字都小于某个常数,则存在线性解,并且是多项式时间近似。请参阅上面提到的维基百科页面。
答案 6 :(得分:0)
如果问题要求最小数量的项目,则贪婪的解决方案有效。但是最大(..)函数应考虑到当M为负时,最大值应该将数字的距离返回到0.(abs()的值)。
maximum()函数可以通过二进制堆实现。复杂性是O(n.logn)。
答案 7 :(得分:-1)
贪婪的解决方案可行 伪:
algo(input_set , target_sum, output_set )
if input_set is NULL
error "target_sum can never be reached with all input_set"
return
the_max = maximum(input_set)
remove the_max from input_set
if the_max > target_sum
algo(input_set, target_sum, ouptput_set )
return
add the_max to output_set
algo(input_set, target_sum - the_max, output_set )
使用output_set = NULL调用。 排序intput_set以提高效率。