Problem overview
FindLargestNumber(int S) looks for the largest n for which 1² + 2² + 3² + ... + n² is at most S.
For S = 35: the running sums are 1, 5, 14 and 30 after adding the squares of 1, 2, 3 and 4. Adding 5² = 25 would make 55, which is above 35. So the answer is 4.
Approach
Keep a running total of the squares added so far, and try the numbers 1, 2, 3, ... in order. Before adding the next square, check whether the total would still be at most S. If it would, add it and move on. If not, stop: the squares are positive, so no later number can fit either.
This is a while loop, because the number of repetitions depends on S and isn't known beforehand. The key is that the condition looks one square ahead. The loop never lets the total go above S, so when it ends the total is valid and the next number is the first one that doesn't fit.
Step by step
- Create the running total.
totalSoFarstarts at 0, because nothing has been added yet. - Create the next number to try.
numberstarts at 1, since the sum begins with 1². - Write the loop condition.
totalSoFar + number * number <= Sasks whether adding the next square still fits. - Add the square.
totalSoFar += number * numberincludes the current number in the sum. - Advance.
number++moves to the following number. Without it, the loop would add the same square forever. - Return one less than
number. After the last successful addition,numberwas incremented once more, so it points one past the answer.
Tracing S = 35
| Before the check | totalSoFar | number | Test |
|---|---|---|---|
| Pass 1 | 0 | 1 | 0 + 1 <= 35, so add |
| Pass 2 | 1 | 2 | 1 + 4 <= 35, so add |
| Pass 3 | 5 | 3 | 5 + 9 <= 35, so add |
| Pass 4 | 14 | 4 | 14 + 16 <= 35, so add |
| Pass 5 | 30 | 5 | 30 + 25 = 55 > 35, so stop |
The loop ends with number equal to 5, and the function returns 5 - 1 = 4.
The solution in C#
class Solution {
public int FindLargestNumber(int S) {
int totalSoFar = 0;
int number = 1;
while (totalSoFar + number * number <= S) {
totalSoFar += number * number;
number++;
}
return number - 1;
}
}
Some C# details:
- There is no
^for powers. In C#,^is a bitwise operator (exclusive or). Write a square asnumber * number, or useMath.Pow, which works ondoubleand would need a cast back toint. Plain multiplication is simpler and exact. - Integer arithmetic stays integer. All three variables are
int, so there is no rounding to worry about. number++adds 1 to the variable. It is a statement here, so the prefix or postfix form makes no difference.
Complexity
Time. The sum of the first n squares is about n³ / 3, so the loop stops once n reaches roughly the cube root of 3S. So the number of passes is proportional to the cube root of S, which is very small. Even for S near two billion, the loop makes under two thousand passes.
Space: O(1). Only two integer variables are used.
Edge cases and common mistakes
- Small
S. ForS = 1the loop adds 1, then 1 + 4 exceeds the limit, so the answer is 1. IfSwere below 1, the loop wouldn't run and the function would return 0, which is also correct because no square fits. - An exact fit. For
S = 30the sum equals the limit after 4, and the answer is 4. That is why the comparison is<=and not<. - Returning
number. That is one too many. The loop leavesnumberat the first value that didn't fit. - Checking after adding. A loop that adds first and tests afterwards would include a square that pushes the total over
S, then have to take it back out. - Overflow for huge
S.intarithmetic wraps silently in C#. WhenSis within about a million ofint.MaxValue, the expressiontotalSoFar + number * numbercan exceed theintrange, wrap to a negative value and pass the test. WithS = 2147000000, for example, the function returns 8548 instead of 1860. Usinglongfor the total avoids that. Inputs of ordinary size are not affected.
Testing
S = 35gives 4.S = 30gives 4.S = 5gives 2.S = 1gives 1.