簡體   English   中英

組合算法

[英]Combination Algorithm

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

程序需要一個長值,對於那個特定的長值,我們必須從上面的列表中找到可能的組合,添加后給我一個輸入結果(相同的值可以使用兩次)。 可能會有+/- 30的差異。

必須使用最大的數字。

例如:Length = 868對於此組合可以是

組合1 = 618 + 250

組合2 = 308 + 232 + 200 +128

正確的組合將是組合1

但是也應該有不同的組合。

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

我已經使用了上面的代碼,是否可以更簡化,在這里我也得到唯一的值。 值可以多次使用。 但是必須將最大的數字放在首位。

我有一個驗證來檢查總和是否大於輸入值。 即使在那里,邏輯也會失敗。

您顯示的算法假設列表按升序排序。 如果不是,則必須首先以O(nlogn)時間對列表進行排序,然后執行算法。

同樣,它假定您僅考慮對的組合,並且在第一個匹配項時退出。 如果要查找所有組合,則只需輸出組合並遞增startIndex或遞減endIndex即可,而不是“ break”。

此外,您應該檢查范圍(targetSum-30到targetSum + 30),而不是僅檢查精確值,因為問題表明允許誤差范圍。

對我來說,這是最好的解決方案,因為它的復雜度為O(nlogn + n),包括排序。

V4-遞歸方法,在線程上使用堆棧結構而不是堆棧框架

它可以工作(在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();
            }
        }

    }

也許是V3-建議嘗試所有組合的有序解決方案

盡管未選擇此答案作為相關問題的答案,但我相信這是一個好方法-https: //stackoverflow.com/a/17258033/887092 (否則,您可以嘗試選擇的答案(盡管該輸出集合中只有2個項被求和,而不是n個項))-它將枚舉每個選項,包括相同值的倍數。 V2可以運行,但是效率要比有序解決方案低一點,因為相同的失敗嘗試可能會嘗試多次。

V2-隨機選擇-能夠重復使用相同的數字兩次

我喜歡將隨機變量用於“智能”,以使計算機強力解決該問題。 它也很容易分發-例如,同時嘗試的兩個線程之間沒有狀態相關性。

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-有序選擇-無法重復使用相同的號碼兩次

  1. 添加此非常方便的擴展功能(使用二進制算法查找所有組合):

     //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. 使用功能

      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. 測試解決方案

     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