Problem overview
The function findLargestNumber(S: Int) receives a positive number S. It must return the largest n for which 1² + 2² + 3² + ... + n² is less than or equal to S.
For S = 35 the answer is 4. The sum up to 4 is 1 + 4 + 9 + 16 = 30, which is at most 35. Adding 5² = 25 would give 55, which is over the limit, so 5 is too large.
Approach
The squares are all positive, so the running sum only grows as n grows. That means the answer is the point where adding one more square would cross S. The simplest method is to add the squares one at a time, starting from 1, and stop just before the sum would go over S.
The loop therefore needs two variables: the sum so far, and the number whose square comes next. The condition looks one square ahead. If adding the next square still fits, add it and move on; if not, the last number added is the answer.
Step by step
- The running sum.
var totalSoFar = 0holds the sum of the squares added so far. It starts at 0 because nothing has been added. - The next number.
var number = 1is the number whose square is added next, because the sum begins with1². - The loop condition.
while (totalSoFar + number * number <= S)asks whether the sum would still be withinSafter adding the next square. Checking before adding keepstotalSoFarfrom ever going over the limit. - Add the square.
totalSoFar += number * numberincludes the current term. - Move on.
number++makes the next pass consider the following square. Without it, the same condition would stay true forever. - Correct the final value. When the loop stops,
numberis the first number whose square did not fit, one past the last number added. Returningnumber - 1gives the answer.
With S = 35 the loop adds 1, 4, 9 and 16, reaching 30 with number equal to 5. The condition then asks whether 30 + 25 is at most 35, which is false, so the loop ends and the function returns 4.
The solution in Kotlin
class Solution {
fun findLargestNumber(S: Int): Int {
var totalSoFar = 0
var number = 1
while (totalSoFar + number * number <= S) {
totalSoFar += number * number
number++
}
return number - 1
}
}
Kotlin details:
varfor both variables. Both change in the loop, so avalwould not compile.Intis 32 bits and overflow wraps silently. Kotlin doesn't warn when anIntgoes past 2147483647. It wraps around to a negative number. See the edge cases below for how this affects largeS.number++adds 1 to the variable and, used as a statement like this, has the same effect asnumber += 1.- Operator precedence.
*binds tighter than+, and+binds tighter than<=, so the condition needs no extra parentheses.
Complexity
The sum of the first n squares is n(n + 1)(2n + 1) / 6, which grows like n³ / 3. So the loop runs about (3S)^(1/3) times, the cube root of 3S. For S = 35 that is 4 passes.
Time: O(S^(1/3)), which is far below O(S). For S = 1,000,000 the loop runs 143 times.
Space: O(1). The function uses two variables.
Edge cases and common mistakes
- Small
S. ForS = 1the loop adds1²and the answer is 1. IfSwere smaller than 1, no square fits, the loop never runs and the function returns0. - A sum that lands exactly on
S.S = 5gives 2, because 1 + 4 = 5 is allowed. The condition uses<=, not<. With<the function would return 1 forS = 5. - Just below a sum. The sums of 1, 2 and 3 terms are 1, 5 and 14. So
S = 13gives 2, because 14 doesn't fit, whileS = 14gives 3. - Returning
numberinstead ofnumber - 1. The loop ends one step after the last successful addition, so the answer would be one too high. - Checking after adding. A loop that adds first and tests afterwards can end with a sum that's already over
S, and the answer is again one too high. - Overflow for very large
S. The sum of squares reaches theIntlimit nearS = 2147483647. ForS = 2147000000, the correct answer is 1860, but withIntvariables the expressiontotalSoFar + number * numberwraps to a negative value after the sum passes the limit, the condition stays true, and the function returns 8548. IfScan be that large, declaretotalSoFarandnumberasLongand compare withS.toLong().
Testing
S = 35gives4.S = 1gives1.S = 5gives2.S = 14gives3, andS = 15also gives3.S = 55gives5.