Problem overview
Given a positive number S, the method findLargestNumber(S) returns the largest integer n for which 1² + 2² + 3² + ... + n² is less than or equal to S.
For S = 35 the sums are 1, 5, 14, 30 and then 55. Four squares give 30, which fits within 35, but adding 5² = 25 would give 55, which doesn't. The answer is 4.
Approach: add squares until the next one doesn't fit
No formula is needed. Keep a running total and a counter for the next number to add. Each round asks one question: if I add the square of the counter, is the total still at most S? If yes, add it and move the counter up. If no, stop.
The check looks one square ahead and the total only changes after the check passes. That means the total never exceeds S, so no undoing is needed afterwards. Because the counter moves up after every successful addition, it ends one past the last number that was added, and the answer is the counter minus one.
Step by step
- Declare
totalSoFarwithletand set it to 0, since nothing has been added yet. - Declare
numberwithletand set it to 1, the first number whose square is added. - Write the loop with the condition
totalSoFar + number * number <= S. It tests the sum that would result, without changing anything. - Add the square to the total with
+=inside the loop. - Increment
numberso the next round tests the following square. Without this line the loop would never end. - Return
number - 1after the loop. ForS = 35the loop adds 1, 4, 9 and 16, then stops withnumberequal to 5, because 30 + 25 exceeds 35. The answer 4 is one less.
The solution in TypeScript
class Solution {
findLargestNumber(S: number): number {
let totalSoFar: number = 0;
let number: number = 1;
while (totalSoFar + number * number <= S) {
totalSoFar += number * number;
number++;
}
return number - 1;
}
}
The variable named number has the same name as the type number. That is legal because types and variables live in separate places: in let number: number = 1 the first word is a variable name and the one after the colon is the type. It is confusing to read, though, and a name such as current would be clearer in your own code.
TypeScript's number is a double-precision floating-point value, so S could be any number, not only an integer, and the loop still works: it compares the sums with S as written. The variables hold whole numbers here, and sums stay exact up to 2^53, far beyond any reasonable input.
Complexity
The sum of the first n squares is about n³ / 3, so the loop stops when n is around the cube root of 3S. The time is therefore O(S^(1/3)), which is very fast: for S near a billion, the loop runs about 1,400 times. It uses two variables, so the extra space is O(1).
Edge cases and common mistakes
Ssmaller than 1. The first check,0 + 1 <= S, is false, so the loop never runs and the method returns 0. No positive number has a square sum withinS, and 0 stands for "none".S = 1. One square fits and a second would give 5, so the answer is 1.- An exact fit. For
S = 30the sum 30 is allowed because the condition uses<=. The answer is 4. Using<would wrongly return 3. - Returning
numberinstead ofnumber - 1. That is the number that did not fit. - Adding first and checking afterwards. Then the total can overshoot
Sand the counter ends two past the answer.
Testing
Try S = 35 (answer 4), S = 55 (answer 5, because 1 + 4 + 9 + 16 + 25 = 55) and S = 3 (answer 1).