简体   繁体   English

组合算法

[英]Combination Algorithm

 Length = input Long(can be 2550, 2880, 2568, etc)
 List<long> = {618, 350, 308, 300, 250, 232, 200, 128}

The program takes a long value, for that particular long value we have to find the possible combination from the above list which when added give me a input result(same value can be used twice). 程序需要一个长值,对于那个特定的长值,我们必须从上面的列表中找到可能的组合,添加后给我一个输入结果(相同的值可以使用两次)。 There can be a difference of +/- 30. 可能会有+/- 30的差异。

Largest numbers have to be used most. 必须使用最大的数字。

Ex:Length = 868 For this combinations can be 例如:Length = 868对于此组合可以是

Combination 1 = 618 + 250 组合1 = 618 + 250

Combination 2 = 308 + 232 + 200 +128 组合2 = 308 + 232 + 200 +128

Correct Combination would be Combination 1 正确的组合将是组合1

But there should also be different combinations. 但是也应该有不同的组合。

public static void Main(string[] args)
    {
        //subtotal list
        List<int> totals = new List<int>(new int[] { 618, 350, 308, 300, 250, 232, 200, 128 });

        // get matches
        List<int[]> results = KnapSack.MatchTotal(2682, totals);

        // print results
        foreach (var result in results)
        {
            Console.WriteLine(string.Join(",", result));
        }

        Console.WriteLine("Done.");            
    }

internal static List<int[]> MatchTotal(int theTotal, List<int> subTotals)
    {
        List<int[]> results = new List<int[]>();
        while (subTotals.Contains(theTotal))
        {
            results.Add(new int[1] { theTotal });
            subTotals.Remove(theTotal);
        }

        if (subTotals.Count == 0)
            return results;

        subTotals.Sort();

        double mostNegativeNumber = subTotals[0];
        if (mostNegativeNumber > 0)
            mostNegativeNumber = 0;

        if (mostNegativeNumber == 0)
            subTotals.RemoveAll(d => d > theTotal);

        for (int choose = 0; choose <= subTotals.Count; choose++)
        {
            IEnumerable<IEnumerable<int>> combos = Combination.Combinations(subTotals.AsEnumerable(), choose);

            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 ?
                new[] { new T[0] } :
                elements.SelectMany((element, i) =>
                    elements.Skip(i + 1).Combinations(choose - 1).Select(combo => (new[] { element }).Concat(combo)));
        }
}

I Have used the above code, can it be more simplified, Again here also i get unique values. 我已经使用了上面的代码,是否可以更简化,在这里我也得到唯一的值。 A value can be used any number of times. 值可以多次使用。 But the largest number has to be given the most priority. 但是必须将最大的数字放在首位。

I have a validation to check whether the total of the sum is greater than the input value. 我有一个验证来检查总和是否大于输入值。 The logic fails even there.. 即使在那里,逻辑也会失败。

The algorithm you have shown assumes that the list is sorted in ascending order. 您显示的算法假设列表按升序排序。 If not, then you shall first have to sort the list in O(nlogn) time and then execute the algorithm. 如果不是,则必须首先以O(nlogn)时间对列表进行排序,然后执行算法。

Also, it assumes that you are only considering combinations of pairs and you exit on the first match. 同样,它假定您仅考虑对的组合,并且在第一个匹配项时退出。 If you want to find all combinations, then instead of "break", just output the combination and increment startIndex or decrement endIndex. 如果要查找所有组合,则只需输出组合并递增startIndex或递减endIndex即可,而不是“ break”。

Moreover, you should check for ranges (targetSum - 30 to targetSum + 30) rather than just the exact value because the problem says that a margin of error is allowed. 此外,您应该检查范围(targetSum-30到targetSum + 30),而不是仅检查精确值,因为问题表明允许误差范围。

This is the best solution according to me because its complexity is O(nlogn + n) including the sorting. 对我来说,这是最好的解决方案,因为它的复杂度为O(nlogn + n),包括排序。

V4 - Recursive Method, using Stack structure instead of stack frames on thread V4-递归方法,在线程上使用堆栈结构而不是堆栈框架

It works (tested in VS), but there could be some bugs remaining. 它可以工作(在VS中测试),但是可能还存在一些错误。

    static int Threshold = 30;
    private static Stack<long> RecursiveMethod(long target)
    {
        Stack<long> Combination = new Stack<long>(establishedValues.Count); //Can grow bigger, as big as (target / min(establishedValues)) values
        Stack<int> Index = new Stack<int>(establishedValues.Count); //Can grow bigger
        int lowerBound = 0;
        int dimensionIndex = lowerBound;
        long fail = -1 * Threshold;
        while (true)
        {
            long thisVal = establishedValues[dimensionIndex];
            dimensionIndex++;
            long afterApplied = target - thisVal;

            if (afterApplied < fail)
                lowerBound = dimensionIndex;
            else
            {
                target = afterApplied;
                Combination.Push(thisVal);
                if (target <= Threshold)
                    return Combination;
                Index.Push(dimensionIndex);
                dimensionIndex = lowerBound;
            }

            if (dimensionIndex >= establishedValues.Count)
            {
                if (Index.Count == 0)
                    return null; //No possible combinations

                dimensionIndex = Index.Pop();
                lowerBound = dimensionIndex;
                target += Combination.Pop();
            }
        }

    }

Maybe V3 - Suggestion for Ordered solution trying every combination 也许是V3-建议尝试所有组合的有序解决方案

Although this isn't chosen as the answer for the related question, I believe this is a good approach - https://stackoverflow.com/a/17258033/887092 (, otherwise you could try the chosen answer (although the output for that is only 2 items in set being summed, rather than up to n items)) - it will enumerate every option including multiples of the same value. 尽管未选择此答案作为相关问题的答案,但我相信这是一个好方法-https: //stackoverflow.com/a/17258033/887092 (否则,您可以尝试选择的答案(尽管该输出集合中只有2个项被求和,而不是n个项))-它将枚举每个选项,包括相同值的倍数。 V2 works but would be slightly less efficient than an ordered solution, as the same failing-attempt will likely be attempted multiple times. V2可以运行,但是效率要比有序解决方案低一点,因为相同的失败尝试可能会尝试多次。

V2 - Random Selection - Will be able to reuse the same number twice V2-随机选择-能够重复使用相同的数字两次

I'm a fan of using random for "intelligence", allowing the computer to brute force the solution. 我喜欢将随机变量用于“智能”,以使计算机强力解决该问题。 It's also easy to distribute - as there is no state dependence between two threads trying at the same time for example. 它也很容易分发-例如,同时尝试的两个线程之间没有状态相关性。

static int Threshold = 30;

    public static List<long> RandomMethod(long Target)
    {
        List<long> Combinations = new List<long>();
        Random rnd = new Random();

        //Assuming establishedValues is sorted
        int LowerBound = 0;
        long runningSum = Target;

        while (true)
        {
            int newLowerBound = FindLowerBound(LowerBound, runningSum);

            if (newLowerBound == -1)
            {
                //No more beneficial values to work with, reset
                runningSum = Target;
                Combinations.Clear();
                LowerBound = 0;
                continue;
            }


            LowerBound = newLowerBound;

            int rIndex = rnd.Next(LowerBound, establishedValues.Count);
            long val = establishedValues[rIndex];
            runningSum -= val;
            Combinations.Add(val);

            if (Math.Abs(runningSum) <= 30)
                return Combinations;
        }
    }

    static int FindLowerBound(int currentLowerBound, long runningSum)
    {
        //Adjust lower bound, so we're not randomly trying a number that's too high
        for (int i = currentLowerBound; i < establishedValues.Count; i++)
        {
            //Factor in the threshold, because an end aggregate which exceeds by 20 is better than underperforming by 21.
            if ((establishedValues[i] - Threshold) < runningSum)
            {
                return i;

            }
        }
        return -1;
    }

V1 - Ordered selection - Will not be able to reuse the same number twice V1-有序选择-无法重复使用相同的号码两次

  1. Add this very handy extension function (uses a binary algorithm to find all combinations): 添加此非常方便的扩展功能(使用二进制算法查找所有组合):

     //Make sure you put this in a static class inside System namespace public static IEnumerable<List<T>> EachCombination<T>(this List<T> allValues) { var collection = new List<List<T>>(); for (int counter = 0; counter < (1 << allValues.Count); ++counter) { List<T> combination = new List<T>(); for (int i = 0; i < allValues.Count; ++i) { if ((counter & (1 << i)) == 0) combination.Add(allValues[i]); } if (combination.Count == 0) continue; yield return combination; } } 
  2. Use the function 使用功能

      static List<long> establishedValues = new List<long>() {618, 350, 308, 300, 250, 232, 200, 128, 180, 118, 155}; //Return is a list of the values which sum to equal the target. Null if not found. List<long> FindFirstCombination(long target) { foreach (var combination in establishedValues.EachCombination()) { //if (combination.Sum() == target) if (Math.Abs(combination.Sum() - target) <= 30) //Plus or minus tolerance for difference return combination; } return null; //Or you could throw an exception } 
  3. Test the solution 测试解决方案

     var target = 858; var result = FindFirstCombination(target); bool success = (result != null && result.Sum() == target); //TODO: for loop with random selection of numbers from the establishedValues, Sum and test through FindFirstCombination 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM