Solutions to Project Euler’s second 50 problems

algorithm
project euler
python
numba
Published

October 12, 2017

These are codes to solve the second 50 problems in Project Euler. These python codes will give solutions in less than 1 second. This is achieved by using the excellent numba package.

Problem 51

def is_prime(n):
    if n < 5:
        return n == 2 or n == 3
    elif n % 2 == 0 or n % 3 == 0:
        return False
    for i in range(5, int(n**0.5)+1, 6):
        if n % i == 0 or n % (i+2) == 0:
            return False
    return True
%%time
def problem51(max_digits=6):
    position_list = [[i, j, k] for i in range(6) for j in range(i+1, 6)
                     for k in range(j+1, 6)]
    for origin in range(10**(max_digits-3)):
        for position in position_list:
            start_digit = 0 if position[0] == 0 else 1
            fail_count = start_digit
            answer = []
            for digit in range(start_digit, 10):
                origin_str = "{:0{}d}".format(origin, max_digits-3)
                expanded_digits = [str(digit)] * 6
                expanded_digits[position[0]] = origin_str[0]
                expanded_digits[position[1]] = origin_str[1]
                expanded_digits[position[2]] = origin_str[2]
                generated_number = int("".join(expanded_digits))
                if is_prime(generated_number) == False:
                    fail_count += 1
                    if fail_count == 3:
                        break
                else:
                    answer.append(generated_number)
            if len(answer) == 8:
                return answer[0]

print(problem51())
121313
CPU times: user 40 ms, sys: 0 ns, total: 40 ms
Wall time: 40.9 ms

Problem 52

%%time
def problem52(limit=10**7):
    for i in range(1, limit):
        sorted_i = sorted(str(i))
        if sorted_i == sorted(str(2*i)):
            if sorted_i == sorted(str(3*i)):
                if sorted_i == sorted(str(4*i)):
                    if sorted_i == sorted(str(5*i)):
                        if sorted_i == sorted(str(6*i)):
                            return i

print(problem52())
142857
CPU times: user 208 ms, sys: 0 ns, total: 208 ms
Wall time: 205 ms

Problem 53

from math import factorial
%%time
def problem53():    
    count = 0
    for n in range(23, 101):
        for r in range(1, n):
            if factorial(n) // (factorial(r) * factorial(n-r)) > 10**6:
                count += 1
    return count

print(problem53())
4075
CPU times: user 12 ms, sys: 0 ns, total: 12 ms
Wall time: 9.28 ms

Problem 54

from urllib.request import urlopen

url = 'https://projecteuler.net/project/resources/p054_poker.txt'
with urlopen(url) as response:
    DATA = response.read().decode()

def get_rank_value(hand):
    CARD_TO_VALUE = dict(zip('23456789TJQKA', range(13)))
    RANK_LIST = ['Royal Flush', 'Straight Flush', 'Four of a Kind',
                 'Full House', 'Flush', 'Straight', 'Three of a Kind',
                 'Two Pairs', 'One Pair', 'High Card']
    RANK = dict(zip(RANK_LIST, range(9,-1,-1)))
    
    cards = [CARD_TO_VALUE[card[0]] for card in hand]
    is_flush = len(set([card[1] for card in hand])) == 1
    card_freqs = sorted([(cards.count(card), card) for card in set(cards)])
    n_cards = len(card_freqs)
    value = sum([card_freqs[i][1]*10**(2*i) for i in range(n_cards)])
    if card_freqs[-1][0] == 4:
        return (RANK['Four of a Kind'], value)
    elif card_freqs[-1][0] == 3:
        if n_cards == 2:
            return (RANK['Full House'], value)
        else:
            return (RANK['Three of a Kind'], value)
    elif card_freqs[-1][0] == 2:
        if n_cards == 3:
            return (RANK['Two Pairs'], value)
        else:
            return (RANK['One Pair'], value)
    elif (((card_freqs[4][1] - card_freqs[0][1]) == 4) or
          ((card_freqs[4][1] == CARD_TO_VALUE['A'] and
            card_freqs[0][1] == CARD_TO_VALUE['2'] and
            card_freqs[3][1] == CARD_TO_VALUE['5']))):
        if is_flush:
            if card_freqs[0][1] == CARD_TO_VALUE['A']:
                return (RANK['Royal Flush'], value)
            else:
                return (RANK['Straight Flush'], value)
        else:
            return (RANK['Straight'], value)
    elif is_flush:
        return (RANK['Flush'], value)
    else:
        return (RANK['High Card'], value)
%%time
def problem54():
    lines = DATA.strip().split('\n')
    count = 0
    for line in lines:
        cards = line.split()
        player_1 = cards[:5]
        player_2 = cards[5:]
        if get_rank_value(player_1) > get_rank_value(player_2):
            count += 1
    return count

print(problem54())
376
CPU times: user 20 ms, sys: 0 ns, total: 20 ms
Wall time: 17.8 ms

Problem 55

%%time
def problem55(limit=10000):
    c = 0
    for i in range(limit):
        n = i
        n_t = int(str(n)[::-1])
        is_Lychrel = True
        for j in range(49):
            n = n + n_t
            n_t = int(str(n)[::-1])
            if n == n_t:
                is_Lychrel = False
                break
        if is_Lychrel:
            c += 1
    return c
    
print(problem55())
249
CPU times: user 40 ms, sys: 0 ns, total: 40 ms
Wall time: 40.7 ms

Problem 56

%%time
def problem56():
    max_digital_sum = 0
    for a in range(100):
        for b in range(100):
            d_sum = sum([int(x) for x in str(a**b)])
            if d_sum > max_digital_sum:
                max_digital_sum = d_sum
    return max_digital_sum

print(problem56())
972
CPU times: user 128 ms, sys: 0 ns, total: 128 ms
Wall time: 130 ms

Problem 57

from fractions import Fraction
%%time
def problem57():    
    x = 2 + Fraction(1, 2)
    count = 0
    for i in range(1000):
        y = x - 1
        if len(str(y.numerator)) > len(str(y.denominator)):
            count += 1
        x = 2 + Fraction(1, x)
    return count

print(problem57())
153
CPU times: user 40 ms, sys: 0 ns, total: 40 ms
Wall time: 40.9 ms

Problem 58

from numba import jit

@jit
def is_prime(n):
    if n < 5:
        return n == 2 or n == 3
    elif n % 2 == 0 or n % 3 == 0:
        return False
    for i in range(5, int(n**0.5)+1, 6):
        if n % i == 0 or n % (i+2) == 0:
            return False
    return True
%%time
@jit
def problem58():
    x = 1
    gap = 0
    n_prime_x10 = 0
    n_x = 1
    while True:
        gap = gap + 2
        for i in range(3):
            x = x + gap
            if is_prime(x):
                n_prime_x10 += 10
        x = x + gap
        n_x += 4
        if n_prime_x10 < n_x:
            return int(x**0.5)

print(problem58())
26241
CPU times: user 216 ms, sys: 4 ms, total: 220 ms
Wall time: 220 ms

Problem 59

from urllib.request import urlopen
    
url = 'https://projecteuler.net/project/resources/p059_cipher.txt'
with urlopen(url) as response:
    DATA = response.read().decode()
%%time
def problem59():
    cipher = list(map(lambda x: int(x), DATA.rstrip().split(',')))
    length = len(cipher)
    ord_a = ord('a')
    for i in range(ord_a, ord_a+26):
        for j in range(ord_a, ord_a+26):
            for k in range(ord_a, ord_a+26):
                key = ([i, j, k] * (length//3+1))[:length]
                message = bytes([x^y for x, y in zip(cipher, key)])
                if b'that' in message and b'have' in message:
                    return sum([ord(x) for x in message.decode()])

print(problem59())
107359
CPU times: user 332 ms, sys: 0 ns, total: 332 ms
Wall time: 333 ms

Problem 60

import numpy as np
from numba import jit, int8, int64

@jit(int8[:](int64))
def create_sieve_of_halfprimes(n):
    sieve = np.ones(n//2, dtype=np.int8)
    for i in range(3, int(n**0.5)+1, 2):
        if sieve[i//2]:
            sieve[i*i//2::i] = 0
    return sieve

@jit(int8[:,:](int8[:], int64[:]))
def create_check_matrix(sieve, primes):
    check_matrix = np.zeros((len(primes), len(primes)), dtype=np.int8)
    for i1 in range(len(primes)):
        for i2 in range(i1+1, len(primes)):
            half_prime12 = (primes[i1]
                            * 10**(int(np.log10(primes[i2]))+1)
                            + primes[i2]) // 2
            if sieve[half_prime12]:
                half_prime21 = (primes[i2]
                                * 10**(int(np.log10(primes[i1]))+1)
                                + primes[i1]) // 2
                if sieve[half_prime21]:
                    check_matrix[i1, i2] = 1
    return check_matrix

@jit(int64(int64[:], int8[:,:]))
def find_answer(primes, check_matrix):
    for a1 in range(len(primes)):
        for a2 in range(a1+1, len(primes)):
            if check_matrix[a1, a2]:
                for a3 in range(a2+1, len(primes)):
                    if check_matrix[a2, a3] and check_matrix[a1, a3]:
                        for a4 in range(a3+1, len(primes)):
                            if (check_matrix[a3, a4] and check_matrix[a2, a4]
                                    and check_matrix[a1, a4]):
                                for a5 in range(a4+1, len(primes)):
                                    if (check_matrix[a4, a5]
                                            and check_matrix[a3, a5]
                                            and check_matrix[a2, a5]
                                            and check_matrix[a1, a5]):
                                        return (primes[a1] + primes[a2]
                                                + primes[a3] + primes[a4]
                                                + primes[a5])
    return 0
%%time
def problem60(limit=10**4):
    sieve = create_sieve_of_halfprimes(limit**2)
    primes = 2*np.nonzero(sieve[:limit//2])[0][1:] + 1
    check_matrix = create_check_matrix(sieve, primes)
    return find_answer(primes, check_matrix)

print(problem60())
26033
CPU times: user 296 ms, sys: 4 ms, total: 300 ms
Wall time: 298 ms

Problem 61

def create_polygonal_numbers(k):
    l = []
    p = n = 1
    while p < 10000:
        if p >= 1000:
            l.append(p)
        n = n + 1
        p = n * ((k-2)*n - k + 4) // 2
    return l

def check(a, b, c, d, e, f, polygonal_dict):
    ab = a*100 + b
    bc = b*100 + c
    cd = c*100 + d
    de = d*100 + e
    ef = e*100 + f
    fa = f*100 + a
    for i1 in polygonal_dict[ab]:
        for i2 in polygonal_dict[bc]:
            for i3 in polygonal_dict[cd]:
                for i4 in polygonal_dict[de]:
                    for i5 in polygonal_dict[ef]:
                        for i6 in polygonal_dict[fa]:
                            if len(set([i1,i2,i3,i4,i5,i6])) == 6:
                                return True
    return False
%%time
def problem61():
    polygonal_dict = dict()
    for k in range(3, 9):
        for n in create_polygonal_numbers(k):
            if n not in polygonal_dict:
                polygonal_dict[n] = [k]
            else:
                polygonal_dict[n].append(k)
                
    cyclic = dict()
    for n in polygonal_dict:
        a, b = (n//100, n%100)
        if b < 10:
            continue
        if a not in cyclic:
            cyclic[a] = [b]
        else:
            cyclic[a].append(b)

    for a in cyclic:
        for b in cyclic[a]:
            if b not in cyclic:
                continue
            for c in cyclic[b]:
                if c not in cyclic:
                    continue
                for d in cyclic[c]:
                    if d not in cyclic:
                        continue
                    for e in cyclic[d]:
                        if e not in cyclic:
                            continue
                        for f in cyclic[e]:
                            if f not in cyclic:
                                continue
                            if a not in cyclic[f]:
                                continue
                            if check(a, b, c, d, e, f, polygonal_dict):
                                return sum([a,b,c,d,e,f]) * 101

print(problem61())
28684
CPU times: user 4 ms, sys: 0 ns, total: 4 ms
Wall time: 741 µs

Problem 62

from collections import Counter
%%time
def problem62():    
    k = 1
    while True:
        cubes = []
        for n in range(int(int('1'*k)**(1/3))+1, int(int('9'*k)**(1/3))+1):
            cubes.append(n**3)
        arranged_cubes = [''.join(sorted(str(x))) for x in cubes]    
        counter = Counter(arranged_cubes)
        for c in list(counter):
            if counter[c] == 5:
                return cubes[arranged_cubes.index(c)]
        k += 1

print(problem62())
127035954683
CPU times: user 28 ms, sys: 0 ns, total: 28 ms
Wall time: 28.3 ms

Problem 63

%%time
def problem63():
    n = 1
    c = 1
    while len(str(9**n)) >= n:
        for i in range(2, 10):
            if i**n >= 10**(n-1):
                c += 1
        n += 1
    return c

print(problem63())
49
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 141 µs

Problem 64

def count_period(n):
    if n == int(n**0.5)**2:
        return 0
    b = 0
    c = 1
    l = []
    while True:
        if (b,c) in l:
            return len(l) - l.index((b,c))
        l.append((b,c))
        b = int((n**0.5 + b) / c) * c - b
        c = (n - b**2) // c
%%time
def problem64(limit=10**4):
    c = 0
    for i in range(2, limit+1):
        if count_period(i) % 2 == 1:
            c += 1
    return c

print(problem64())
1322
CPU times: user 420 ms, sys: 0 ns, total: 420 ms
Wall time: 419 ms

Problem 65

from fractions import Fraction
%%time
def problem65():
    fraction_list = [2]
    for i in range(1, 34):
        fraction_list += [1, 2*i, 1]
    rational_approx = Fraction(fraction_list[-1])
    for a in fraction_list[-2::-1]:
        rational_approx = a + 1 / rational_approx
    return sum([int(x) for x in str(rational_approx.numerator)])

print(problem65())
272
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 1.02 ms

Problem 66

from fractions import Fraction

def diophantine_solution(D):
    if D == int(D**0.5)**2:
        return 1
    b = 0
    c = 1
    a = a0 = int(D**0.5)
    fraction_list = []
    while True:
        a = int((D**0.5 + b) / c)
        if a == 2*a0:
            break
        fraction_list.append(a)
        b = a*c - b
        c = (D - b**2) // c
    rational_approx = Fraction(fraction_list[-1])
    for a in fraction_list[-2::-1]:
        rational_approx = a + 1 / rational_approx
    if rational_approx.numerator**2 - D*rational_approx.denominator**2 == 1:
        return rational_approx.numerator
    rational_approx = rational_approx + a0
    for a in fraction_list[::-1]:
        rational_approx = a + 1 / rational_approx
    return rational_approx.numerator
%%time
def problem66(limit=1000):
    x_max = 3
    d_max = 2
    for i in range(3, limit+1):
        x = diophantine_solution(i)
        if x > x_max:
            x_max = x
            d_max = i
    return d_max

print(problem66())
661
CPU times: user 96 ms, sys: 0 ns, total: 96 ms
Wall time: 93.3 ms

Problem 67

from urllib.request import urlopen
    
url = 'https://projecteuler.net/project/resources/p067_triangle.txt'
with urlopen(url) as response:
    DATA = response.read().decode()
%%time
def problem67():
    triangle = [[int(y) for y in x.split()] for x in DATA.rstrip().split('\n')]
    route = [59]
    for i, row in enumerate(triangle[1:]):
        current = [row[0] + route[0]] \
            + [x + max(route[j], route[j+1]) for j, x in enumerate(row[1:-1])] \
            + [row[-1] + route[-1]]
        route = current
    return max(route)

print(problem67())
7273
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 2.3 ms

Problem 68

%%time
def problem68():
    solutions = []
    a = 10
    for b in range(1, 10):
        for c in range(1, 10):
            if b == c:
                continue
            for d in range(1, 10):
                if d in [b, c]:
                    continue
                e = a + b - d
                if e < 1 or e > 9 or e in [b, c, d]:
                    continue
                for f in range(1, 10):
                    if f in [b, c, d, e]:
                        continue
                    g = c + d - f
                    if g < 1 or g > 9 or g in [b, c, d, e, f]:
                        continue
                    for h in range(1, 10):
                        if h in [b, c, d, e, f, g]:
                            continue
                        i = e + f - h
                        j = g + h - b
                        if i < 1 or j < 1 or i > 9 or j > 9:
                            continue
                        if len(set([a,b,c,d,e,f,g,h,i,j])) == 10:
                            solutions.append([a,b,c,d,c,e,f,e,g,h,g,i,j,i,b])
    ordered_solutions = []
    for solution in solutions:
        i_min = solution.index(min(solution[0], solution[3],
                                   solution[6], solution[9], solution[12]))
        ordered_solution = solution[i_min:] + solution[:i_min]
        ordered_solution_to_int = int(''.join(
            [str(x) for x in ordered_solution]))
        ordered_solutions.append(ordered_solution_to_int)
    return max(ordered_solutions)

print(problem68())
6531031914842725
CPU times: user 0 ns, sys: 4 ms, total: 4 ms
Wall time: 1.34 ms

Problem 69

import numpy as np
%%time
def problem69(limit=10**6):    
    sieve = np.ones(limit+1)
    n_per_phi_n_list = np.ones(limit+1)
    for i in range(2, int(limit**0.5)+1):
        if sieve[i] == 1:
            sieve[i::i] = 0
            n_per_phi_n_list[i::i] *= i / (i-1)
    return np.argmax(n_per_phi_n_list)

print(problem69())
510510
CPU times: user 28 ms, sys: 0 ns, total: 28 ms
Wall time: 27.5 ms

Problem 70

import numpy as np
from numba import jit

@jit
def create_sieve(n):
    sieve = np.ones(n, dtype=np.int8)
    sieve[0] = sieve[1] = 0
    sieve[4::2] = 0
    for i in range(3, int(n**0.5)+1, 2):
        if sieve[i]:
            sieve[i*i::i] = 0
    return sieve
%%time
def problem70(limit=10**7):
    sieve = create_sieve(limit)
    prime_list = np.nonzero(sieve)[0]
    nb_primes = len(prime_list)
    n_min = 87109
    ratio_min = 87109 / 79180
    i_max = np.nonzero((prime_list ** 2) >= 10**7)[0][0]-1
    for i in range(i_max, -1, -1):
        if prime_list[i] / (prime_list[i]-1) >= ratio_min:
            break
        for j in range(i+1, nb_primes):
            i_times_j = prime_list[i] * prime_list[j]
            if i_times_j >= 10**7:
                break
            phi_i_times_j = (prime_list[i]-1) * (prime_list[j]-1)
            ratio = i_times_j / phi_i_times_j
            if ratio < ratio_min:
                if sorted(str(i_times_j)) == sorted(str(phi_i_times_j)):
                    n_min = i_times_j
                    ratio_min = ratio
    return n_min

print(problem70())
8319823
CPU times: user 280 ms, sys: 0 ns, total: 280 ms
Wall time: 279 ms

Problem 71

from fractions import Fraction
%%time
def problem71(limit=10**6):
    d_min = 5
    n_min = 2
    for d in range(2, limit+1):
        n = 3 * d // 7
        if d % 7 == 0:
            n = n-1
        if n * d_min > d * n_min:
            n_min = n
            d_min = d
    return Fraction(n_min, d_min).numerator

print(problem71())
428570
CPU times: user 220 ms, sys: 0 ns, total: 220 ms
Wall time: 219 ms

Problem 72

import numpy as np
from numba import jit
%%time
@jit
def problem72(limit=10**6):
    sieve = np.ones(limit+1)
    phi_n_list = np.arange(limit+1)
    phi_n_list[1] = 0
    for i in range(2, limit+1):
        if sieve[i] == 1:
            sieve[i*i::i] = 0
            phi_n_list[i::i] *= (i-1)
            phi_n_list[i::i] //= i
    return sum(phi_n_list)

print(problem72())
303963552391
CPU times: user 360 ms, sys: 0 ns, total: 360 ms
Wall time: 359 ms

Problem 73

from numba import jit

@jit
def count_Stern_Brocot(limit, left_n, left_d, right_n, right_d):
    mid_n = left_n + right_n
    mid_d = left_d + right_d
    if mid_d > limit:
        return 0
    else:
        count = 1
        count += count_Stern_Brocot(limit, left_n, left_d, mid_n, mid_d)
        count += count_Stern_Brocot(limit, mid_n, mid_d, right_n, right_d)
        return count
%%time
def problem73(limit=12000):
    return count_Stern_Brocot(12000, 1, 3, 1, 2)

print(problem73())
7295372
CPU times: user 124 ms, sys: 0 ns, total: 124 ms
Wall time: 124 ms

Problem 74

from math import factorial
from itertools import permutations

def count_permuation(n):
    p = set(permutations(str(n)))
    if str(n)[0] == '1':
        new_str = '0' + str(n)[1:]
        p = p.union(set(permutations(new_str)))
    if str(n)[:2] == '11':
        new_str = '00' + str(n)[2:]
        p = p.union(set(permutations(new_str)))
    if str(n)[:3] == '111':
        new_str = '000' + str(n)[3:]
        p = p.union(set(permutations(new_str)))
    if str(n)[:4] == '1111':
        new_str = '0000' + str(n)[4:]
        p = p.union(set(permutations(new_str)))
    if str(n)[:5] == '11111':
        p = p.add(str(n)[5:]+'00000')
    c = 0
    for e in p:
        if e[0] != '0':
            c += 1
    return c
%%time
def problem74():
    fac = [factorial(x) for x in range(10)]
    a = [0] * 2200000
    answer_list = []
    for i1 in range(10):
        for i2 in range(i1, 10):
            for i3 in range(i2, 10):
                for i4 in range(i3, 10):
                    for i5 in range(i4, 10):
                        for i6 in range(i5, 10):
                            i = i1*10**5+i2*10**4+i3*1000+i4*100+i5*10+i6
                            chain = []
                            j = i
                            while (a[j] == 0) and (j not in chain):
                                chain.append(j)
                                s = 0
                                jr = j
                                while jr != 0:
                                    s += fac[jr % 10]
                                    jr //= 10
                                j = s
                            if j in chain:
                                for t in range(len(chain)):
                                    a[chain[t]] = len(chain) - t
                                    if chain[t] == j:
                                        break
                                for t1 in range(t+1, len(chain)):
                                    a[chain[t1]] = len(chain) - t
                            else:
                                for t, k in enumerate(chain):
                                    a[k] = len(chain) - t + a[j]
                            if a[i] == 60:
                                answer_list.append(i)
    return sum([count_permuation(answer) for answer in answer_list])

print(problem74())
402
CPU times: user 20 ms, sys: 0 ns, total: 20 ms
Wall time: 18.7 ms

Problem 75

from numba import jit

@jit
def gcd(m, n):
    if m == 0:
        return n
    return gcd(n % m, m)
%timeit
def problem75(limit=1500000):
    d = [0] * (limit + 1)
    for n in range(1, 1000):
        for m in range(n+1, 1000, 2):
            if gcd(m, n) != 1:
                continue
            L = 2 * m * (m + n)
            k_max = limit // L
            for k in range(1, k_max+1):
                d[L*k] += 1
    c = 0
    for i in range(limit+1):
        if d[i] == 1:
            c += 1
    return c

print(problem75())
161667

Problem 76

import numpy as np
%%time
def problem76():
    A = np.zeros((101, 101), dtype=np.int)
    for i in range(1, 101):
        for j in range(1, i+1):
            A[i, j] = 1
            for k in range(j, i//2+1):
                A[i, j] += A[i-k, k]
    return A[100, 1] - 1

print(problem76())
190569291
CPU times: user 24 ms, sys: 0 ns, total: 24 ms
Wall time: 21.3 ms

Problem 77

import numpy as np

def is_prime(n):
    if n < 5:
        return n == 2 or n == 3
    elif n % 2 == 0 or n % 3 == 0:
        return False
    for i in range(5, int(n**0.5)+1, 6):
        if n % i == 0 or n % (i+2) == 0:
            return False
    return True
%%time
def problem77():
    A = np.zeros((101, 101), dtype=np.int)
    for i in range(1, 101):
        for j in range(1, i+1):
            if is_prime(i):
                A[i, j] = 1
            for k in range(j, i//2+1):
                if is_prime(k):
                    A[i, j] += A[i-k, k]
            if A[i, j] > 5000 + is_prime(i):
                return i

print(problem77())
71
CPU times: user 20 ms, sys: 0 ns, total: 20 ms
Wall time: 20.5 ms

Problem 78

from numba import jit
%%time
@jit
def problem78(limit=60000):
    p = [0] * limit
    p[0] = 1
    p[1] = 1
    for i in range(2, limit):
        k = 1
        s = 1
        g_k = 0
        while True:
            g_k += 2*k - 1
            if g_k > i:
                break
            p[i] = p[i] + s*p[i-g_k]
            g_k += k
            if g_k > i:
                break
            p[i] = p[i] + s*p[i-g_k]
            k += 1
            s = -s
        p[i] = p[i] % 10**6
        if p[i] == 0:
            return i
        
print(problem78())
55374
CPU times: user 100 ms, sys: 0 ns, total: 100 ms
Wall time: 102 ms

Problem 79

from urllib.request import urlopen
from itertools import permutations, combinations
    
url = 'https://projecteuler.net/project/resources/p079_keylog.txt'
with urlopen(url) as response:
    DATA = response.read().decode()
%%time
def problem79():
    for p in permutations(''.join(sorted(list(set(DATA) - {'\n'})))):
        passcode = ''.join(p)
        passcode_keys = [''.join(x) for x in combinations(passcode, 3)]
        flag = True
        for key in DATA.split():
            if key not in passcode_keys:
                flag = False
                break
        if flag == True:
            return passcode

print(problem79())
73162890
CPU times: user 300 ms, sys: 0 ns, total: 300 ms
Wall time: 300 ms

Problem 80

from decimal import getcontext, Decimal
%%time
def problem80():
    getcontext().prec = 102
    s = 0
    for i in range(1, 101):
        if i not in [y**2 for y in range(1, 11)]:
            s += sum([int(x) for x in str(Decimal(i).sqrt())[:101]
                      if x != '.'])
    return s

print(problem80())
40886
CPU times: user 4 ms, sys: 0 ns, total: 4 ms
Wall time: 4.47 ms

Problem 81

from urllib.request import urlopen
    
url = 'https://projecteuler.net/project/resources/p081_matrix.txt'
with urlopen(url) as response:
    DATA = response.read().decode()
%%time
def problem81():
    matrix = [[int(y) for y in x.split(',')] for x in DATA.split()]
    path_sum = [[0]*80]*80
    for i in range(80):
        for j in range(80):
            if i == 0 and j == 0:
                min_path = 0
            elif i == 0:
                min_path = path_sum[i][j-1]
            elif j == 0:
                min_path = path_sum[i-1][j]
            else:
                min_path = min(path_sum[i][j-1], path_sum[i-1][j])
            path_sum[i][j] = matrix[i][j] + min_path
    return path_sum[79][79]

print(problem81())
427337
CPU times: user 4 ms, sys: 0 ns, total: 4 ms
Wall time: 3.94 ms

Problem 82

from urllib.request import urlopen
import numpy as np
    
url = 'https://projecteuler.net/project/resources/p082_matrix.txt'
with urlopen(url) as response:
    DATA = response.read().decode()
%%time
def problem82():
    matrix = np.array([[int(y) for y in x.split(',')] for x in DATA.split()], 
                      dtype='uint16')
    path_sum = np.zeros((80, 80), dtype='uint32')
    min_path = np.zeros((80, 80), dtype='uint32')

    for j in range(80):
        for i in range(80):
            if j == 0:
                min_path = [0]
            else:
                min_path = [path_sum[i, j-1]]
                for k in range(80):
                    if k < i:
                        min_path.append(matrix[k:i, j].sum()
                                        + path_sum[k, j-1])
                    if k > i:
                        min_path.append(matrix[i+1:k+1, j].sum()
                                        + path_sum[k, j-1])
            path_sum[i, j] = matrix[i, j] + min(min_path)
    return path_sum[:,79].min()

print(problem82())
260324
CPU times: user 956 ms, sys: 0 ns, total: 956 ms
Wall time: 958 ms

Problem 83

from urllib.request import urlopen
import numpy as np
    
url = 'https://projecteuler.net/project/resources/p083_matrix.txt'
with urlopen(url) as response:
    DATA = response.read().decode()
    
def update(i, j, path_sum, matrix, inf):
    min_around = [inf]
    if i > 0:
        min_around.append(path_sum[i-1, j])
    if i < 79:
        min_around.append(path_sum[i+1, j])
    if j > 0:
        min_around.append(path_sum[i, j-1])
    if j < 79:
        min_around.append(path_sum[i, j+1])
    path_sum[i, j] = min(min_around) + matrix[i, j]
%%time
def problem83():
    matrix = np.array([[int(y) for y in x.split(',')] for x in DATA.split()], 
                      dtype='uint16')

    inf = np.iinfo(np.uint32).max // 2
    path_sum = np.zeros((80, 80), dtype='uint32')
    path_sum[:] = inf
    path_sum[0, 0] = matrix[0, 0]

    visited = np.zeros((80, 80), dtype='bool')
    visited[0, 0] = True
    update(0, 1, path_sum, matrix, inf)
    update(1, 0, path_sum, matrix, inf)
    
    while not visited[79, 79]:
        i, j = np.unravel_index(np.ma.argmin(
            np.ma.MaskedArray(path_sum, visited)), (80, 80))
        visited[i, j] = True
        if i > 0 and not visited[i-1, j]:
            update(i-1, j, path_sum, matrix, inf)
        if i < 79 and not visited[i+1, j]:
            update(i+1, j, path_sum, matrix, inf)
        if j > 0 and not visited[i, j-1]:
            update(i, j-1, path_sum, matrix, inf)
        if j < 79 and not visited[i, j+1]:
            update(i, j+1, path_sum, matrix, inf)
    return path_sum[79, 79]

print(problem83())
425185
CPU times: user 360 ms, sys: 0 ns, total: 360 ms
Wall time: 359 ms

Problem 84

from random import randint
%%time
def problem84():
    jail = 10
    g2j = 30
    cc = [2, 17, 33]
    ch = [7, 22, 36]
    double = 0
    table = [0] * 40
    i = 0
    for j in range(100000):
        dice1 = randint(1, 4)
        dice2 = randint(1, 4)
        if dice1 == dice2:
            double += 1
        else:
            double = 0
        if double == 3:
            i = jail
        else:
            i = (i + dice1 + dice2) % 40
        if i in cc:
            cc_open = randint(1, 16)
            if cc_open == 1:
                i = 0
            elif cc_open == 2:
                i = jail
        if i in ch:
            ch_open = randint(1, 16)
            if ch_open == 1:
                i = 0
            elif ch_open == 2:
                i = jail
            elif ch_open == 3:
                i = 11
            elif ch_open == 4:
                i = 24
            elif ch_open == 5:
                i = 39
            elif ch_open == 6:
                i = 5
            elif ch_open in [7, 8]:
                if i == ch[0]:
                    i = 15
                elif i == ch[1]:
                    i = 25
                else:
                    i = 5
            elif ch_open == 9:
                if i == ch[1]:
                    i = 28
                else:
                    i = 12
            elif ch_open == 10:
                i = (i-3)%40
        if i == g2j:
            i = jail
        table[i] += 1
    a, b, c = sorted(table, reverse=True)[:3]
    return '{:02d}{:02d}{:02d}'.format(table.index(a),
                                       table.index(b), table.index(c))

print(problem84())
101516
CPU times: user 232 ms, sys: 0 ns, total: 232 ms
Wall time: 234 ms

Problem 85

%%time
def problem85():
    areas = []
    rectangles = []

    for n in range(1, 3000):
        for m in range(n, 3000//n):
            areas.append(n * m)
            rectangles.append(n * m * (n+1) * (m+1) // 4)

    ds = [int(abs(r - 2e6)) for r in rectangles]
    return areas[ds.index(min(ds))]

print(problem85())
2772
CPU times: user 4 ms, sys: 0 ns, total: 4 ms
Wall time: 6.8 ms

Problem 86

from numba import jit
%%time
@jit
def problem86():
    n_sol = 0
    M = 0
    while n_sol <= 10**6:
        M += 1
        for a_plus_b in range(2, 2*M+1):
            min_square = M**2 + a_plus_b**2
            if min_square == (int(min_square**0.5)**2):
                b_min = max(1, a_plus_b-M)
                b_max = a_plus_b//2
                n_sol += (b_max - b_min + 1)
    return M

print(problem86())
1818
CPU times: user 84 ms, sys: 0 ns, total: 84 ms
Wall time: 83.7 ms

Problem 87

import numpy as np
from numba import jit
%%time
@jit
def problem87():
    limit = 5*10**7-1
    x_max = int(limit**(1/2))
    y_max = int(limit**(1/3))
    z_max = int(limit**(1/4))
    sieve = np.ones(x_max+1, dtype=np.bool)
    sieve[0] = sieve[1] = 0
    for i in range(2, int(limit**0.5)+1):
        if sieve[i] == 1:
            sieve[2*i::i] = 0
    x_list = np.nonzero(sieve[:x_max+1])[0]
    y_list = np.nonzero(sieve[:y_max+1])[0]
    z_list = np.nonzero(sieve[:z_max+1])[0]
    A = np.zeros(limit+1, dtype=np.bool)
    for x in x_list:
        for y in y_list:
            for z in z_list:
                n = x**2 + y**3 + z**4
                if n <= limit:
                    A[n] = 1
    return A.sum()

print(problem87())
1097343
CPU times: user 512 ms, sys: 4 ms, total: 516 ms
Wall time: 514 ms

Problem 88

%%time
def problem88():
    k_max = 12000
    product_max = 2 * k_max
    a_max = k_max // 4
    m_max = k_max
    prod = 1
    mps = [2*k_max] * (k_max+1)
    mps[0] = mps[1] = 0
    for a in range(1, a_max//prod+1):
        prod = a
        for b in range(a, a_max//prod+1):
            prod = a*b
            for c in range(b, a_max//prod+1):
                prod = a*b*c
                for d in range(c, a_max//prod+1):
                    prod = a*b*c*d
                    for e in range(d, a_max//prod+1):
                        prod = a*b*c*d*e
                        for f in range(e, a_max//prod+1):
                            prod = a*b*c*d*e*f
                            for g in range(f, a_max//prod+1):
                                prod = a*b*c*d*e*f*g
                                for h in range(g, a_max//prod+1):
                                    prod = a*b*c*d*e*f*g*h
                                    for i in range(h, a_max//prod+1):
                                        prod = a*b*c*d*e*f*g*h*i
                                        for j in range(i, a_max//prod+1):
                                            prod = a*b*c*d*e*f*g*h*i*j
                                            for k in range(j, a_max//prod+1):
                                                prod = a*b*c*d*e*f*g*h*i*j*k
                                                l_min = max(2, k)
                                                l_max = k_max // (2*prod)
                                                for l in range(l_min,
                                                               l_max+1):
                                                    for m in range(l,
                                                                   k_max+1):
                                                        prod = (a*b*c*d*e*f*g
                                                                *h*i*j*k*l*m)
                                                        s = (a+b+c+d+e+f+g+h
                                                             +i+j+k+l+m)
                                                        kk = prod - s + 13
                                                        if kk > k_max:
                                                            break
                                                        if prod < mps[kk]:
                                                            mps[kk] = prod
    return sum(set(mps))

print(problem88())
7587457
CPU times: user 232 ms, sys: 0 ns, total: 232 ms
Wall time: 235 ms

Problem 89

from urllib.request import urlopen
    
url = 'https://projecteuler.net/project/resources/p089_roman.txt'
with urlopen(url) as response:
    DATA = response.read().decode()
    
def minimize_form(number):
    number = number.replace('VIIII', 'IX')
    number = number.replace('IIII', 'IV')
    number = number.replace('LXXXX', 'XC')
    number = number.replace('XXXX', 'XL')
    number = number.replace('DCCCC', 'CM')
    number = number.replace('CCCC', 'CD')
    return number
%%time
def problem89():
    return (sum([len(x) for x in DATA.split()])
            - sum([len(minimize_form(x)) for x in DATA.split()]))

print(problem89())
743
CPU times: user 4 ms, sys: 0 ns, total: 4 ms
Wall time: 1.1 ms

Problem 90

from itertools import combinations, product

def check(square, pairs):
    if (square[0], square[1]) in pairs or (square[1], square[0]) in pairs:
        return True
    else:
        return False
%%time
def problem90():
    arrangements = [set(x) | {'6', '9'} if ('6' in x or '9' in x)
                    else set(x) for x in combinations('0123456789', 6)]
    squares = ['01', '04', '09', '16', '25', '36', '49', '64', '81']
    c = 0
    for dice1 in arrangements:
        for dice2 in arrangements:
            pairs = list(product(dice1, dice2))
            flag = True
            for square in squares:
                if not check(square, pairs):
                    flag = False
                    break
            if flag == True:
                c += 1
    return c//2


print(problem90())
1217
CPU times: user 360 ms, sys: 0 ns, total: 360 ms
Wall time: 360 ms

Problem 91

from numba import jit
@jit
def problem91():
    s = 0
    for i in range(0, 51):
        for j in range(0, i):
            c = i**2 + j**2
            for a in range(0, 51):
                for b in range(0, 51):
                    if a == 0 and b == 0:
                        continue
                    if a == i and b == j:
                        continue
                    if a**2 + b**2 + (i-a)**2 + (j-b)**2 == c:
                        s += 1
            for k in range(1, 51):
                if k**2 + k**2 == c + (i-k)**2 + (j-k)**2:
                    s += 1
    return 2*s + 50*50

print(problem91())
14234

Problem 92

from numba import jit

@jit
def make_sum_digits2_table():
    sum_digits2_table = [0] * 10**7
    digits2 = [x**2 for x in range(10)]
    for i1 in range(10):
        for i2 in range(10):
            for i3 in range(10):
                for i4 in range(10):
                    for i5 in range(10):
                        for i6 in range(10):
                            for i7 in range(10):
                                num = (i1+i2*10+i3*100+i4*1000+i5*10000
                                       +i6*100000+i7*1000000)
                                sd2 = (digits2[i1]+digits2[i2]+digits2[i3]
                                       +digits2[i4]+digits2[i5]
                                       +digits2[i6]+digits2[i7])
                                sum_digits2_table[num] = sd2
    return sum_digits2_table
%%time
@jit
def problem92():
    sum_digits2_table = make_sum_digits2_table()
    chain_end = [0] * 10**7
    chain_end[1] = 1
    chain_end[89] = 2
    for i in range(1, 10**7):
        j = i
        while chain_end[j] == 0:
            j = sum_digits2_table[j]
        chain_end[i] = chain_end[j]
        
    c = 0
    for i in range(10**7):
        if chain_end[i] == 2:
            c += 1
    return c

print(problem92())
8581146
CPU times: user 432 ms, sys: 16 ms, total: 448 ms
Wall time: 451 ms

Problem 93

from itertools import combinations, permutations
from operator import add, truediv, mul, sub
%%time
def problem93():
    combos = dict()
    ops = [add, truediv, mul, sub,
           lambda a, b: sub(b, a), lambda a, b: truediv(b, a)]
    for four_digit in list(combinations(range(1, 10), 4)):
        A = [0]*10000
        for (a, b, c, d) in permutations(four_digit):
            for op1 in ops:
                for op2 in ops:
                    for op3 in ops:
                        try:
                            num = op3(op2(op1(a,b),c),d)
                        except ZeroDivisionError:
                            continue
                        if num > 0 and num - int(num) < 0.001:
                            A[int(num)] = 1
        A[0] = 1
        combos[four_digit] = A.index(0) - 1
    max_combo = max(combos, key=combos.get)
    return ''.join([str(x) for x in max_combo])

print(problem93())
1258
CPU times: user 304 ms, sys: 0 ns, total: 304 ms
Wall time: 303 ms

Problem 94

from numba import jit
%%time
@jit
def problem94():
    n_max = (10**9 - 2) // 6
    s = 0
    for n in range(2, n_max+1):
        h2 = 3*n*n + 4*n + 1
        if h2 == int(h2**0.5)**2:
            s += (6*n+2)
        h2 = 3*n*n - 4*n + 1
        if h2 == int(h2**0.5)**2:
            s += (6*n-2)
    n = n_max+1
    h2 = 3*n*n - 4*n + 1
    if h2 == int(h2**0.5)**2:
        s += (6*n-2)
    return s

print(problem94())
518408346
CPU times: user 632 ms, sys: 8 ms, total: 640 ms
Wall time: 639 ms

Problem 95

import numpy as np
from numba import jit

@jit
def get_proper_sum(limit):
    sieve = np.ones(limit+1)
    proper_sum = np.ones(limit+1, dtype=np.int32)
    current_n = np.arange(limit+1)
    for p in range(2, int(limit**0.5)+1):
        if sieve[p] == 1:
            sieve[p::p] = 0
            for n in range(p, limit+1, p):
                temp = proper_sum[n]
                while current_n[n] % p == 0:
                    current_n[n] //= p
                    proper_sum[n] = proper_sum[n]*p + temp
    current_n = (current_n != 1) + current_n
    proper_sum = proper_sum * current_n - np.arange(limit+1)
    return proper_sum
%%time
@jit
def problem95():
    limit = 10**6
    proper_sum = get_proper_sum(limit)
    checked = np.zeros(limit+1, dtype=np.int8)
    checked[proper_sum > limit] = -1
    checked[proper_sum == 1] = -1
    checked[1] = -1
    len_max = 0
    start_min = 0
    for i in range(2, limit+1):
        if checked[i] != 0:
            continue
        next_i = i
        while checked[next_i] == 0:
            checked[next_i] = 1
            next_i = proper_sum[next_i]
        if checked[next_i] == 1:
            j = i
            while j != next_i:
                checked[j] = -1
                j = proper_sum[j]
            chain_len = 1
            chain_min = next_i
            j = proper_sum[next_i]
            while j != next_i:
                checked[j] = -1
                chain_len += 1
                if j < chain_min:
                    chain_min = j
                j = proper_sum[j]
            if chain_len > len_max:
                len_max = chain_len
                start_min = chain_min
        else:
            j = i
            while checked[j] == 1:
                checked[j] = -1
                j = proper_sum[j]
    return start_min

print(problem95())
14316
CPU times: user 808 ms, sys: 0 ns, total: 808 ms
Wall time: 808 ms

Problem 96

from urllib.request import urlopen
import numpy as np
    
url = 'https://projecteuler.net/project/resources/p096_sudoku.txt'
with urlopen(url) as response:
    DATA = response.read().decode()

def scan(sudoku):
    for num in range(9):
        for a in range(3):
            for b in range(3):
                if sudoku[a,b,:,:,num].sum() == 1:
                    c_list, d_list = np.nonzero(sudoku[a,b,:,:,num])
                    return a, b, c_list[0], d_list[0], num
    for num in range(9):
        for c in range(3):
            for d in range(3):
                if sudoku[:,:,c,d,num].sum() == 1:
                    a_list, b_list = np.nonzero(sudoku[:,:,c,d,num])
                    return a_list[0], b_list[0], c, d, num
    for num in range(9):
        for a in range(3):
            for c in range(3):
                if sudoku[a,:,c,:,num].sum() == 1:
                    b_list, d_list = np.nonzero(sudoku[a,:,c,:,num])
                    return a, b_list[0], c, d_list[0], num
    for a in range(3):
        for b in range(3):
            for c in range(3):
                for d in range(3):
                    if sudoku[a,b,c,d,:].sum() == 1:
                        num_list = np.nonzero(sudoku[a,b,c,d,:])
                        return a, b, c, d, num_list[0][0]
    return 0, 0, 0, 0, -1

def fill(sudoku, a, b, c, d, num):
    sudoku[a, b, :, :, num] = 0
    sudoku[:, :, c, d, num] = 0
    sudoku[a, :, c, :, num] = 0
    sudoku[a, b, c, d, :] = 0
    sudoku[a, b, c, d, num] = 10

def scan_and_fill(sudoku):
    a, b, c, d, num = scan(sudoku)
    if num == -1:
        return False
    fill(sudoku, a, b, c, d, num)
    return True

def solve(sudoku):
    unknown = 81 - (sudoku == 10).sum()
    while unknown != 0:
        if not scan_and_fill(sudoku):
            a, b, c, d = np.unravel_index(sudoku.sum(axis=4).argmin(),
                                          dims=(3,3,3,3))
            num_list = np.nonzero(sudoku[a,b,c,d])[0]
            for num in num_list:
                new_sudoku = sudoku.copy()
                fill(new_sudoku, a, b, c, d, num)
                if solve(new_sudoku):
                    sudoku[:] = new_sudoku[:]
                    return True
            return False
        unknown -= 1
    return True

def read(quiz):
    sudoku = np.ones((3, 3, 3, 3, 9), dtype=np.uint8)
    for row in range(9):
        for col in range(9):
            a = row // 3
            b = row % 3
            c = col // 3
            d = col % 3
            num = int(quiz[row][col])
            if num != 0:
                fill(sudoku, a, b, c, d, num-1)
    return sudoku
%%time
def problem96():
    s = 0
    for i in range(50):
        quiz = DATA.split()[11*i+2:11*i+11]
        sudoku = read(quiz)
        solve(sudoku)
        a, b, c = np.nonzero(sudoku[0,0,0,:])[1]
        s += (a+1)*100 + (b+1)*10 + (c+1)
    return s
    
print(problem96())
24702
CPU times: user 552 ms, sys: 0 ns, total: 552 ms
Wall time: 552 ms

Problem 97

%%time
def problem97():
    28433*2**7830457+1
    two_to_100000 = (2**100000) % 10**10
    two_to_30457 = (2**30457) % 10**10
    return (28433 * two_to_100000**78 * two_to_30457 + 1) % 10**10

print(problem97())
8739992577
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 86.5 µs

Problem 98

from urllib.request import urlopen
from collections import defaultdict
    
url = 'https://projecteuler.net/project/resources/p098_words.txt'
with urlopen(url) as response:
    DATA = response.read().decode()
%%time
def problem98():  
    word_list = DATA.strip('"').split('","')
    anagram_dict = defaultdict(list)
    for word in word_list:
        anagram_dict[''.join(sorted(word))].append(word)
    filter_dict = dict((key, value)
                       for (key, value) in anagram_dict.items() if len(value)>1)

    square_dict = dict()
    for i in set(len(key) for key in filter_dict.keys()):
        square_max = int((10**i-1)**0.5)
        square_min = int((10**(i-1)-1)**0.5)+1
        square_dict[i] = [x**2 for x in range(square_min, square_max+1)]
        
    s_max = 0
    for key in filter_dict:
        if len(filter_dict[key]) == 2:
            word1, word2 = filter_dict[key]
            for square in square_dict[len(key)]:
                match = {w: s for w, s in zip(word1, str(square))}
                reverse_match = {s: w for w, s in zip(word1, str(square))}
                if len(match) != len(key) or len(reverse_match) != len(key):
                    continue
                square2 = int(''.join([match[c] for c in word2]))
                if square2 in square_dict[len(key)]:
                    if square > s_max:
                        s_max = square
                    if square2 > s_max:
                        s_max = square2
    return s_max

print(problem98())
18769
CPU times: user 116 ms, sys: 0 ns, total: 116 ms
Wall time: 115 ms

Problem 99

from urllib.request import urlopen
    
url = 'https://projecteuler.net/project/resources/p099_base_exp.txt'
with urlopen(url) as response:
    DATA = response.read().decode()
%%time
def problem99():
    x_max = 1
    y_max = 1
    line_max = 0
    for line, pair in enumerate(DATA.split(), 1):
        x, y = [int(a) for a in pair.split(',')]
        if x > x_max**(y_max/y):
            x_max, y_max = x, y
            line_max = line
    return line_max

print(problem99())
709
CPU times: user 4 ms, sys: 0 ns, total: 4 ms
Wall time: 1.05 ms

Problem 100

%%time
def problem100():
    pell_fund = (1, 1)
    x, y = pell_fund
    while x <= 2*10**12-1:
        x, y = 3*x + 4*y, 2*x + 3*y
    return (y+1)//2
    
print(problem100())
756872327473
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 122 µs
Back to top