Euler Problem 003

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.