0

I need to solve a test question that asks to count how many apples can be eaten in total when X apples are given as a start amount, Y apples are eaten every time, and 1 apple is added every time apples are eaten.

My current solution uses a recursive function and therefore would cause an infinite loop if Y is smaller than X or if given X is way large.

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

How can I make this more efficient? There's probably a more elegant way to do this but I can't figure it out.

EDIT: Original test question text:

/**
 * 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.
 */
BadmintonCat
  • 9,416
  • 14
  • 78
  • 129
  • 1
    Y apples are eating every time. Can you explain about this? It's funny to know that apples are eating. Do yo mean eaten? – david Jul 16 '18 at 09:54
  • This sounds like it simplifies into a linear equation, but either way there's no need to use recursion. – AKX Jul 16 '18 at 09:55
  • @david that was a typo. corrected. – BadmintonCat Jul 16 '18 at 09:55
  • What happens when there are 3 apples to start with, and 5 apples are eaten each time? – MineR Jul 16 '18 at 09:57
  • @MineR I guess the result is 3 – Rafalon Jul 16 '18 at 10:00
  • @MineR then it would result in 4 eaten. 3 available plus one for eating them. 5 clamps down to the smaller given amount. – BadmintonCat Jul 16 '18 at 10:00
  • @BadmintonCat wait, *what?* I eat 5 apples, well only 3 as there are not enough apples, so I break, shouldn't I? I understood your problem as "*I eat 5, I add 1, I eat 5, I add 1...*" until I can't continue – Rafalon Jul 16 '18 at 10:02
  • @Rafalon well, as per test question, every time any number of apples are eaten, one is added, which then is also eaten. – BadmintonCat Jul 16 '18 at 10:03
  • @BadmintonCat but in this case, shouldn't "*any*" be "*5*"? If not, then you will always have infinite loops – Rafalon Jul 16 '18 at 10:04
  • Does your test include some expected results so you can test your algorithm? – Rafalon Jul 16 '18 at 10:11
  • @Rafalon see my comment below AKX's answer. – BadmintonCat Jul 16 '18 at 10:15
  • 2
    Having seen your update I want to try to clarify things: Y apples are not eaten every time. Apples are eaten one at a time and every time you reach a multiple of Y you add an extra apple. This is absolutely unambiguously clear in its specification. – Chris Jul 16 '18 at 10:40

5 Answers5

3

Not sure if you are restricted in the answer but with some maths you can come up with the following method:

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

The first guess is calculated using the fact that we keep getting new apples so for every newEvery apples we've eaten one of them was free! Of course this actually breaks down if the first guess is a whole number. This would require the free apple to have been given in order to allow us to eat it. Confusing so lets look at an example.

If we have 3 apples and we get a new apple every two then our first guess is 6 apples. However that's three free apples and we'd only get the third one after we've eaten 6 which relies on three free ones! So in this case we need to take one off. The rest of the time we can just round down to remove the fractional part which represents how close we are to a free apple.

And this just goes to show that the maths you learn at school can be used in the real world. The above is a few simple calculations and is probably going to be faster than most iterative/recursive methods of calculating this.

Additional note: It was pointed out in a now deleted comment that this could be better done using integer arithmetic for the firstGuess calculation. This would save needing to cast back to int for the return value. The reason it is written as it is currently is partly because it is the way I was thinking about it when writing it and partly because while I was iterating for the right answer I was viewing that decimal part while debugging (to confirm it was only when firstGuess was whole that I needed to do something special).

If you did change to integer maths instead then the if condition would need to be changed (since it would no longer work as is) and you would then end up with Sefe's answer.

Final Note:

If you did want to do an iterative technique then the following method matches the specification:

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

Its pretty straightforward. while you have apples it increases the count of ones you've eaten and decreases those you have left. Then if the number you have eaten is a multiple of newEvery then it adds an extra one.

Chris
  • 27,210
  • 6
  • 71
  • 92
  • I didn't know you could do double%1 to do that check. Nice. – MineR Jul 16 '18 at 11:01
  • @MineR: I was not 100% sure the best way to do the check so found the recommendations in https://stackoverflow.com/questions/2751593/how-to-determine-if-a-decimal-double-is-an-integer – Chris Jul 16 '18 at 11:03
  • @PaulF: I think you are probably right. The method above was written as that as a combination of how I was thinking about it in my head and because while iterating and testing I was looking at the decimal parts of that. I'm going to leave it as it is for now. Partly because changing to integer arithmetic would mean changing my if condition and what I'd have to change it to would make it basically identical to Sefe's answer. :) At least now there are two slightly different versions to choose between. :) – Chris Jul 16 '18 at 11:12
  • I did see the subtle difference in the if statement afterwards & removed my comment. – PaulF Jul 16 '18 at 11:21
  • @PaulF: I think it was absolutely a good comment and worth keeping to the extent that now you've deleted it I will go and add a note to my answer instead. :) – Chris Jul 16 '18 at 11:35
2

Recursion for this problem seems like overkill so I'd suggest an alternative mathematical approach.

Lets start with the fact that we'll always eat at least X apples. The real question is how much apples in total will have been added after everything gets eaten.

Say that ni will be the number of apples remaining after i "eatings". Then:

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

Solving for ni = 0 will give us i - the number of "eatings" required to eat everything:

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

Now we know how many times we'll eat, so the total number of apples that are going to be eaten is the original X plus the number of times Y apples get eaten (since every time we get an extra apple when doing so):

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

We round down the result since setting ni = 0 captures a partial number of "eatings" that then result in partial apples.

Example:

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
Petras Purlys
  • 1,093
  • 6
  • 18
2

If you only want to count the apples eaten without tracking the progress you need no recursion and no loop. You can simply calculate the result with linear O(1) code:

int result = apples + apples / (eatenPerRound - 1);
if ((apples % (eatenPerRound - 1)) <= 1) {
    result--;
}
Console.WriteLine($"Total number of {result} apples eaten.");
Sefe
  • 13,731
  • 5
  • 42
  • 55
  • I'm quite sceptic about this approach – Rafalon Jul 16 '18 at 10:36
  • @Rafalon Sceptic about whether the result answers the question correctly? The question is *"What is the maximum number of apples you can eat?"*. This question is answered. – Sefe Jul 16 '18 at 10:41
  • 1
    Note this doesn't give the result he requires for 3 starting, 2 eaten per round... Should give 5, but gives 6. – MineR Jul 16 '18 at 10:48
  • If you can change it to make it work, it deserves an upvote for being fastest. :) – MineR Jul 16 '18 at 10:49
  • @Sefe MineR's comment is exactly why I was sceptic about this approach – Rafalon Jul 16 '18 at 10:53
  • Does this fix the problem - change if condition to _"if ((apples % (eatenPerRound - 1)) == 0) result--;"_ – PaulF Jul 16 '18 at 10:56
  • @Paul: You are right, the original version assumes the new apple is added at the beginning, not at the end. – Sefe Jul 16 '18 at 11:01
  • It was confusing from the original question - I was working on a solution with the apple added first because of one of OP's comments. – PaulF Jul 16 '18 at 11:05
  • For those interested I have added a non-recursive/iterative answer myself. I think after the latest edit to this one our answers are actually the same... – Chris Jul 16 '18 at 11:06
1

Here is a running example of how I understood your problem:

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

Edit

I changed X > Y to X >= Y so it matches your specs.

Check javascript equivalent:

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))
Rafalon
  • 4,450
  • 2
  • 16
  • 30
0

I don't have a C# toolchain at hand, so the example below is in Python Community edits have made the example C#! Magic!

As said in the comments, this is an iterative problem, not a recursive one.

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
AKX
  • 152,115
  • 15
  • 115
  • 172
  • [This](https://www.tutorialspoint.com/compile_csharp_online.php) is an online C# compiler I frequently use – Rafalon Jul 16 '18 at 09:59
  • That doesn't quite add up. Given X is 3 and Y is 2 it should result in 5 (eat 2, add 1, eat 2, add 1, eat 1), but this code returns 6. – BadmintonCat Jul 16 '18 at 10:14
  • @BadmintonCat You have 3, you go to eat and now you have 4, you eat 2, and have 2 left, you go to eat and you have 3, you eat 2 and have 1 left, you go to eat and have 2, you eat and have 0 left. Total is 6. – MineR Jul 16 '18 at 10:18
  • @BadmintonCat the order in which the extra apple is added is important. – MineR Jul 16 '18 at 10:20
  • @MineR Ok, but that's not how the test question gives an example (it does as in my previous comment). – BadmintonCat Jul 16 '18 at 10:26
  • @BadmintonCat: You should probably quote the test question precisely in your question. As is said it is very important how apples get added. Am I right in thinking that the exact logic is "an apple is added after eating if you already have at least one apple? Your question says "1 apple is added every time apples are eaten" but in your example on the third eat you are not adding an apple. – Chris Jul 16 '18 at 10:30
  • @Chris The test question itself is kind of vague. I'll add it above! – BadmintonCat Jul 16 '18 at 10:31