Euler Problem 003

Back to overview.

The prime factors of 13195 are 5, 7, 13 and 29.

What is the largest prime factor of the number 600851475143?

We start by writing a function which calculates all primes till a certain value using the Sieve of Eratosthenes.

In [1]:
def get_primes_smaller(number):
    primes = []
    prospects = [n for n in range(2, number)]
    while prospects:
        p = prospects[0]
        prospects = [x for x in prospects if x % p != 0]
        primes.append(p)
    return primes

assert(get_primes_smaller(0) == [])
assert(get_primes_smaller(10) == [2, 3, 5, 7,])

Now we create a function which does prime factorization. It is very important that we only test primes smaller than the squre root. Otherwise the complexity becomes too big.

In [2]:
import math

def get_prime_factors(number):
    possible_factors = get_primes_smaller(math.ceil(math.sqrt(number)))
    remainder = number
    factors = []
    for p in possible_factors:
        while remainder % p == 0:
            remainder /= p
            factors.append(p)
        if remainder == 1:
            break
    if remainder != 1:
        factors.append(remainder)
    return factors

assert(get_prime_factors(7) == [7])
assert(get_prime_factors(12) == [2, 2, 3])    
assert(get_prime_factors(88) == [2, 2, 2, 11]) 
assert(get_prime_factors(13195) == [5, 7, 13, 29])

Now we can go ahead an brute force the solution.

In [3]:
def get_largest_prime(number):
    return get_prime_factors(number)[-1]

assert(get_largest_prime(13195) == 29)
#print(get_largest_prime(600851475143))
print(6857) # computed the previously but remove it so that we can reexecute the complete kernel
6857

Okay, actually we can brute force, but it is really slow. A better solution is to write a prime number generator which calculates the next number on demand.

In [4]:
def is_prime(n, smaller_primes):
    for s in smaller_primes:
        if n % s == 0:
            return False
        if s * s > n:
            return True
    return True

def prime_generator_function():
    primes = [2, 3, 5, 7]
    for p in primes:
        yield p
    while True:
        p += 2
        if is_prime(p, primes):
            primes.append(p)
            yield p
            
def get_prime_factors(number):
    prime_generator = prime_generator_function()
    remainder = number
    factors = []
    for p in prime_generator:
        while remainder % p == 0:
            remainder /= p
            factors.append(p)
        if remainder == 1 or p * p > number:
            break
    if remainder != 1:
        factors.append(remainder)
    return factors

print(get_largest_prime(600851475143))
6857

Here we go. Obviously much better than precalculation primes that we never need.