[英]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#
I have a list of random values as below 我有一个随机值列表如下
319, 4, 90, 50, 20, 99, 500, 95, 900 319,4,90,50,20,99,500,59,900
and i have to find a values which sum is within selected range say 5% to 10%. 我必须找到一个总和在选定范围内的值,例如5%到10%。
for example if the number is 300 and the range was 5% to 10% 例如,如果数字是300,范围是5%到10%
then the difference should be in the range 15 to 30 那么差异应该在15到30之间
then the list which satisfy this condition is 那么满足这个条件的列表就是
319 => 319-300=-19 which nearest to 300 and difference within range 5% to 10% 319,4 => 319+4=323 => 323-300=-23 which nearest to 300 and difference within range 5% to 10% 90,99,97 => 90+99+95=284 => 284-300=16 which nearest to 300 and difference within range 5% to 10% 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%之间
the result will be 结果将是
319, 319,
319,4 319,4
90,99,95 90,99,95
i have tried with modifying the recursive algorithm ( Efficient algorithm to find a combination, which summation is equal to a known number, in a set of number ) but it is able to return only few matched sequence and not all. 我已经尝试修改递归算法( 高效算法找到一个组合,其总和等于已知数字,在一组数字中 ),但它只能返回少数匹配的序列而不是全部。
Code: 码:
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;
}
}
}
}
}
Could anybody help me with this. 任何人都可以帮我这个。
Possibly a better approach is to generate all possible combinations using code like so: 可能更好的方法是使用如下代码生成所有可能的组合:
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;
}
Then you can write a method to sum each possible combination: 然后你可以编写一个方法来总结每个可能的组合:
static IEnumerable<(int Sum, List<int> Values)> SummedCombinations(IList<int> values)
{
return
Combinations(values)
.Select(comb => comb.ToList())
.Select(comb => (comb.Sum(), comb));
}
Then you can write a method to find all the combinations where the sum matches the range you're looking for: 然后,您可以编写一个方法来查找总和与您要查找的范围匹配的所有组合:
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;
}
}
Putting this all together into a compilable console app: 将这些全部放在一个可编辑的控制台应用程序中:
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;
}
}
}
This outputs: 这输出:
319
319, 4
90, 99, 95
Which is your expected output. 这是您的预期产量。
Note: The code above is using C# 7 tuples - if you are using an earlier version you'll have to change FindMatches()
and SummedCombinations()
to: 注意:上面的代码使用的是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));
}
Explanation of the Combinations part 组合部分的说明
The combinations work as follows: 组合工作如下:
i
from 1 to 2^N-1 where N is the number of items to combine. 从1到2 ^ N-1中选择i
,其中N是要组合的项目数。 i
, return the item from the corresponding location in the input values. 对于i
设置的每个位,从输入值中的相应位置返回项。 So for example if you have 3 values; 例如,如果你有3个值; A, B and C: A,B和C:
i
will go from 1 to (2^3-1) = 7. i
将从1到(2 ^ 3-1)= 7。
Look at the binary values we will get for 1..7, and look at the corresponding elements of the A, B, C input: 查看我们将获得的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.