Solve problem 143. Meh.

main
felixm 2024-04-30 19:08:14 -04:00
parent 0c1280ce91
commit 5ce2c5bcbf
1 changed files with 127 additions and 0 deletions

127
python/e143.py Normal file
View File

@ -0,0 +1,127 @@
import math
import concurrent.futures
from collections import defaultdict
def attempt_1(a, b, c):
""" For the triangle with the sites a, b, c, calculate
the coordinates of C where we define A to be at (0, 0),
and B at (c, 0). """
assert a + b > c
assert a + c > b
assert b + c > a
S2 = [s**2 for s in range(120000)]
S4 = [s**4 for s in range(120000)]
a2 = S2[a]
b2 = S2[b]
c2 = S2[c]
a4 = S4[a]
b4 = S4[b]
c4 = S4[c]
# I've derived this myself but unfortunately I was completely on the wrong
# track.
x = -3 * (a4 + b4 + c4) + 6 * (a2*b2 + a2*c2 + b2*c2)
y = math.isqrt(x)
# y = math.sqrt(-3 * (a4 + b4 + c4) + 6 * (a2*b2 + a2*c2 + b2*c2))
if not y * y == x:
return "no int"
d = math.isqrt((c2 + b2 + a2 + y) // 2)
if d * d * 2 == c2 + b2 + a2 + y:
return d
else:
return "no int"
def is_square(n):
i = math.isqrt(n)
return i * i == n
def check_range(start, end, limit):
# When you realize that the angles in the middle are all 120 degress, you
# can derive the following formula with the law of cosines:
# c**2 = p**2 + p * r + r**2
rs = set()
for q in range(start, end):
for r in range(1, q):
if q + r > limit:
break
a2 = q**2 + q * r + r**2
if not is_square(a2):
continue
for p in range(1, r):
if q + r + p > limit:
break
b2 = p**2 + p * q + q**2
if not is_square(b2):
continue
c2 = p**2 + p * r + r**2
if is_square(c2):
d = p + q + r
# assert 3*(a**4 + b**4 + c**4 + d**4) == (a**2 + b**2 + c**2 + d**2)**2
print(math.sqrt(a2), math.sqrt(b2), math.sqrt(c2), d)
if d < limit:
rs.add(d)
return rs
def euler_143_brute_foce():
m = 120_000
range_size = 1000
s = set()
with concurrent.futures.ProcessPoolExecutor() as executor:
futures = []
for start in range(1, m, range_size):
end = min(start + range_size, m)
futures.append(executor.submit(check_range, start, end, m))
for future in concurrent.futures.as_completed(futures):
s |= future.result()
return sum(s)
def euler_143():
pairs = defaultdict(set)
limit = 120_000
# Instead of bruteforcing, we can calculate 120 degree triangles directly.
#
# https://en.wikipedia.org/wiki/Integer_triangle#Integer_triangles_with_a_120%C2%B0_angle
#
# We then map one 120 angle adjacent site to another one.
for m in range(1, math.isqrt(limit)):
for n in range(1, m):
a = m**2 + m * n + n**2
b = 2*m*n + n**2
c = m**2 - n**2
assert a**2 == b**2 + b * c + c**2
k = 1
while k * (b + c) < limit:
pairs[k * b].add(k * c)
pairs[k * c].add(k * b)
k += 1
# Which ultimately allows us to construct all the triangles by iterating
# over the sides and looking up the respective next side.
xs = set()
for q in pairs:
for r in pairs[q]:
for p in pairs[r]:
if q in pairs[p]:
s = q + r + p
if s < limit:
xs.add(s)
return sum(xs)
if __name__ == "__main__":
solution = euler_143()
print("e143.py: " + str(solution))
assert solution == 30758397