Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 找出集合中的哪些数字组合加起来等于给定的总数_Algorithm_Math_Nested Loops_Accounting - Fatal编程技术网

Algorithm 找出集合中的哪些数字组合加起来等于给定的总数

Algorithm 找出集合中的哪些数字组合加起来等于给定的总数,algorithm,math,nested-loops,accounting,Algorithm,Math,Nested Loops,Accounting,我的任务是帮助一些会计师解决他们遇到的一个常见问题——给出交易清单和总存款,哪些交易是存款的一部分?例如,假设我有一个数字列表: 1.00 2.50 3.75 8.00 我知道我的存款总额是10.50,我很容易看出它是由8.00和2.50交易组成的。然而,考虑到100笔交易和数百万美元的存款,这很快就会变得更加困难 在测试蛮力解决方案(这需要很长时间才能实现)时,我有两个问题: 有了一个大约60个数字的列表,似乎可以为任何合理的总数找到十几个或更多的组合我期望一个组合可以满足我的总体需求,或者

我的任务是帮助一些会计师解决他们遇到的一个常见问题——给出交易清单和总存款,哪些交易是存款的一部分?例如,假设我有一个数字列表:

1.00
2.50
3.75
8.00
我知道我的存款总额是
10.50
,我很容易看出它是由
8.00
2.50
交易组成的。然而,考虑到100笔交易和数百万美元的存款,这很快就会变得更加困难

在测试蛮力解决方案(这需要很长时间才能实现)时,我有两个问题:

  • 有了一个大约60个数字的列表,似乎可以为任何合理的总数找到十几个或更多的组合我期望一个组合可以满足我的总体需求,或者可能满足一些可能性,但似乎总是有很多组合。是否有一个数学原理来描述这一点?似乎给定一个中等大小的随机数集合,您可以找到一个倍数组合,其总和几乎等于您想要的总和

  • 我为这个问题建立了一个蛮力解决方案,但它显然是O(n!),并且很快就失控了。除了明显的快捷方式(不包括大于总数的数字),还有没有办法缩短计算时间

  • 有关我当前(超慢)解决方案的详细信息:

    明细金额列表从大到小排序,然后递归运行以下过程:

    • 拿下列表中的下一项,看看把它加到你的跑步总量中是否会使你的总量与目标相符。如果是,则将当前链作为匹配项放在一边。如果它没有达到您的目标,请将其添加到您的跑步总数中,将其从明细金额列表中删除,然后再次调用此流程
    <>这个方法很快排除了更大的数字,把列表切割成只需要考虑的数字。然而,它仍然是n!而且更大的列表似乎永远不会结束,所以我对任何可以加快速度的捷径都感兴趣——我怀疑即使从列表中删除1个数字也会将计算时间减少一半


    谢谢你的帮助

    它是一类类似于0-1背包问题的NP完全问题,可以通过动态规划在多项式时间内求解


    但在算法结束时,您还需要检查总和是否符合您的要求。

    如果我正确理解您的问题,您有一组事务,您只想知道其中哪些可以包含在给定的总数中。因此,如果有4个可能的事务,那么有2^4=16个可能的集合需要检查。这个问题是,对于100个可能的事务,搜索空间有2^100=1267650600228229401496703205376个可能的组合要搜索。对于组合中的1000个潜在交易,它将增长到

    1071508607186267320948425049060001181051404811705536074437503883703511249361224931983788156955812759467291755314682587145285692314043598457746985748574803345674824230985421074605623711418754182153647498358194126739876761655946077069145711964776767676766042316526286767656680376

    必须测试的设置。暴力很难解决这些问题


    相反,使用能够处理问题的解算器。但即使如此,我也不确定你是否能在不使用蛮力的情况下生成所有可能解决方案的完整枚举。

    背包问题的这种特殊情况称为。

    根据你的数据,你可以首先查看每个事务的美分部分。就像在您最初的示例中一样,您知道2.50必须是总数的一部分,因为它是唯一一组增加到50的非零分交易。

    C#version

    设置测试:

    using System;
    using System.Collections.Generic;
    
    public class Program
    {
        public static void Main(string[] args)
        {
        // subtotal list
        List<double> totals = new List<double>(new double[] { 1, -1, 18, 23, 3.50, 8, 70, 99.50, 87, 22, 4, 4, 100.50, 120, 27, 101.50, 100.50 });
    
        // get matches
        List<double[]> results = Knapsack.MatchTotal(100.50, totals);
    
        // print results
        foreach (var result in results)
        {
            Console.WriteLine(string.Join(",", result));
        }
    
        Console.WriteLine("Done.");
        Console.ReadKey();
        }
    }
    

    如果重复小计,则会出现重复结果(预期效果)。实际上,您可能希望使用带有某个ID的小计元组,以便将其与数据关联起来。

    有一个便宜的Excel加载项可以解决此问题:


    不是一个超级高效的解决方案,但这里有一个coffeescript实现

    组合
    返回
    列表中元素的所有可能组合

    combinations = (list) ->
            permuations = Math.pow(2, list.length) - 1
            out = []
            combinations = []
    
            while permuations
                out = []
    
                for i in [0..list.length]
                    y = ( 1 << i )
                    if( y & permuations and (y isnt permuations))
                        out.push(list[i])
    
                if out.length <= list.length and out.length > 0
                    combinations.push(out)
    
                permuations--
    
            return combinations
    
    这里有一个例子

    list = [7.2, 3.3, 4.5, 6.0, 2, 4.1]
    total = 7.2 + 2 + 4.1
    
    console.log(find_components(total, list)) 
    

    返回
    [7.2,2,4.1]

    超级用户网站上发布的Excel Solver插件有一个很好的解决方案(如果您有Excel)

    查看背包问题。除非您有更多信息,否则将有多种解决方案。
    double
    有64位,并且您的数字来自其中的一小部分,因此在60个元素中有许多解决方案,有2^60个子集的和映射到double。这正是它的本质,在阅读了关于它的维基文章之后。似乎已经有一些方法比我现在做的更有效,但我希望有一些捷径可以让我更节省搜索空间-也许可以识别哪些数字与集合中的其他数字相加,然后避免重复检查它们。谢谢你的链接!可能是数学上的一个小问题。。。给定一组4个金额,我们需要对包含1、2、3和4个金额的所有子集求和。这是C(4,r)的和,其中r从1到4变化。即C(4,1)+C(4,2)+C(4,3)+C(4,4)=(4)+(6)+(4)+(1)=152^4。无论如何,这仍然会导致暴力解决方案的失败。唯一的另一个子集是零情况,在零情况下,根本不使用任何事务。如果从搜索中排除该情况,那么在集合中只有1个元素更少,对于2 ^ n-1个集合要考虑。对于大n来说,这个差别对我来说似乎微不足道
    combinations = (list) ->
            permuations = Math.pow(2, list.length) - 1
            out = []
            combinations = []
    
            while permuations
                out = []
    
                for i in [0..list.length]
                    y = ( 1 << i )
                    if( y & permuations and (y isnt permuations))
                        out.push(list[i])
    
                if out.length <= list.length and out.length > 0
                    combinations.push(out)
    
                permuations--
    
            return combinations
    
    find_components = (total, list) ->
        # given a list that is assumed to have only unique elements
    
            list_combinations = combinations(list)
    
            for combination in list_combinations
                sum = 0
                for number in combination
                    sum += number
    
                if sum is total
                    return combination
            return []
    
    list = [7.2, 3.3, 4.5, 6.0, 2, 4.1]
    total = 7.2 + 2 + 4.1
    
    console.log(find_components(total, list))