Skip to content

Commit 5f1a786

Browse files
mantaurntomsickeon
authored
Add num_perfect_squares keon#767 (keon#848)
* Added num_perfect_squares * Added final test for increased coverage * Fix requested changes for issue keon#767. Also improved documentation and added 1 test case. * Doc update to clarify intent for issue keon#767 This documentation update clarifies the intent and order of each code piece. Co-authored-by: unknown <[email protected]> Co-authored-by: ntomsic <[email protected]> Co-authored-by: Keon <[email protected]>
1 parent 451614b commit 5f1a786

File tree

4 files changed

+69
-0
lines changed

4 files changed

+69
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ If you want to uninstall algorithms, it is as simple as:
225225
- [next_bigger](algorithms/maths/next_bigger.py)
226226
- [next_perfect_square](algorithms/maths/next_perfect_square.py)
227227
- [nth_digit](algorithms/maths/nth_digit.py)
228+
- [num_perfect_squares](algorithms/maths/num_perfect_squares.py)
228229
- [polynomial](algorithms/maths/polynomial.py)
229230
- [power](algorithms/maths/power.py)
230231
- [prime_check](algorithms/maths/prime_check.py)

algorithms/maths/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@
2525
from .power import *
2626
from .magic_number import *
2727
from .krishnamurthy_number import *
28+
from .num_perfect_squares import *
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""
2+
Given an integer num_perfect_squares will return the minimum amount of perfect squares are required
3+
to sum to the specified number. Lagrange's four-square theorem gives us that the answer will always
4+
be between 1 and 4 (https://en.wikipedia.org/wiki/Lagrange%27s_four-square_theorem).
5+
6+
Some examples:
7+
Number | Perfect Squares representation | Answer
8+
-------|--------------------------------|--------
9+
9 | 3^2 | 1
10+
10 | 3^2 + 1^2 | 2
11+
12 | 2^2 + 2^2 + 2^2 | 3
12+
31 | 5^2 + 2^2 + 1^2 + 1^2 | 4
13+
"""
14+
15+
import math
16+
17+
def num_perfect_squares(number):
18+
"""
19+
Returns the smallest number of perfect squares that sum to the specified number.
20+
:return: int between 1 - 4
21+
"""
22+
# If the number is a perfect square then we only need 1 number.
23+
if int(math.sqrt(number))**2 == number:
24+
return 1
25+
26+
# We check if https://en.wikipedia.org/wiki/Legendre%27s_three-square_theorem holds and divide
27+
# the number accordingly. Ie. if the number can be written as a sum of 3 squares (where the
28+
# 0^2 is allowed), which is possible for all numbers except those of the form: 4^a(8b + 7).
29+
while number > 0 and number % 4 == 0:
30+
number /= 4
31+
32+
# If the number is of the form: 4^a(8b + 7) it can't be expressed as a sum of three (or less
33+
# excluding the 0^2) perfect squares. If the number was of that form, the previous while loop
34+
# divided away the 4^a, so by now it would be of the form: 8b + 7. So check if this is the case
35+
# and return 4 since it neccessarily must be a sum of 4 perfect squares, in accordance
36+
# with https://en.wikipedia.org/wiki/Lagrange%27s_four-square_theorem.
37+
if number % 8 == 7:
38+
return 4
39+
40+
# By now we know that the number wasn't of the form 4^a(8b + 7) so it can be expressed as a sum
41+
# of 3 or less perfect squares. Try first to express it as a sum of 2 perfect squares, and if
42+
# that fails, we know finally that it can be expressed as a sum of 3 perfect squares.
43+
for i in range(1, int(math.sqrt(number)) + 1):
44+
if int(math.sqrt(number - i**2))**2 == number - i**2:
45+
return 2
46+
47+
return 3

tests/test_maths.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
find_primitive_root,
2525
num_digits,
2626
diffie_hellman_key_exchange, krishnamurthy_number,
27+
num_perfect_squares,
2728
chinese_remainder_theorem,
2829
)
2930

@@ -502,6 +503,25 @@ def test_num_digits(self):
502503
self.assertEqual(3, num_digits(-254))
503504

504505

506+
507+
class TestNumberOfPerfectSquares(unittest.TestCase):
508+
"""[summary]
509+
Test for the file num_perfect_squares.py
510+
511+
Arguments:
512+
unittest {[type]} -- [description]
513+
"""
514+
def test_num_perfect_squares(self):
515+
self.assertEqual(4,num_perfect_squares(31))
516+
self.assertEqual(3,num_perfect_squares(12))
517+
self.assertEqual(2,num_perfect_squares(13))
518+
self.assertEqual(2,num_perfect_squares(10))
519+
self.assertEqual(4,num_perfect_squares(1500))
520+
self.assertEqual(2,num_perfect_squares(1548524521))
521+
self.assertEqual(3,num_perfect_squares(9999999993))
522+
self.assertEqual(1,num_perfect_squares(9))
523+
524+
505525
class TestChineseRemainderSolver(unittest.TestCase):
506526
def test_k_three(self):
507527
# Example which should give the answer 143

0 commit comments

Comments
 (0)