【问题标题】:Can someone abuse LINQ and solve this money puzzle?有人可以滥用 LINQ 并解决这个金钱难题吗?
【发布时间】:2009-08-29 22:04:51
【问题描述】:

为了好玩,我希望看到有人使用和滥用 LINQ 来解决这个金钱问题。 我真的不知道你会怎么做 - 我想填充一些集合然后选择它。

如果给定硬币的总数并给出所有硬币的总和: 显示所有可能的硬币组合。硬币有夸特 (.25)、角钱 (.10) 镍 (.05) 和便士 (.01)

包含该选项,以便一种硬币可以有零个,或者每种硬币必须至少有一个。

示例问题:如果我有 19 个硬币,硬币加起来是 1.56 美元,那么每种硬币必须至少有 1 个。

答案是:

1 夸特,9 硬币,8 镍币,1 便士

2 夸特,5 个硬币,11 个镍币,1 个便士

2 夸特,9 角钱,2 镍币,6 便士

3 夸特,1 硬币,14 镍,1 便士

3 夸特,5 个硬币,5 个镍币,6 个便士

4 夸特,1 硬币,8 镍币,6 便士

5 夸特,1 硬币,2 镍币,11 便士

如果我们允许硬币为零,我们允许获得额外的硬币 0 夸特,13 硬币,5 镍币,1 便士

这里是一些示例 C# 代码,使用蛮力方法来解决问题。 不用费心改进示例,让我们看看使用 Linq 的解决方案。 //如果可能,尽量不要使用任何常规的 c# 循环代码。

private void SolveCoinProblem(int totalNumberOfCoins, double totalAmount, int minimumNumberOfEachCoin)
    {
        int foundCount = 0;
        long numberOfTries = 0;
        Console.WriteLine(String.Format("Solving Coin Problem:TotalNumberOfCoins={0}TotalAmount={1}MinimumNumberOfEachCoin{2}", totalNumberOfCoins, totalAmount, minimumNumberOfEachCoin));
        for (int totalQuarters = minimumNumberOfEachCoin; totalQuarters < totalNumberOfCoins; totalQuarters++)
        {
            for (int totalDimes = minimumNumberOfEachCoin; totalDimes < totalNumberOfCoins; totalDimes++)
            {
                for (int totalNickels = minimumNumberOfEachCoin; totalNickels < totalNumberOfCoins; totalNickels++)
                {
                    for (int totalPennies = minimumNumberOfEachCoin; totalPennies < totalNumberOfCoins; totalPennies++)
                    {
                        numberOfTries++;
                        if (totalQuarters + totalDimes + totalNickels + totalPennies == totalNumberOfCoins)
                        {
                            if (Math.Round((totalQuarters * .25) + (totalDimes * .10) + (totalNickels * .05) + (totalPennies * .01),2) == totalAmount)
                            {
                                Console.WriteLine(String.Format("{0} Quarters, {1} Dimes, {2} Nickels, {3} Pennies", totalQuarters, totalDimes, totalNickels, totalPennies));
                                foundCount++;
                            }
                        }
                    }
                }
            }
        }
        Console.WriteLine(String.Format("{0} Combinations Found. We tried {1} combinations.", foundCount, numberOfTries));
    }

【问题讨论】:

  • 使用 LINQ 来解决这个问题绝不是滥用! :-)
  • 顺便说一句,您的代码找到 5 个(而不是 7 个)答案的原因是舍入错误。始终切换为十进制(0.05M 等),您可能会感到惊讶!
  • Re "floating point BS" - 那不是 BS;这就是浮点的工作原理。经验法则:如果您在同一个句子中看到“float”(或“double”)和“money”,则可能是错误的。

标签: c# linq math


【解决方案1】:

未经测试,但是:

        int minQuarters = 1, minDimes = 1,
            minNickels = 1, minPennies = 1,
            maxQuarters = 19, maxDimes = 19,
            maxNickels = 19, maxPennies = 19,
            coinCount = 19, total = 156;
        var qry = from q in Enumerable.Range(minQuarters, maxQuarters)
                  from d in Enumerable.Range(minDimes, maxDimes)
                  from n in Enumerable.Range(minNickels, maxNickels)
                  from p in Enumerable.Range(minPennies, maxPennies)
                  where q + d + n + p == coinCount
                  where q * 25 + d * 10 + n * 5 + p == total
                  select new {q,d,n,p};
        foreach (var row in qry)
        {
            Console.WriteLine("{0} quarter(s), {1} dime(s), {2} nickel(s) and {3} pennies",
                row.q, row.d, row.n, row.p);
        }

实际上,出于零售目的 - 也许更好的查询是“我可以分发的最少硬币是多少”?替换为:

...
from p in Enumerable.Range(minPennies, maxPennies)
where q + d + n + p <= coinCount
where q * 25 + d * 10 + n * 5 + p == total
orderby q + d + n + p
...

并使用First()Take(...) ;-p

您还可以通过在maxDimes 测试中减去(例如)q(等等...)来减少检查案例的数量 - 类似于(简化):

        int minCount = 1,
            coinCount = 19, total = 156;
        var qry = from q in Enumerable.Range(minCount, coinCount - (3 * minCount))
                  where q * 25 <= total
                  from d in Enumerable.Range(minCount, coinCount - (q + (2 * minCount)))
                  where q * 25 + d * 10 <= total
                  from n in Enumerable.Range(minCount, coinCount - (q + d + minCount))
                  where q * 25 + d * 10 + n * 5 <= total
                  from p in Enumerable.Range(minCount, coinCount - (q + d + n))
                  where q + d + n + p == coinCount
                  where q * 25 + d * 10 + n * 5 + p == total
                  select new { q, d, n, p };

【讨论】:

  • 这是一个非常快速的答案。您在返回的集合中确实有所有答案,但它似乎也返回了不正确的结果。 LINQ Query 找到 35 个结果,其中只有 5 个(和我的一样)符合 19 个计数的条件,总计 1.56 美元。
  • +1 蛮力,如果它不起作用,你就没有使用足够的它。
  • 35?我从我发布的查询中得到 7 个,它们看起来都很好。
  • 另外两个是“1个季度,9个硬币,8个镍和1个便士”和“5个季度,1个硬币,2镍和 11 便士”
  • 您的代码运行良好。就是发现7。其实就是指出了蛮力法的一个问题。蛮力方法中的两个项目被遗漏了,因为它检查总数的行正在执行一些浮点 BS,并且评估为 1.59999 而不是 1.59。
猜你喜欢
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 2019-04-15
相关资源
最近更新 更多