[英]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-有序選擇-無法重復使用相同的號碼兩次
添加此非常方便的擴展功能(使用二進制算法查找所有組合):
//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; } }
使用功能
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 }
測試解決方案
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.