如何确定一组值之和的任何组合是否等于某个值?

时间:2012-01-30 23:57:05

标签: c# algorithm excel-vba vba excel

下面有一组值。我需要知道的是,如果这些值的任何组合总和一定值(在这种情况下为46,134.77)。想出这个的最佳方法是什么?当然,手动操作需要数小时。

如果它返回true,我需要知道组合是什么。我可以在Excel VBA或C#应用程序中进行设置。什么都行不通。我不知道如何到达那里。

 125.00 
 1,000.00 
 1,039.36 
 1,171.60 
 1,200.00 
 1,320.00 
 1,680.00 
 1,757.20 
 1,768.80 
 1,970.00 
 2,231.25 
 2,300.00 
 2,369.25 
 2,589.20 
 2,720.00 
 2,887.50 
 3,000.00 
 3,085.00 
 3,142.60 
 3,174.40 
 3,742.70 
 3,847.20 
 5,609.25 
 5,881.05 
 12,240.48 
 14,112.00 
 29,318.07 
 32,551.80 

5 个答案:

答案 0 :(得分:6)

正如paislee的答案中已经提到的,这是knapsack problem的变体。事实上,这个特定问题被称为subset sum problem,就像背包问题一样,它是NP完全的。

链接的维基百科页面显示了如何使用动态编程来解决问题,但请注意,由于其NP完整性,如果使整数列表过大,则总是很慢/不可能解决。

以下是一些更相关的SO问题:

答案 1 :(得分:3)

这几乎是一个有界knapsack problem,是history中研究得最多的计算问题之一。本地:


NP Complete 意味着您应该准备好编写一个巨型循环来总结每个(或几乎每个)数字组合。

我建议不要手工完成。几个小时是低估。 这将花费你的一生。例如,从28人的游泳池中选择时,有超过4千万个14个数字的组合。(那只是14')。 p>

答案 2 :(得分:3)

您所描述的是Knapsack problem的变体。它的计算难度很难有效地解决,但对于一个小的集合,这是可行的。

此特定输入的确切数字组合为:

29,318.07 + 5,881.05 + 3,174.40 + 3,085.00 + 2,231.25 + 1,320.00 + 1,000.00 + 125.00

我使用以下Perl脚本来确定此解决方案:

sub knapsack {
    my ($target, $path, @vals) = @_;
    if ($target == 0) {
        print "Got it: @$path\n";
        exit;
    }
    while (my $val = pop @vals) {
        next if $val > $target;
        knapsack($target - $val, [@$path, $val], @vals);
    }
}

knapsack(46134_77, [], (
    125_00,  1000_00, 1039_36, 1171_60, 1200_00, 1320_00, 1680_00, 1757_20,
    1768_80, 1970_00, 2231_25, 2300_00, 2369_25, 2589_20, 2720_00, 2887_50,
    3000_00, 3085_00, 3142_60, 3174_40, 3742_70, 3847_20, 5609_25, 5881_05,
    12240_48, 14112_00, 29318_07, 32551_80,
));

请注意,我已将您的十进制值转换为整数(通过将它们全部乘以100),因为浮点比较是一个雷区。 (有关详细信息,请参阅What Every Computer Scientist Should Know About Floating-Point Arithmetic。)

答案 3 :(得分:1)

首先阅读其他答案/评论。这是一个可用于小型数据集的解决方案。

double[] nums = new double[] { 10,20,30,40,50,60,70,80,90,100,150,200,250,300,400,500};

Parallel.ForEach(GetIndexes(nums.Length), list =>
{
    if (list.Select(n => nums[n]).Sum()==350)
    {
        Console.WriteLine(list.Aggregate("", (s, n) => s += nums[n] + " "));
    }
});

IEnumerable<IEnumerable<int>> GetIndexes(int count)
{
    for (ulong l = 0; l < Math.Pow(2, count); l++)
    {
        List<int> list = new List<int>();
        BitArray bits = new BitArray(BitConverter.GetBytes(l));
        for (int i = 0; i < sizeof(ulong)*8; i++)
        {
            if (bits.Get(i)) list.Add(i);
        }
        yield return list;
    }
}

答案 4 :(得分:0)

是的duskwuff是对的。唯一的组合,加起来为46134.77 就是这个:

125 1,000.00 1,320.00 2,231.25 3,085.00 3,174.40 5,881.05 29,318.07

花了2秒才发现。我使用过SumMatch Excel加载项。