【问题标题】:Recursive function returns duplicates递归函数返回重复项
【发布时间】:2013-05-02 02:31:29
【问题描述】:

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

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 将代码修改为不包含重复项吗?

【问题讨论】:

  • 你应该从生成排序数组开始。
  • 顺便说一句,我建议将 coinValues 移动到静态只读数组,而不是在每次调用 getCoins 时重新创建它。

标签: c# recursion


【解决方案1】:

您不能允许任何大于数组中已有的硬币。

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

【讨论】:

  • 只是指出您在1111 错过了1,应该是11111,但我们知道它有效;)。
【解决方案2】:

正如提到的其他答案,您可以确保仅按降序添加硬币。这应该可行,但它为最后添加的硬币添加了一个额外的参数,而不是使用 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 });
}

【讨论】:

  • 此答案为 getCoins(3, 200) 生成了 {2,1}、{1,1,1} 和 {1,2} 的错误结果。它没有在函数第二行的 if 语句中对 price
【解决方案3】:

问题是,如前所述,您的实现强制执行顺序,因此 {1c, 2c}{2c, 1c} 实际上并不相等。试图从外部IEnumerable 中删除它们可能不会起作用/很难起作用,相反,您应该首先尝试阻止它们被添加。您可以通过添加支票来做到这一点,以便您只按降序添加硬币。另一种选择是使用我在 C# 中没有做过的集合操作,但是集合没有顺序的概念,所以如果这两个值是集合,它们将被认为是相等的。

【讨论】:

    【解决方案4】:

    您只需要添加不大于最后添加的硬币的硬币:

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 2016-02-01
        • 1970-01-01
        • 2020-08-24
        • 2022-06-29
        • 2017-06-19
        • 1970-01-01
        • 2022-08-13
        • 2013-10-13
        • 2021-09-28
        相关资源
        最近更新 更多