递归函数返回重复项

时间:2013-05-01 16:34:14

标签: c# recursion

我编写了以下代码,该代码返回使用具有一定硬币值的货币中的硬币来表示一定金额的所有可能方式:

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的生成器?

5 个答案:

答案 0 :(得分:2)

你不能允许任何比阵列中已有的硬币更大的硬币。

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结果:

  • 2 1
  • 1 1 1

5结果:

  • 5
  • 2 1 1 1
  • 1 1 1 1 1
  • 2 2 1

答案 1 :(得分:0)

问题是,如上所述,您的实施会强制执行订单,因此{1c, 2c}{2c, 1c}实际上并不相同。试图从外部IEnumerable删除它们可能无法正常工作/难以开展工作,相反,您应该尝试防止它们首先被添加。您可以通过添加一个检查来执行此操作,以便您只按降序添加硬币。另一个替代方案是使用set操作,我没有在C#中完成,但是set没有顺序概念,所以如果这两个值是set,它们将被认为是相等的。

答案 2 :(得分:0)

如上所述,您可以确保只按降序添加硬币。这应该可行,但它为最后添加的硬币添加了额外的参数,而不是使用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 });
}

答案 3 :(得分:0)

如果硬币不超过最后添加的硬币,则只需添加硬币:

    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 });
    }

答案 4 :(得分:0)

在我看来,对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);
                }
            }
        }
    }