簡體   English   中英

如何構建一個算法來查找一個組合,其總和最接近一個數字,其差異在c#的范圍內

[英]How to build an algorithm to find a combination, which summation is nearest to a number and its difference is within a range in c#

我有一個隨機值列表如下

319,4,90,50,20,99,500,59,900

我必須找到一個總和在選定范圍內的值,例如5%到10%。

例如,如果數字是300,范圍是5%到10%

那么差異應該在15到30之間

那么滿足這個條件的列表就是

319 => 319-300 = -19,最接近300,差異在5%到10%范圍內319,4 => 319 + 4 = 323 => 323-300 = -23,最接近300,差異在5%范圍內到10%90,99,97 => 90 + 99 + 95 = 284 => 284-300 = 16,最接近300,差異在5%到10%之間

結果將是
319,
319,4
90,99,95

我已經嘗試修改遞歸算法( 高效算法找到一個組合,其總和等於已知數字,在一組數字中 ),但它只能返回少數匹配的序列而不是全部。

碼:

   public static IEnumerable<string> GetSequence(decimal[] set, decimal? sum, decimal? startPercent, decimal? endPercent, string values = "")
    {            
        for (int i = 0; i < set.Length; i++)
        {
            decimal? left = sum - set[i];
            string vals = set[i] + "," + values;
            if (Math.Abs(decimal.Parse(left.ToString())) >= startPercent && Math.Abs(decimal.Parse(left.ToString())) <= endPercent)
            {
                yield return vals;
            }
            else
            {
                decimal[] possible = set.Take(i).Where(n => n <= sum).ToArray();
                if (possible.Length > 0)
                {
                    foreach (string s in GetSequence(possible, left, startPercent, endPercent, vals))
                    {
                        yield return s;
                    }
                }
            }
        }
    }

任何人都可以幫我這個。

可能更好的方法是使用如下代碼生成所有可能的組合:

public static IEnumerable<IEnumerable<T>> Combinations<T>(IList<T> items)
{
    return Combinations(items.Count).Select(comb => comb.Select(index => items[index]));
}

public static IEnumerable<IEnumerable<int>> Combinations(int n)
{
    long m = 1 << n;

    for (long i = 1; i < m; ++i)
        yield return bitIndices((uint)i);
}

static IEnumerable<int> bitIndices(uint n)
{
    uint mask = 1;

    for (int bit = 0; bit < 32; ++bit, mask <<= 1)
        if ((n & mask) != 0)
            yield return bit;
}

然后你可以編寫一個方法來總結每個可能的組合:

static IEnumerable<(int Sum, List<int> Values)> SummedCombinations(IList<int> values)
{
    return 
        Combinations(values)
        .Select(comb => comb.ToList())
        .Select(comb => (comb.Sum(), comb));
}

然后,您可以編寫一個方法來查找總和與您要查找的范圍匹配的所有組合:

static IEnumerable<List<int>> FindMatches(IList<int> values, int target, int toleranceLow, int toleranceHigh)
{
    int minDiff = (target * toleranceLow)  / 100;
    int maxDiff = (target * toleranceHigh) / 100;

    foreach (var sum in SummedCombinations(values))
    {
        int diff = Math.Abs(sum.Sum - target);

        if (minDiff <= diff && diff <= maxDiff)
            yield return sum.Values;
    }
}

將這些全部放在一個可編輯的控制台應用程序中:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            int[] values = {319, 4, 90, 50, 20, 99, 500, 95, 900};

            foreach (var combination in FindMatches(values, 300, 5, 10))
            {
                Console.WriteLine(string.Join(", ", combination));
            }
        }

        static IEnumerable<List<int>> FindMatches(IList<int> values, int target, int toleranceLow, int toleranceHigh)
        {
            int minDiff = (target * toleranceLow)  / 100;
            int maxDiff = (target * toleranceHigh) / 100;

            foreach (var sum in SummedCombinations(values))
            {
                int diff = Math.Abs(sum.Sum - target);

                if (minDiff <= diff && diff <= maxDiff)
                    yield return sum.Values;
            }
        }

        static IEnumerable<(int Sum, List<int> Values)> SummedCombinations(IList<int> values)
        {
            return 
                Combinations(values)
                .Select(comb => comb.ToList())
                .Select(comb => (comb.Sum(), comb));
        }

        public static IEnumerable<IEnumerable<T>> Combinations<T>(IList<T> items)
        {
            return Combinations(items.Count).Select(comb => comb.Select(index => items[index]));
        }

        public static IEnumerable<IEnumerable<int>> Combinations(int n)
        {
            long m = 1 << n;

            for (long i = 1; i < m; ++i)
                yield return bitIndices((uint)i);
        }

        static IEnumerable<int> bitIndices(uint n)
        {
            uint mask = 1;

            for (int bit = 0; bit < 32; ++bit, mask <<= 1)
                if ((n & mask) != 0)
                    yield return bit;
        }
    }
}

這輸出:

319
319, 4
90, 99, 95

這是您的預期產量。


注意:上面的代碼使用的是C#7元組 - 如果您使用的是早期版本,則必須將FindMatches()SummedCombinations()更改為:

static IEnumerable<List<int>> FindMatches(IList<int> values, int target, int toleranceLow, int toleranceHigh)
{
    int minDiff = (target * toleranceLow)  / 100;
    int maxDiff = (target * toleranceHigh) / 100;

    foreach (var sum in SummedCombinations(values))
    {
        int diff = Math.Abs(sum.Item1 - target);

        if (minDiff <= diff && diff <= maxDiff)
            yield return sum.Item2;
    }
}

static IEnumerable<Tuple<int, List<int>>> SummedCombinations(IList<int> values)
{
    return 
        Combinations(values)
        .Select(comb => comb.ToList())
        .Select(comb => Tuple.Create(comb.Sum(), comb));
}

組合部分的說明

組合工作如下:

  • 從1到2 ^ N-1中選擇i ,其中N是要組合的項目數。
  • 對於i設置的每個位,從輸入值中的相應位置返回項。

例如,如果你有3個值; A,B和C:

i將從1到(2 ^ 3-1)= 7。

查看我們將獲得的1..7的二進制值,並查看A,B,C輸入的相應元素:

C B A (Input)
2 1 0 (Bit number, i.e. power of two)
---------------------------------------
0 0 1 [1] = A
0 1 0 [2] = B
0 1 1 [3] = A B
1 0 0 [4] = C
1 0 1 [5] = A C
1 1 0 [5] = B C
1 1 1 [6] = A B C

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM