#   # 279. Perfect Squares

## The math behind the coding challenge

### Problem statement

Given an integer `n`, return the least number of perfect square numbers that sum to `n`.

A perfect square is an integer that is the square of an integer; in other words, it is the product of some integer with itself. For example, `1`, `4`, `9`, and `16` are perfect squares while `3` and `11` are not.

#### Example 1

``````Input: n = 9
Output: 1
Explanation: 9 is already a perfect square.
``````

#### Example 2

``````Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.
``````

#### Example 3

``````Input: n = 7
Output: 4
Explanation: 7 = 4 + 1 + 1 + 1.
``````

#### Example 4

``````Input: n = 12
Output: 3
Explanation: 12 = 4 + 4 + 4.
``````

#### Constraints

• `1 <= n <= 10^4`.

### Solution 1: Dynamic Programming

Let us call the function to be computed `numSquares(n)`, which calculates the least number of perfect squares that sum to `n`.

Here are the findings.

1. If `n` is already a perfect square then `numSquares(n) = 1`.

2. Otherwise, it could be written as `n = 1 + (n-1)`, or `n = 4 + (n-4)`, or `n = 9 + (n-9)`, etc. which means `n` is a sum of a perfect square (`1, 4` or `9`, etc.) and another number `m < n`. That leads to the problems `numSquares(m)` of smaller values `m`.

3. If you have gotten the results of the smaller problems `numSquares(n-1)`, `numSquares(n-4)`, `numSquares(n-9)`, etc. then `numSquares(n) = 1 + the minimum of those results`.

#### Example 4

`n = 12` is not a perfect square. It can be written as `n = 1 + 11 = 4 + 8 = 9 + 3`.

• For `m = 11`, it is not a perfect square and can be written as `m = 1 + 10 = 4 + 7 = 9 + 2`.

• For `m = 8`, it is not a perfect square and can be written as `m = 1 + 7 = 4 + 4` (matched). You get `numSquares(8) = 2`.

• For `m = 3`, it is not a perfect square and can be written as `m = 1 + 2`.

You can continue to compute `numSquares(m)` for other values `m` in this recursive process. But you can see the case of `m = 8` was already the best solution. And `numSquares(12) = 1 + numSquares(8) = 1 + 2 = 3`, which is the case of `n = 12 = 4 + 4 + 4`.

To improve runtime, you can apply dynamic programming to cache the `numSquares(n)` that you have computed.

#### Code

``````#include <iostream>
#include <cmath>
#include <unordered_map>
using namespace std;
int nsq(int n, unordered_map<int, int>& ns) {
auto it = ns.find(n);
if (it != ns.end()) {
return it->second;
}
const int sq = sqrt(n);
if (sq * sq == n) {
ns[n] = 1;
return 1;
}
int result = n;
for (int i = 1; i <= sq; i++) {
result = min(result, nsq(n - i*i, ns));
}
ns[n] = result + 1;
return ns[n];
}
int numSquares(int n) {
unordered_map<int, int> ns;
return nsq(n, ns);
}
int main() {
cout << numSquares(12) << endl;
cout << numSquares(13) << endl;
}
``````
``````Output:
3
2
``````

#### Complexity

• Runtime: `O(n^2)`.

• Extra space: `O(n)`

### Solution 2: Number Theory

The dynamic programming solution above is good enough. But for those who are interested in Algorithmic Number Theory, there is a very interesting theorem that can solve the problem directly without recursion.

It is called Lagrange's Four-Square Theorem, which states

every natural number can be represented as the sum of four integer squares.

It was proven by Lagrange in 1770.

#### Example 4

`n = 12 = 4 + 4 + 4 + 0` or `12 = 1 + 1 + 1 + 9`.

Applying to our problem, `numSquares(n)` can only be 1, 2, 3, or 4. Not more.

It turns into the problem of

identifying when `numSquares(n)` returns 1, 2, 3, or 4.

Here are the cases.

1. If `n` is a perfect square, `numSquares(n) = 1`.

2. There is another theorem, Legendre's Three-Square Theorem, which states that `numSquares(n)` cannot be 1, 2, or 3 if `n` can be expressed as

`````` n = 4^a(8*b + 7),
``````

where `a, b` are nonnegative integers. In other words, `numSquares(n) = 4` if `n` is of this form.

#### Example 3

`n = 7 = 4^0(8*0 + 7)`. It can only be written as `7 = 4 + 1 + 1 + 1`.

#### Code

``````#include <iostream>
#include <cmath>
using namespace std;
bool isSquare(int n) {
int sq = sqrt(n);
return sq * sq == n;
}
int numSquares(int n) {
if (isSquare(n)) {
return 1;
}
// Legendre's three-square theorem
int m = n;
while (m % 4 == 0) {
m /= 4;
}
if (m % 8 == 7) {
return 4;
}
const int sq = sqrt(n);
for (int i = 1; i <= sq; i++) {
if (isSquare(n - i*i)) {
return 2;
}
}
return 3;
}
int main() {
cout << numSquares(12) << endl;
cout << numSquares(13) << endl;
}
``````
``````Output:
3
2
``````

#### Complexity

• Runtime: `O(logn)`.

• Extra space: `O(1)`.

### Solution 3: Further performance improvement

Lagrange's Four-Square Theorem and Legendre's Three-Square Theorem are so powerful to solve this problem. But you can still do a little more algebra to improve further the runtime of the implementation above.

Instead of looping over `sqrt(n)` in the final `for` loop, we will prove that this loop over `sqrt(m)` is enough. That will improve runtime a lot since `m` is much less than `n`.

Let `m` be the reduced value of `n` after the Legendre's `while` loop. It satisfies

``````n = 4^a * m.
``````

We will prove that `numSquares(n) = numSquares(m)`.

In fact, if `m` is written as `m = x^2 + y^2 + z^2`, where `x, y, z` are nonnegative integers. Then

``````n = 4^a * m = (2^a)^2 * m = (2^a * x)^2 + (2^a * y)^2 + (2^a * z)^2.
``````

In other words, `numSquares(n) = numSquares(m)`.

Now you can change directly the value `n` during the Legendre's `while` loop without affecting the final result.

#### Code

``````#include <iostream>
#include <cmath>
using namespace std;
bool isSquare(int n) {
int sq = sqrt(n);
return sq * sq == n;
}
int numSquares(int n) {
if (isSquare(n)) {
return 1;
}
// Legendre's three-square theorem
while (n % 4 == 0) {
n /= 4;
}
if (n % 8 == 7) {
return 4;
}
const int sq = sqrt(n);
for (int i = 1; i <= sq; i++) {
if (isSquare(n - i*i)) {
return 2;
}
}
return 3;
}
int main() {
cout << numSquares(12) << endl;
cout << numSquares(13) << endl;
}
``````
``````Output:
3
2
``````

#### Complexity

• Runtime: `O(logn)`.

• Extra space: `O(1)`.

### Conclusion

• The title of this coding challenge (Perfect squares) gives you a hint it is more about mathematics than coding technique.

• It is amazing from Lagrange's Four-Square Theorem there are only four possibilities for the answer to the problem. Not many people knowing it.

• You can get an optimal solution to a coding problem when you know something about the mathematics behind it.

Hope you learn something from this code challenge.

Have fun with coding and mathematics!

Thanks for reading. Feel free to share your thought about my content and check out my FREE book 10 Classic Coding Challenges.

What is your approach? The problem was picked from leetcode.com. You can submit your solution in any programming language and check the performance.