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

后端 未结 8 1142
旧时难觅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条回答
  •  萌比男神i
    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 totals = new List(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 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 MatchTotal(double theTotal, List subTotals)
        {
        List results = new List();
    
        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> 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> Combinations(this IEnumerable 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.

提交回复
热议问题