Find out which combinations of numbers in a set add up to a given total

后端 未结 8 1110
旧时难觅i
旧时难觅i 2020-12-01 08:11

I\'ve been tasked with helping some accountants solve a common problem they have - given a list of transactions and a total deposit, which transactions are part of the depos

相关标签:
8条回答
  • 2020-12-01 08:36

    There is a cheap Excel Add-in that solves this problem: SumMatch

    SumMatch in action

    0 讨论(0)
  • 2020-12-01 08:38

    C# version

    setup test:

    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();
        }
    }
    

    code:

    using System.Collections.Generic;
    using System.Linq;
    
    public class Knapsack
    {
        internal static List<double[]> MatchTotal(double theTotal, List<double> subTotals)
        {
        List<double[]> results = new List<double[]>();
    
        while (subTotals.Contains(theTotal))
        {
            results.Add(new double[1] { theTotal });
            subTotals.Remove(theTotal);
        }
    
        // if no subtotals were passed
        // or all matched the Total
        // return
        if (subTotals.Count == 0)
            return results;
    
        subTotals.Sort();
    
        double mostNegativeNumber = subTotals[0];
        if (mostNegativeNumber > 0)
            mostNegativeNumber = 0;
    
        // if there aren't any negative values
        // we can remove any values bigger than the total
        if (mostNegativeNumber == 0)
            subTotals.RemoveAll(d => d > theTotal);
    
        // if there aren't any negative values
        // and sum is less than the total no need to look further
        if (mostNegativeNumber == 0 && subTotals.Sum() < theTotal)
            return results;
    
        // get the combinations for the remaining subTotals
        // skip 1 since we already removed subTotals that match
        for (int choose = 2; choose <= subTotals.Count; choose++)
        {
            // get combinations for each length
            IEnumerable<IEnumerable<double>> combos = Combination.Combinations(subTotals.AsEnumerable(), choose);
    
            // add combinations where the sum mathces the total to the result list
            results.AddRange(from combo in combos
                     where combo.Sum() == theTotal
                     select combo.ToArray());
        }
    
        return results;
        }
    }
    
    public static class Combination
    {
        public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int choose)
        {
        return choose == 0 ?                        // if choose = 0
            new[] { new T[0] } :                    // return empty Type array
            elements.SelectMany((element, i) =>     // else recursively iterate over array to create combinations
            elements.Skip(i + 1).Combinations(choose - 1).Select(combo => (new[] { element }).Concat(combo)));
        }
    }
    

    results:

    100.5
    100.5
    -1,101.5
    1,99.5
    3.5,27,70
    3.5,4,23,70
    3.5,4,23,70
    -1,1,3.5,27,70
    1,3.5,4,22,70
    1,3.5,4,22,70
    1,3.5,8,18,70
    -1,1,3.5,4,23,70
    -1,1,3.5,4,23,70
    1,3.5,4,4,18,70
    -1,3.5,8,18,22,23,27
    -1,3.5,4,4,18,22,23,27
    Done.
    

    If subTotals are repeated, there will appear to be duplicate results (the desired effect). In reality, you will probably want to use the subTotal Tupled with some ID, so you can relate it back to your data.

    0 讨论(0)
  • 2020-12-01 08:44

    The Excel Solver Addin as posted over on superuser.com has a great solution (if you have Excel) https://superuser.com/questions/204925/excel-find-a-subset-of-numbers-that-add-to-a-given-total

    0 讨论(0)
  • 2020-12-01 08:44

    Its kind of like 0-1 Knapsack problem which is NP-complete and can be solved through dynamic programming in polynomial time.

    http://en.wikipedia.org/wiki/Knapsack_problem

    But at the end of the algorithm you also need to check that the sum is what you wanted.

    0 讨论(0)
  • 2020-12-01 08:45

    Depending on your data you could first look at the cents portion of each transaction. Like in your initial example you know that 2.50 has to be part of the total because it is the only set of non-zero cent transactions which add to 50.

    0 讨论(0)
  • 2020-12-01 08:51

    This special case of the Knapsack problem is called Subset Sum.

    0 讨论(0)
提交回复
热议问题