forked from nayuki/Project-Euler-solutions
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathp094.py
More file actions
81 lines (76 loc) · 2.74 KB
/
p094.py
File metadata and controls
81 lines (76 loc) · 2.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#
# Solution to Project Euler problem 94
# Copyright (c) Project Nayuki. All rights reserved.
#
# https://www.nayuki.io/page/project-euler-solutions
# https://github.com/nayuki/Project-Euler-solutions
#
import eulerlib, fractions, itertools
# Consider an arbitrary almost equilateral triangle with side lengths (c, c, c +/- 1).
# Split it down the middle to get a right triangle, and label the new sides.
# /\ /|
# c / \ c c / | b
# / \ --> / |
# -------- -----
# c +/- 1 a
# Note that a = (c +/- 1) / 2, and a^2 + b^2 = c^2 (Pythagorean theorem).
#
# We know that c is an integer. The area of the original triangle is a*b,
# which is an integer by definition from the problem statement.
# - If a is an integer, then b is an integer (so that a*b is an integer),
# thus (a,b,c) is a Pythagorean triple.
# - Otherwise a is an integer plus a half, then b must be even,
# but a^2 + b^2 is not an integer, which contradicts c being an integer.
#
# Conversely, consider an arbitrary Pythagorean triple (a,b,c).
# If 2a = c +/- 1, then we can form an almost equilateral triangle:
# /|\
# c / | \ c
# / | \
# ---------
# 2a
# For this to happen, the Pythagorean triple must be primitive. Because if not,
# then a = 0 mod k and c = 0 mod k for some k > 1, which means 2a = 0 mod k which
# cannot equal c +/- 1 = +/- 1 mod k. So we only need to generate primitive triples.
#
# Pythagorean triples theorem:
# Every primitive Pythagorean triple with a odd and b even can be expressed as
# a = st, b = (s^2-t^2)/2, c = (s^2+t^2)/2, where s > t > 0 are coprime odd integers.
def compute():
LIMIT = 10**9
ans = 0
# What search range do we need?
# c = (s^2+t^2)/2. Perimeter = p = 3c +/- 1 = 3/2 (s^2+t^2) +/- 1 <= LIMIT.
# We need to keep the smaller perimeter within limit for
# the search to be meaningful, so 3/2 (s^2+t^2) - 1 <= LIMIT.
# With t < s, we have that s^2+t^2 < 2s^2, so 3/2 (s^2+t^2) - 1 < 3s^2 - 1.
# Therefore it is sufficient to ensure that 3s^2 - 1 <= LIMIT, i.e. s^2 <= (LIMIT+1)/3.
for s in itertools.count(1, 2):
if s * s > (LIMIT + 1) // 3:
break
for t in range(s - 2, 0, -2):
if fractions.gcd(s, t) == 1:
a = s * t
b = (s * s - t * t) // 2
c = (s * s + t * t) // 2
if a * 2 == c - 1:
p = c * 3 - 1
if p <= LIMIT:
ans += p
if a * 2 == c + 1:
p = c * 3 + 1
if p <= LIMIT:
ans += p
# Swap the roles of a and b and try the same tests
# Note that a != b, since otherwise c = a * sqrt(2) would be irrational
if b * 2 == c - 1:
p = c * 3 - 1
if p <= LIMIT:
ans += p
if b * 2 == c + 1:
p = c * 3 + 1
if p <= LIMIT:
ans += p
return str(ans)
if __name__ == "__main__":
print(compute())