2019-08-29 06:29:12 +02:00
|
|
|
from functools import lru_cache
|
|
|
|
|
2019-08-16 05:26:47 +02:00
|
|
|
|
|
|
|
def euler_078():
|
2019-08-29 06:29:12 +02:00
|
|
|
piles_look_up = {}
|
|
|
|
|
|
|
|
def count_piles_limited(n, max_size):
|
|
|
|
if max_size == 1:
|
|
|
|
return 1
|
|
|
|
try:
|
|
|
|
return piles_look_up[(n, max_size)]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
count = 0
|
|
|
|
for i in range(1, n):
|
|
|
|
n_new = n - i
|
|
|
|
count_i = count_piles_limited(n_new, min([n_new, i]))
|
|
|
|
count += count_i
|
|
|
|
piles_look_up[(n, i)] = count
|
|
|
|
# for n itself
|
|
|
|
count += 1
|
|
|
|
piles_look_up[(n, n)] = count
|
|
|
|
return count
|
|
|
|
|
|
|
|
piles_look_up_modular = {}
|
|
|
|
|
|
|
|
def count_piles_limited_modular(n, max_size, modulu):
|
|
|
|
if max_size == 1:
|
|
|
|
return 1
|
|
|
|
try:
|
|
|
|
return piles_look_up_modular[(n, max_size)]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
count = 0
|
|
|
|
for i in range(1, n):
|
|
|
|
n_new = n - i
|
|
|
|
count_i = count_piles_limited_modular(
|
|
|
|
n_new, min([n_new, i]), modulu)
|
|
|
|
count = (count + count_i) % modulu
|
|
|
|
piles_look_up_modular[(n, i)] = count
|
|
|
|
# for n itself
|
|
|
|
count = (count + 1) % modulu
|
|
|
|
piles_look_up_modular[(n, n)] = count
|
|
|
|
return count
|
|
|
|
|
|
|
|
@lru_cache(maxsize=1000000)
|
|
|
|
def count_piles(n, max_size):
|
|
|
|
if max_size == 0 or max_size == 1:
|
|
|
|
return 1
|
|
|
|
if n == 0 or n == 1:
|
|
|
|
return 1
|
|
|
|
|
|
|
|
count = 0
|
|
|
|
for k in range(1, max_size + 1):
|
|
|
|
n_new = n - k
|
|
|
|
max_size_new = min([k, n_new])
|
|
|
|
count += count_piles(n_new, max_size_new)
|
|
|
|
return count
|
|
|
|
|
|
|
|
"""
|
|
|
|
I tried to implement my own algorithm but I would run out of memory.
|
|
|
|
I tried to find a pattern in how the count can be calculated directly,
|
|
|
|
but I could not find a pattern. I then looked up partioning and
|
|
|
|
implemented the algorithm explained here [1]. This was literally the first
|
|
|
|
time in my life that I have learned about generator functions. Once I
|
|
|
|
used this algorithm it was easy. I definitely want to learn more about
|
|
|
|
generator functions.
|
|
|
|
|
|
|
|
[1] https://www.coursera.org/lecture/enumerative-combinatorics/computing-the-number-of-partitions-via-the-pentagonal-theorem-CehOM
|
|
|
|
"""
|
|
|
|
|
|
|
|
def sign(n):
|
|
|
|
if n % 2 == 0:
|
|
|
|
return -1
|
|
|
|
return 1
|
|
|
|
|
|
|
|
@lru_cache(maxsize=1000000)
|
|
|
|
def euler_identity(n):
|
|
|
|
r = (3 * n * n - n) // 2
|
|
|
|
return r
|
|
|
|
|
|
|
|
@lru_cache(maxsize=1000000)
|
|
|
|
def p(n):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
if n == 0:
|
|
|
|
return 1
|
|
|
|
if n == 1:
|
|
|
|
return 1
|
|
|
|
|
|
|
|
m = 1000000
|
|
|
|
r = 0
|
|
|
|
for i in range(1, n):
|
|
|
|
s = sign(i)
|
|
|
|
|
|
|
|
e = euler_identity(i)
|
|
|
|
new_n = n - e
|
|
|
|
if new_n < 0:
|
|
|
|
break
|
|
|
|
if m:
|
|
|
|
r = (r + s * p(new_n)) % m
|
|
|
|
else:
|
|
|
|
r = r + s * p(new_n)
|
|
|
|
|
|
|
|
e = euler_identity(-i)
|
|
|
|
new_n = n - e
|
|
|
|
if new_n < 0:
|
|
|
|
break
|
|
|
|
if m:
|
|
|
|
r = (r + s * p(new_n)) % m
|
|
|
|
else:
|
|
|
|
r = r + s * p(new_n)
|
|
|
|
return r
|
|
|
|
|
|
|
|
for n in range(1, 100000):
|
|
|
|
a = p(n)
|
|
|
|
if a == 0:
|
|
|
|
return n
|
|
|
|
break
|
2019-08-16 05:26:47 +02:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
print("e078.py: " + str(euler_078()))
|
2019-08-29 06:29:12 +02:00
|
|
|
assert(euler_078() == 55374)
|