Commit 61f5c1d9 authored by Nayuki's avatar Nayuki

P86, P93, P95, P98, P206, P425: Added Python solutions.

parent afd595b6
......@@ -107,15 +107,19 @@ ANSWERS = {
82: "260324",
83: "425185",
85: "2772",
86: "1818",
87: "1097343",
88: "7587457",
89: "743",
90: "1217",
91: "14234",
92: "8581146",
93: "1258",
94: "518408346",
95: "14316",
96: "24702",
97: "8739992577",
98: "18769",
99: "709",
100: "756872327473",
101: "37076114526",
......@@ -172,6 +176,7 @@ ANSWERS = {
203: "34029210557338",
204: "2944730",
205: "0.5731441",
206: "1389019170",
214: "1677366278943",
215: "806844323190414",
216: "5437849",
......@@ -194,6 +199,7 @@ ANSWERS = {
381: "139602943319822",
387: "696067597313468",
401: "281632621",
425: "46479497324",
429: "98792821",
493: "6.818741802",
518: "100315739184392",
......
#
# Solution to Project Euler problem 86
# by Project Nayuki
#
# https://www.nayuki.io/page/project-euler-solutions
# https://github.com/nayuki/Project-Euler-solutions
#
import fractions, itertools
def compute():
# solutions[k] is the set of all solutions where the largest side has length k.
# A solution is a triple (x, y, z) such that 0 < x <= y <= z, and in the rectangular prism with dimensions x * y * z,
# the shortest surface path from one vertex to the opposite vertex has an integral length.
solutions = []
# Generates all solutions where the largest side has length less than 'limit'.
def generate_solutions():
# 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.
# Now generate all Pythagorean triples, including non-primitive ones.
for s in itertools.count(3, 2):
for t in range(s - 2, 0, -2):
if s * s // 2 >= limit * 3:
return
if fractions.gcd(s, t) == 1:
for k in itertools.count(1):
a = s * t * k
b = (s * s - t * t) // 2 * k
c = (s * s + t * t) // 2 * k
if a >= limit and b >= limit:
break
find_splits(a, b, c)
find_splits(b, a, c)
# Assumes that a^2 + b^2 = c^2.
def find_splits(a, b, c):
z = b
for x in range(1, a):
y = a - x
if y < x:
break
if c * c == min(
(x + y) * (x + y) + z * z,
(y + z) * (y + z) + x * x,
(z + x) * (z + x) + y * y):
temp = max(x, y, z)
if temp < limit:
# Add canonical solution
item = tuple(sorted((x, y, z)))
solutions[temp].add(item)
# cumulativesolutions[m] = len(solutions[0]) + len(solutions[1]) + ... + len(solutions[m]).
cumulativesolutions = [0]
limit = 1
while True:
# Extend the solutions list with blank sets
while len(solutions) < limit:
solutions.append(set())
generate_solutions()
# Compute the number of cumulative solutions up to and including a certain maximum size
for i in range(len(cumulativesolutions), limit):
sum = cumulativesolutions[i - 1] + len(solutions[i])
cumulativesolutions.append(sum)
if sum > 1000000:
return str(i)
# Raise the limit and keep searching
limit *= 2
if __name__ == "__main__":
print(compute())
#
# Solution to Project Euler problem 93
# by Project Nayuki
#
# https://www.nayuki.io/page/project-euler-solutions
# https://github.com/nayuki/Project-Euler-solutions
#
import fractions, itertools
def compute():
ans = max(((a, b, c, d)
for a in range(1, 10)
for b in range(a + 1, 10)
for c in range(b + 1, 10)
for d in range(c + 1, 10)),
key=longest_consecutive)
return "".join(str(x) for x in ans)
def longest_consecutive(abcd):
a, b, c, d = abcd
expressible = set()
# Try all possible orderings of operands and operators
ops = [0, 0, 0, a, b, c, d] # 0 = operator slot, 1 to 9 = literal operand
while True:
# Try all possibilities for the 3 operators
for i in range(64):
stack = []
j = 0 # Operator index
stackunderflow = False
divbyzero = False
for op in ops:
if 1 <= op <= 9: # Operand
stack.append(fractions.Fraction(op))
elif op == 0: # Operator
if len(stack) < 2:
stackunderflow = True
break
right = stack.pop()
left = stack.pop()
oper = (i >> (j * 2)) & 3
if oper == 0:
stack.append(left + right)
elif oper == 1:
stack.append(left - right)
elif oper == 2:
stack.append(left * right)
elif oper == 3:
if right.numerator == 0:
divbyzero = True
break
stack.append(left / right)
else:
raise AssertionError()
j += 1 # Consume an operator
else:
raise AssertionError()
if stackunderflow:
break
if divbyzero:
continue
if len(stack) != 1:
raise AssertionError()
result = stack.pop()
if result.denominator == 1:
expressible.add(result.numerator)
if not next_permutation(ops):
break
# Find largest set of consecutive expressible integers starting from 1
return next(i for i in itertools.count(1) if (i not in expressible)) - 1
def next_permutation(arr):
# Find non-increasing suffix
i = len(arr) - 1
while i > 0 and arr[i - 1] >= arr[i]:
i -= 1
if i <= 0:
return False
# Find successor to pivot
j = len(arr) - 1
while arr[j] <= arr[i - 1]:
j -= 1
arr[i - 1], arr[j] = arr[j], arr[i - 1]
# Reverse suffix
arr[i : ] = arr[len(arr) - 1 : i - 1 : -1]
return True
if __name__ == "__main__":
print(compute())
#
# Solution to Project Euler problem 95
# by Project Nayuki
#
# https://www.nayuki.io/page/project-euler-solutions
# https://github.com/nayuki/Project-Euler-solutions
#
import itertools
def compute():
LIMIT = 10**6
# divisorsum[n] is the sum of all the proper divisors of n
divisorsum = [0] * (LIMIT + 1)
for i in range(1, LIMIT + 1):
for j in range(i * 2, LIMIT + 1, i):
divisorsum[j] += i
# Analyze the amicable chain length for each number in ascending order
maxchainlen = 0
ans = -1
for i in range(LIMIT + 1):
visited = set()
cur = i
for count in itertools.count(1):
# 'count' is the length of the this amicable chain
visited.add(cur)
next = divisorsum[cur]
if next == i:
if count > maxchainlen:
ans = i
maxchainlen = count
break
# Exceeds limit or not a chain (a rho shape instead)
elif next > LIMIT or next in visited:
break
else:
cur = next
return str(ans)
if __name__ == "__main__":
print(compute())
This diff is collapsed.
#
# Solution to Project Euler problem 206
# by Project Nayuki
#
# https://www.nayuki.io/page/project-euler-solutions
# https://github.com/nayuki/Project-Euler-solutions
#
# The major optimization is to do arithmetic in base 10 in the main loop, avoiding division and modulo
def compute():
# Initialize
n = 1000000000 # The pattern is greater than 10^18, so start searching at 10^9
ndigits = [0] * 10 # In base 10, little-endian
temp = n
for i in range(len(ndigits)):
ndigits[i] = temp % 10
temp //= 10
n2digits = [0] * 19 # Based on length of pattern
temp = n * n
for i in range(len(n2digits)):
n2digits[i] = temp % 10
temp //= 10
# Increment and search
while not is_concealed_square(n2digits):
# Add 20n + 100 so that n2digits = (n + 10)^2
add_20n(ndigits, n2digits)
add_10pow(n2digits, 2)
# Since n^2 ends with 0, n must end with 0
n += 10
add_10pow(ndigits, 1)
# Now n2digits = n^2
return str(n)
def is_concealed_square(n):
for i in range(1, 10): # Scan for 1 to 9
if n[20 - i * 2] != i:
return False
return n[0] == 0 # Special case for 0
def add_10pow(n, i):
while n[i] == 9:
n[i] = 0
i += 1
n[i] += 1
def add_20n(n, n2):
carry = 0
i = 0
while i < len(n):
sum = n[i] * 2 + n2[i + 1] + carry
n2[i + 1] = sum % 10
carry = sum // 10
i += 1
i += 1
while carry > 0:
sum = n2[i] + carry
n2[i] = sum % 10
carry = sum // 10
i += 1
if __name__ == "__main__":
print(compute())
#
# Solution to Project Euler problem 425
# by Project Nayuki
#
# https://www.nayuki.io/page/project-euler-solutions
# https://github.com/nayuki/Project-Euler-solutions
#
import eulerlib, heapq
# Finding all the relatives of 2 can be seen as a single-source shortest path problem,
# which we solve here using Dijkstra's algorithm. The key insight is that at each node (prime number),
# we consider the connection path from 2 to it, and store the maximum path number at the node.
# It is amenable to dynamic programming because it's always best to minimize the maximum path number.
#
# For example, 2 is connected to 103 because 2 <-> 3 <-> 13 <-> 113 <-> 103.
# The maximum number along this path is 113, and among all paths
# this is the minimum possible maximum, so 103 is not a relative of 2.
def compute():
LIMIT = 10**7
isprime = eulerlib.list_primality(LIMIT)
# pathmax[i] = None if i is not prime or i is not connected to 2.
# Otherwise, considering all connection paths from 2 to i and for each path computing
# the maximum number, pathmax[i] is the minimum number among all these maxima.
pathmax = [None] * len(isprime)
# Process paths in increasing order of maximum number
queue = [(2, 2)]
while len(queue) > 0:
pmax, n = heapq.heappop(queue)
if pathmax[n] is not None and pmax >= pathmax[n]:
# This happens if at the time this update was queued, a better
# or equally good update was queued ahead but not processed yet
continue
# Update the target node and explore neighbors
pathmax[n] = pmax
# Try all replacements of a single digit, including the leading zero.
# This generates exactly all (no more, no less) the ways that a number m is connected to n.
digits = to_digits(n)
tempdigits = list(digits)
for i in range(len(tempdigits)): # For each digit position
for j in range(10): # For each digit value
tempdigits[i] = j
m = to_number(tempdigits)
nextpmax = max(m, pmax)
if m < len(isprime) and isprime[m] and (pathmax[m] is None or nextpmax < pathmax[m]):
heapq.heappush(queue, (nextpmax, m))
tempdigits[i] = digits[i] # Restore the digit
ans = sum(i for i in range(len(isprime)) if (isprime[i] and (pathmax[i] is None or pathmax[i] > i)))
return str(ans)
# Returns the given non-negative integer as an array of digits, in big endian, with an extra leading zero.
# e.g. 0 -> [0,0]; 1 -> [0,1]; 8 -> [0,8]; 42 -> [0,4,2]; 596 -> [0,5,9,6].
def to_digits(n):
if n < 0:
raise ValueError()
# Extract base-10 digits in little endian
temp = []
while True:
temp.append(n % 10)
n //= 10
if n == 0:
break
temp.append(0)
temp.reverse()
return temp
def to_number(digits):
result = 0;
for x in digits:
result = result * 10 + x
return result
if __name__ == "__main__":
print(compute())
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment