[英]Recursive function returns duplicates
我已經編寫了以下代碼,它返回使用具有一定硬幣值的貨幣中的硬幣來表示一定金額的所有可能方式:
IEnumerable<IEnumerable<int>> getCoins(int price)
{
int[] coinValues = new int[] { 1, 2, 5, 10, 20, 50, 100, 200 }; // Coin values
if (coinValues.Contains(price)) yield return new int[] { price }; // If the price can be represented be a single coin
// For every coin that is smaller than the price, take it away, call the function recursively and concatenate it later
foreach (int coin in coinValues.Where(x => x < price))
foreach (IEnumerable<int> match in getCoins(price - coin))
yield return match.Concat(new int[] { coin });
}
這樣可以正常工作,但是例如對於price = 3
,它將{1c, 2c}
和{2c, 1c}
視為兩個不同的表示。 可以通過將所有找到的值存儲在List中然后在生成它們時刪除重復項來解決該問題,但這樣就犧牲了代碼的生成器性質。 代碼是否可以修改為不包含重復項,同時仍然是使用yield return
的生成器?
您不能允許任何比陣列中已有的硬幣更大的硬幣。
public IEnumerable<IEnumerable<int>> getCoins(int price)
{
int[] coinValues = new int[] { 1, 2, 5, 10, 20, 50, 100, 200 }; // Coin values
if (coinValues.Contains(price))
yield return new int[] { price }; // If the price can be represented be a single coin
// For every coin that is smaller than the price, take it away, call the function recursively and concatenate it later
foreach (int coin in coinValues.Where(x => x < price))
foreach (IEnumerable<int> match in getCoins(price - coin))
if (match.Min() >= coin)
yield return match.Concat(new int[] { coin });
}
編輯:這具有生成排序數組的額外好處。 但是,列表不是按字典順序生成的。
3結果:
5結果:
問題是,如上所述,您的實施強制執行順序,因此{1c, 2c}
和{2c, 1c}
實際上並不相同。 試圖從外部IEnumerable
刪除它們可能不起作用/將難以工作,相反,您應該嘗試防止它們首先被添加。 您可以通過添加一個檢查來執行此操作,以便您只按降序添加硬幣。 另一個替代方案是使用set操作,我沒有在C#中完成,但是set沒有順序概念,因此如果這兩個值是set,則它們將被視為相等。
正如提到的其他答案,您可以確保只按降序添加硬幣。 這應該可行,但它為最后添加的硬幣添加了額外的參數,而不是使用Min()。
IEnumerable<IEnumerable<int>> getCoins(int price, int prev_coin)
{
int[] coinValues = new int[] { 1, 2, 5, 10, 20, 50, 100, 200 }; // Coin values
if (coinValues.Contains(price)) yield return new int[] { price }; // If the price can be represented be a single coin
// For every coin that is smaller than the price, take it away, call the function recursively and concatenate it later
foreach (int coin in coinValues.Where(x => (x < price && x <= prev_coin)))
foreach (IEnumerable<int> match in getCoins(price - coin, coin))
yield return match.Concat(new int[] { coin });
}
如果硬幣不超過最后添加的硬幣,則只需添加硬幣:
IEnumerable<IEnumerable<int>> getCoins(int price){
return getCoins(price, Int32.MaxValue);
}
IEnumerable<IEnumerable<int>> getCoins(int price, int lastValue)
{
int[] coinValues = new int[] { 1, 2, 5, 10, 20, 50, 100, 200 }; // Coin values
if (coinValues.Contains(price) && price <= lastValue) yield return new int[] { price }; // If the price can be represented be a single coin
// For every coin that is smaller than the price, take it away, call the function recursively and concatenate it later
foreach (int coin in coinValues.Where(x => x < price && x <= lastValue))
foreach (IEnumerable<int> match in getCoins(price - coin, coin))
yield return match.Concat(new int[] { coin });
}
由於它自然排序,因此在我看來對DavidN的輕微改進。 (對不起,我喜歡括號。)
public IEnumerable<IEnumerable<int>> getCoins(int price, int MaxCoin = 200)
{
int[] coinValues = new int[] { 1, 2, 5, 10, 20, 50, 100, 200 }.Reverse().ToArray(); // Coin values
foreach (int coin in coinValues.Where(x => x <= price && x <= MaxCoin))
{
if (coin == price)
{
yield return new int[] { price }; // If the price can be represented be a single coin
}
else
{
foreach (IEnumerable<int> match in getCoins(price - coin, coin))
{
yield return new int[] { coin }.Concat(match);
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.