没有递归的 C# 计数项目

Posted

技术标签:

【中文标题】没有递归的 C# 计数项目【英文标题】:C# Counting Items Without Recursion 【发布时间】:2018-12-23 20:06:44 【问题描述】:

我需要解决一个测试问题,询问总共可以吃多少个苹果每次吃苹果。

我当前的解决方案使用递归函数,因此如果Y 小于X 或者如果X 很大,则会导致无限循环。

public class Apples

    // Counts iterations. If we eat less than we add new every, it'll loop infinetely!
    private static int _recursions;

    private static int _applesRemaining;
    private static int _applesEaten;


    public static int CountApples(int startingAmount, int newEvery)
    
        if (newEvery > startingAmount) newEvery = startingAmount;
        Console.WriteLine("startingAmount: " + startingAmount + ", newEvery: " + newEvery);

        _applesRemaining = startingAmount;

        /* Eat 'newEvery' amount */
        _applesRemaining -= newEvery;
        _applesEaten += newEvery;

        Console.WriteLine("Eat: " + newEvery + ", remaining: " + _applesRemaining);

        /* Get one additional candy */
        _applesRemaining += 1;
        Console.WriteLine("Added 1.");

        if (_applesRemaining > 1 && _recursions++ < 1000)
        
            CountApples(_applesRemaining, newEvery);
        
        else
        
            if (_recursions > 1000) Console.WriteLine("ABORTED!");

            /* Eat the one we've just added last. */
            _applesEaten += 1;
        

        return _applesEaten;
    


    public static void Main(string[] args)
    
        Console.WriteLine(CountApples(10, 2) + "\n");
    

如何提高效率?可能有一种更优雅的方法可以做到这一点,但我想不通。

编辑: 原试题文字:

/**
 * You are given startingAmount of Apples. Whenever you eat a certain number of
 * apples (newEvery), you get an additional apple.
 *
 * What is the maximum number of apples you can eat?
 *
 * For example, if startingAmount equals 3 and newEvery equals 2, you can eat 5 apples in total:
 * 
 * Eat 2. Get 1. Remaining 2.
 * Eat 2. Get 1. Remaining 1.
 * Eat 1.
 */

【问题讨论】:

Y 个苹果每次都在吃。你能解释一下吗?知道苹果在吃东西很有趣。你的意思是吃了吗? 这听起来像是简化成一个线性方程,但无论哪种方式都不需要使用递归。 @david 这是一个错字。已更正。 一开始是3个苹果,每次吃掉5个苹果会怎样? 看到您的更新后,我想澄清一下:不是每次都吃苹果。一次吃一个苹果,每次你达到 Y 的倍数时,你就多吃一个苹果。这在其规范中是绝对明确的。 【参考方案1】:

不确定你的答案是否受到限制,但通过一些数学计算,你可以想出以下方法:

public int CheatEatenApples(int startingApples, int newEvery)

    var firstGuess = (double)startingApples*newEvery/(newEvery-1);

    if (firstGuess%1==0) // Checks if firstGuess is a whole number
    
        return (int)firstGuess-1;
    
    else
    
        return (int)firstGuess;
    

第一个猜测是根据我们不断获得新苹果的事实来计算的,因此对于我们吃过的每个newEvery 苹果,其中一个是免费的!当然,如果第一个猜测是一个整数,这实际上会失效。这需要提供免费的苹果才能让我们吃。令人困惑的是,让我们看一个例子。

如果我们有 3 个苹果并且每两个得到一个新苹果,那么我们的第一个猜测是 6 个苹果。然而,那是三个免费的苹果,我们在吃完 6 个之后才能得到第三个,这依赖于三个免费的苹果!所以在这种情况下,我们需要取下一个。剩下的时间我们可以向下取整,去掉代表我们离免费苹果有多近的小数部分。

这只是表明您在学校学习的数学可以在现实世界中使用。以上是一些简单的计算,可能会比大多数迭代/递归计算方法更快。

附加说明:在现已删除的评论中指出,这可以更好地使用整数运算来完成 firstGuess 计算。这将节省需要将返回值转换回 int。之所以写成现在这样,部分是因为这是我在编写它时考虑的方式,部分是因为当我迭代正确答案时,我在调试时查看了那个小数部分(确认它只是在firstGuess 很完整,我需要做一些特别的事情。

如果您确实更改为整数数学,则需要更改 if 条件(因为它不再按原样工作),然后您将得到 Sefe 的答案。

最后说明

如果您确实想做迭代技术,那么以下方法符合规范:

public int CalculateEatenApples(int startingApples, int newEvery)

    int applesEaten = 0;
    int apples = startingApples;
    while (apples>0)
    
        applesEaten++;
        apples--;
        if (applesEaten%newEvery==0)
        
            apples++;
        
    
    return applesEaten;

它非常简单。当你有苹果时,它会增加你吃过的数量并减少你剩下的数量。那么如果你吃的数字是newEvery的倍数,那么它就会增加一个。

【讨论】:

我不知道你可以做 double%1 来做那个检查。不错。 @MineR:我不是 100% 确定进行检查的最佳方法,因此在 ***.com/questions/2751593/… 中找到了建议 @PaulF:我认为你可能是对的。上面的方法被写成结合了我在脑海中的想法,因为在迭代和测试时,我正在查看它的小数部分。我要让它保持原样。部分原因是更改为整数算术意味着更改我的 if 条件以及我必须将其更改为的内容将使其与 Sefe 的答案基本相同。 :) 至少现在有两个略有不同的版本可供选择。 :) 我确实看到了 if 语句的细微差别,并删除了我的评论。 @PaulF:我认为这绝对是一个很好的评论,值得保留,因为现在你已经删除了它,我会在我的答案中添加一个注释。 :)【参考方案2】:

这个问题的递归似乎有点矫枉过正,所以我建议另一种数学方法。

让我们从我们总是至少吃 X 个苹果这一事实开始。真正的问题是,吃完所有东西后总共会添加多少苹果。

假设 ni 将是我“吃”后剩余的苹果数。那么:

n0 = X
n1 = X - Y + 1
n2 = X - 2Y + 2
...
ni = X - i(Y - 1)

求解 ni = 0 将得到 i - 吃完所有东西所需的“吃”次数:

ni = 0 = X - i(Y - 1) => i = X / (Y - 1)

现在我们知道要吃多少次了,所以要吃的苹果总数是原来的 X 加上 Y 个苹果被吃掉的次数(因为每次我们在做所以):

tot = X + roundDown(i) = X * roundDown(X / (Y - 1))

我们将结果四舍五入,因为设置 ni = 0 会捕获部分“吃”,然后导致部分苹果。

例子:

X = 7, Y = 3 => tot = 7 + roundDown(7 / (3 - 1)) = 7 + roundDown(3.5) = 10
starting with 7:
0 eaten, 7 remain
3 eaten, 1 gained, 5 remain
3 eaten, 1 gained, 3 remain
3 eaten, 1 gained, 1 remains
1 eaten, nothing gained, nothing remains
--
10 eaten in total

【讨论】:

【参考方案3】:

如果您只想计算吃掉的苹果而不跟踪进度,则不需要递归和循环。您可以使用线性 O(1) 代码简单地计算结果:

int result = apples + apples / (eatenPerRound - 1);
if ((apples % (eatenPerRound - 1)) <= 1) 
    result--;

Console.WriteLine($"Total number of result apples eaten.");

【讨论】:

我对这种方法持怀疑态度 @Rafalon 怀疑结果是否正确回答了问题?问题是“你最多可以吃多少个苹果?”。这个问题已经回答了。 请注意,这并没有给出他需要 3 开始,每轮吃 2 的结果...应该给出 5,但给出 6。 如果您可以对其进行更改以使其正常工作,那么它应该被评为最快。 :) @Sefe MineR 的评论正是我对这种方法持怀疑态度的原因【参考方案4】:

这是我如何理解您的问题的运行示例:

using System.IO;
using System;

class Program

    static void Main()
    
        Console.WriteLine(GetNumberOfApplesEaten(100,5));
    

    public static int GetNumberOfApplesEaten(int X, int Y)
    
        int result = 0;

        // while we can eat 'Y' apples
        while(X >= Y)
        
            // we eat & count those apples
            result += Y;
            X -= Y;
            // we add an extra apple
            X++;
        
        // we eat what's left
        result += X;

        return result;
    

编辑

我将X &gt; Y 更改为X &gt;= Y,使其符合您的规格。

检查 javascript 等效项:

function getNumberOfApplesEaten(X,Y) 
    var result = 0;

    // while we can eat 'Y' apples
    while(X >= Y) 
        // we eat & count those apples
        result += Y;
        X -= Y;
        // we add an extra apple
        X++;
    
    // we eat what's left
    result += X;

    return result;


console.log(getNumberOfApplesEaten(3,2))

【讨论】:

这对我有用,非常感谢!我添加了一个检查,以防 Y 为 1,这将导致无限循环。 @BadmintonCat 是的,你一定要检查 X &gt; 0Y &gt; 1【参考方案5】:

我手头没有 C# 工具链,所以下面的示例是用 Python 编写的 社区编辑制作了 C# 示例!魔法!

正如 cmets 中所说,这是一个迭代问题,而不是递归问题。

var apples = 100;
var eaten = 0;
var eatPerRound = 5;
var gainPerRound = 1;

while(apples > 0)

    apples -= eatPerRound;
    eaten += eatPerRound;
    apples += gainPerRound;
    Console.WriteLine($"Round round: eaten apples eaten, apples left");


Round 1: 5 apples eaten, 96 left
Round 2: 10 apples eaten, 92 left
Round 3: 15 apples eaten, 88 left
Round 4: 20 apples eaten, 84 left
Round 5: 25 apples eaten, 80 left
Round 6: 30 apples eaten, 76 left
Round 7: 35 apples eaten, 72 left
Round 8: 40 apples eaten, 68 left
Round 9: 45 apples eaten, 64 left
Round 10: 50 apples eaten, 60 left
Round 11: 55 apples eaten, 56 left
Round 12: 60 apples eaten, 52 left
Round 13: 65 apples eaten, 48 left
Round 14: 70 apples eaten, 44 left
Round 15: 75 apples eaten, 40 left
Round 16: 80 apples eaten, 36 left
Round 17: 85 apples eaten, 32 left
Round 18: 90 apples eaten, 28 left
Round 19: 95 apples eaten, 24 left
Round 20: 100 apples eaten, 20 left
Round 21: 105 apples eaten, 16 left
Round 22: 110 apples eaten, 12 left
Round 23: 115 apples eaten, 8 left
Round 24: 120 apples eaten, 4 left
Round 25: 125 apples eaten, 0 left

【讨论】:

This是我经常使用的在线C#编译器 这并不完全加起来。假设 X 为 3,Y 为 2,它应该得到 5(吃 2,加 1,吃 2,加 1,吃 1),但是这段代码返回 6。 @BadmintonCat 你有 3 个,你去吃饭,现在你有 4 个,你吃了 2 个,还剩 2 个,你去吃饭,你有 3 个,你吃 2 个,还剩 1 个,你去吃有2个,你吃完还有0个。总共是 6 个。 @BadmintonCat 添加额外苹果的顺序很重要。 @MineR 好的,但这不是测试问题给出示例的方式(它与我之前的评论一样)。

以上是关于没有递归的 C# 计数项目的主要内容,如果未能解决你的问题,请参考以下文章

想深入学习C#语言,有没有达人知道C#的好的资料呢?

SonarQube:添加一种方法来打破这种方法递归 C#

作业八——项目说明

使用 JSFL 检查库项目的“使用计数”

C# 实现二叉树各种排序

根据项目位置动态更改 StaggeredGridLayout 的列跨度计数