Skip to content

Commit 2018ccc

Browse files
authored
Merge pull request #3 from 07734willy/main
Performance Optimizations
2 parents eb2e10f + 07ad0a4 commit 2018ccc

File tree

2 files changed

+170
-11
lines changed

2 files changed

+170
-11
lines changed

.gitignore

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,144 @@
1+
## Intellij
2+
13
.idea
2-
__pycache__
4+
5+
## Python
6+
7+
# Byte-compiled / optimized / DLL files
8+
__pycache__/
9+
*.py[cod]
10+
*$py.class
11+
12+
# C extensions
13+
*.so
14+
15+
# Distribution / packaging
16+
.Python
17+
build/
18+
develop-eggs/
19+
dist/
20+
downloads/
21+
eggs/
22+
.eggs/
23+
lib/
24+
lib64/
25+
parts/
26+
sdist/
27+
var/
28+
wheels/
29+
share/python-wheels/
30+
*.egg-info/
31+
.installed.cfg
32+
*.egg
33+
MANIFEST
34+
35+
# PyInstaller
36+
# Usually these files are written by a python script from a template
37+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
38+
*.manifest
39+
*.spec
40+
41+
# Installer logs
42+
pip-log.txt
43+
pip-delete-this-directory.txt
44+
45+
# Unit test / coverage reports
46+
htmlcov/
47+
.tox/
48+
.nox/
49+
.coverage
50+
.coverage.*
51+
.cache
52+
nosetests.xml
53+
coverage.xml
54+
*.cover
55+
*.py,cover
56+
.hypothesis/
57+
.pytest_cache/
58+
cover/
59+
60+
# Translations
61+
*.mo
62+
*.pot
63+
64+
# Django stuff:
65+
*.log
66+
local_settings.py
67+
db.sqlite3
68+
db.sqlite3-journal
69+
70+
# Flask stuff:
71+
instance/
72+
.webassets-cache
73+
74+
# Scrapy stuff:
75+
.scrapy
76+
77+
# Sphinx documentation
78+
docs/_build/
79+
80+
# PyBuilder
81+
.pybuilder/
82+
target/
83+
84+
# Jupyter Notebook
85+
.ipynb_checkpoints
86+
87+
# IPython
88+
profile_default/
89+
ipython_config.py
90+
91+
# pyenv
92+
# For a library or package, you might want to ignore these files since the code is
93+
# intended to run in multiple environments; otherwise, check them in:
94+
# .python-version
95+
96+
# pipenv
97+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
98+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
99+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
100+
# install all needed dependencies.
101+
#Pipfile.lock
102+
103+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
104+
__pypackages__/
105+
106+
# Celery stuff
107+
celerybeat-schedule
108+
celerybeat.pid
109+
110+
# SageMath parsed files
111+
*.sage.py
112+
113+
# Environments
114+
.env
115+
.venv
116+
env/
117+
venv/
118+
ENV/
119+
env.bak/
120+
venv.bak/
121+
122+
# Spyder project settings
123+
.spyderproject
124+
.spyproject
125+
126+
# Rope project settings
127+
.ropeproject
128+
129+
# mkdocs documentation
130+
/site
131+
132+
# mypy
133+
.mypy_cache/
134+
.dmypy.json
135+
dmypy.json
136+
137+
# Pyre type checker
138+
.pyre/
139+
140+
# pytype static type analyzer
141+
.pytype/
142+
143+
# Cython debug symbols
144+
cython_debug/

pythagorean_tuples.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from itertools import product
1+
from itertools import product, count
22
from math import log, isqrt, ceil, prod
33
from collections import Counter
44

@@ -49,7 +49,10 @@ def _prime_factors(n: int) -> list:
4949
while n % 2 == 0:
5050
factors.append(2)
5151
n //= 2
52-
for i in range(3, isqrt(n) + 1, 2):
52+
for i in count(3, 2):
53+
if i ** 2 > n:
54+
break
55+
5356
while n % i == 0:
5457
factors.append(i)
5558
n //= i
@@ -81,14 +84,27 @@ def _pythagorean_triples_BP(a: int, factors: Counter):
8184
return triples
8285

8386

87+
def _product_powers(powers_table: list, limit: int, product=1):
88+
if not powers_table:
89+
yield product
90+
return
91+
92+
powers, *rest = powers_table
93+
for power in powers:
94+
if product * power >= limit:
95+
break
96+
yield from _product_powers(rest, limit, product * power)
97+
98+
8499
def _pythagorean_triples_B(a: int, factors: Counter):
85-
# TODO: minimise the number of possible deltas (without `itertools.product()`)
86100
triples = set()
87101
factors_list = list(factors)
88102
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))
103+
104+
powers_table = [[factor ** exponent for exponent in exponents]
105+
for factor, exponents in zip(factors_list, ranges)]
106+
for d in _product_powers(powers_table, a):
107+
triples.add(_TRIPLE(a, d))
92108
return triples
93109

94110

@@ -106,12 +122,13 @@ def _pythagorean_triples_CP(a: int, factors: Counter):
106122

107123

108124
def _pythagorean_triples_C(a: int, factors: Counter):
109-
# TODO: minimise the number of possibilities (without `itertools.product()`)
110125
triples = set()
111126
factors_list = list(factors)
112127
ranges = [range(0, min(ceil(log(a, factor)), 2 * factors[factor] + 1)) for factor in factors_list]
113128
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))
129+
130+
powers_table = [[factor ** exponent for exponent in exponents]
131+
for factor, exponents in zip(factors_list, ranges)]
132+
for d in _product_powers(powers_table, a):
133+
triples.add(_TRIPLE(a, d))
117134
return triples

0 commit comments

Comments
 (0)