Skip to content

Commit eb2e10f

Browse files
committed
Add the first draft of pythagorean_tuples.py
This is the first version of a function generating pythagorean triples that passes all of the tests.
1 parent 9e9eaa9 commit eb2e10f

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

pythagorean_tuples.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
from itertools import product
2+
from math import log, isqrt, ceil, prod
3+
from collections import Counter
4+
5+
6+
def pythagorean_triples(a: int, primitive=False) -> set:
7+
"""
8+
Return all pythagorean triples consisting 'a' as a leg (cathetus) of right triangle.
9+
10+
This function is an implementation of Tanay Roy and Farjana Jaishmin Sonia's paper "A Direct Method To Generate
11+
Pythagorean Triples And Its Generalization To Pythagorean Quadruples And n-tuples".
12+
13+
:param a: positive integer number
14+
:param primitive: True if triples should be primitive, False otherwise
15+
:return: set of all [primitive] pythagorean triples consisting a
16+
"""
17+
18+
if type(a) is not int:
19+
raise TypeError("a must be an integer")
20+
if a < 1:
21+
raise ValueError("a must be positive")
22+
if a in {1, 2}:
23+
return set()
24+
25+
factors = Counter(_prime_factors(a))
26+
27+
# Categorising process
28+
if 2 not in factors:
29+
return _pythagorean_triples_BP(a, factors) if primitive else _pythagorean_triples_B(a, factors)
30+
elif len(factors) == 1:
31+
return _pythagorean_triples_AP(a) if primitive else _pythagorean_triples_A(a, factors)
32+
else:
33+
return _pythagorean_triples_CP(a, factors) if primitive else _pythagorean_triples_C(a, factors)
34+
35+
36+
def _TRIPLE(a: int, delta: int):
37+
b = (a ** 2 - delta ** 2) // (2 * delta)
38+
return a, b, b + delta
39+
40+
41+
def _prime_factors(n: int) -> list:
42+
"""
43+
Return ordered list of all prime factors of n.
44+
45+
:param n: (int) natural number greater than 1
46+
:return: (list) ordered list of all prime factors of n
47+
"""
48+
factors = []
49+
while n % 2 == 0:
50+
factors.append(2)
51+
n //= 2
52+
for i in range(3, isqrt(n) + 1, 2):
53+
while n % i == 0:
54+
factors.append(i)
55+
n //= i
56+
if n > 2:
57+
factors.append(n)
58+
return factors
59+
60+
61+
def _pythagorean_triples_AP(a: int):
62+
return {_TRIPLE(a, 2)}
63+
64+
65+
def _pythagorean_triples_A(a: int, factors: Counter):
66+
triples = set()
67+
m = factors[2]
68+
for r in range(1, m):
69+
d = 2 ** r
70+
triples.add(_TRIPLE(a, d))
71+
return triples
72+
73+
74+
def _pythagorean_triples_BP(a: int, factors: Counter):
75+
triples = set()
76+
triples.add(_TRIPLE(a, 1))
77+
for p in factors:
78+
d = p ** (2 * factors[p])
79+
if a > d:
80+
triples.add(_TRIPLE(a, d))
81+
return triples
82+
83+
84+
def _pythagorean_triples_B(a: int, factors: Counter):
85+
# TODO: minimise the number of possible deltas (without `itertools.product()`)
86+
triples = set()
87+
factors_list = list(factors)
88+
ranges = [range(0, min(ceil(log(a, factor)), 2 * factors[factor] + 1)) for factor in factors_list]
89+
for powers in product(*ranges):
90+
if (d := prod(factors_list[i] ** power for i, power in enumerate(powers))) < a:
91+
triples.add(_TRIPLE(a, d))
92+
return triples
93+
94+
95+
def _pythagorean_triples_CP(a: int, factors: Counter):
96+
triples = set()
97+
m = factors[2]
98+
del factors[2]
99+
for p in factors:
100+
s = factors[p]
101+
for t in 0, 2 * s:
102+
for r in 1, 2 * m - 1:
103+
if a > (d := 2 ** r * p ** t) and r != m:
104+
triples.add(_TRIPLE(a, d))
105+
return triples
106+
107+
108+
def _pythagorean_triples_C(a: int, factors: Counter):
109+
# TODO: minimise the number of possibilities (without `itertools.product()`)
110+
triples = set()
111+
factors_list = list(factors)
112+
ranges = [range(0, min(ceil(log(a, factor)), 2 * factors[factor] + 1)) for factor in factors_list]
113+
ranges[0] = range(1, min(ceil(log(a, 2)), 2 * factors[2])) # concerns the factor 2
114+
for powers in product(*ranges):
115+
if (d := prod(factors_list[i] ** power for i, power in enumerate(powers))) < a:
116+
triples.add(_TRIPLE(a, d))
117+
return triples

0 commit comments

Comments
 (0)