我有一個問題,我需要使用C#來解決。有一個十進制數字的數組(表示倉庫在不同時間收到的物料的數量)。該數組已按照接收數量的順序排序。我需要能夠找到總計達到指定總量的最早數量組合。例如,假設我有一些按時間順序排列的數量如下[13,6,9,8,23,18,4],並說我的總量匹配是23.那麼我應該能夠得到[13,6,4]作爲匹配的子集,儘管[6,9,8]和[23]也是匹配的,但不是最早的。查找加起來給定數字的數字的子集
什麼是最好的方法/算法呢?
我到目前爲止想出了一個相當天真的方法使用遞歸。
public class MatchSubset
{
private decimal[] qty = null;
private decimal matchSum = 0;
public int operations = 0;
public int[] matchedIndices = null;
public int matchCount = 0;
private bool SumUp(int i, int n, decimal sum)
{
operations++;
matchedIndices[matchCount++] = i;
sum += qty[i];
if (sum == matchSum)
return true;
if (i >= n - 1)
{
matchCount--;
return false;
}
if (SumUp(i + 1, n, sum))
return true;
sum -= qty[i];
matchCount--;
return SumUp(i + 1, n, sum);
}
public bool Match(decimal[] qty, decimal matchSum)
{
this.qty = qty;
this.matchSum = matchSum;
matchCount = 0;
matchedIndices = new int[qty.Count()];
return SumUp(0, qty.Count(), 0);
}
}
static void Main(string[] args)
{
var match = new MatchSubset();
int maxQtys = 20;
Random rand = new Random(DateTime.Now.Millisecond);
decimal[] qty = new decimal[maxQtys];
for (int i = 0; i < maxQtys - 2; i++)
qty[i] = rand.Next(1, 500);
qty[maxQtys - 2] = 99910;
qty[maxQtys - 1] = 77910;
DateTime t1 = DateTime.Now;
if (match.Match(qty, 177820))
{
Console.WriteLine(DateTime.Now.Subtract(t1).TotalMilliseconds);
Console.WriteLine("Operations: " + match.operations);
for (int i = 0; i < match.matchCount; i++)
{
Console.WriteLine(match.matchedIndices[i]);
}
}
}
匹配子集可以短至一個元件,並且只要原始集(包含所有元件)。但爲了測試最糟糕的情況,在我的測試程序中,我使用了一個任意長的集合,其中只有最後兩個匹配給定的數字。
我看到在集合中有20個數字,它會調用遞歸函數超過一百萬次,最大遞歸深度爲20位。如果我在生產中碰到一組30個或更多數字,我擔心它會消耗很長時間。
有沒有辦法進一步優化呢?另外,看着降價,這是否是這樣的問題的錯誤地點?
https://en.wikipedia.org/wiki/Knapsack_problem –
謝謝阿列克謝。在發佈這個問題之前,我看到子集總和文章https://en.wikipedia.org/wiki/Subset_sum_problem,但我的主要問題是我無法重新排列數字。我必須保持原始順序並選擇最早的子集。 – spatel
@spatel這個問題是題外話 - 太廣泛。即使對於這個問題沒有太多的算法,「最好的」也沒有定義,沒有任何標準給出,肯定有很多可能的實現。請閱讀[幫助]。 – BartoszKP