4

I'm trying to find the largest prime factor of a given number (600851475143) using Python. I've made the following code, but the problem is, it takes forever, probably because it's iterating through lists millions of times. How to optimize this process?

def factors(n):
    list = []
    i = 1
    while i < n:
        if n % i == 0:
            list.append(i)
        i += 1
    return list

def prime(n):
    list = factors(n)
    primelist = []
    for item in list[1:]:
        if item % 2 != 0 and item % 3 != 0 and item % 4 != 0 and item  \
        % 5 != 0 and item % 6 != 0 and item % 7 != 0 and item % 8 != 0 \
        and item % 9 != 0:
            primelist.append(item)
    return primelist

def greatestprime(n):
    list = prime(n)
    list[0] = lnum
    i = 1
    while i < len(list):
        if list[i] > lnum:
            lnum = list[i]
    return lnum

#print(greatestprime(600851475143))
Will Ness
  • 70,110
  • 9
  • 98
  • 181
srhoades28
  • 125
  • 1
  • 2
  • 7
  • 2
    (Prime) factorization is a *very hard* problem. It’s normal that this takes very long, especially since you are first finding all factors and then filter out prime numbers (and prime check is also a very hard problem). That being said, your prime number check is not correct; you only check factors 2–9, which does not match the definition of primes at all. – poke Feb 10 '14 at 18:09
  • 2
    possible duplicate of [Finding the greatest prime divisor (the fastest program possible)](http://stackoverflow.com/questions/20581491/finding-the-greatest-prime-divisor-the-fastest-program-possible) – SzieberthAdam Feb 11 '14 at 09:04
  • possible duplicate of [Which is the fastest algorithm to find prime numbers?](http://stackoverflow.com/questions/453793/which-is-the-fastest-algorithm-to-find-prime-numbers) – fuesika Jun 30 '15 at 13:08
  • @poke. Actually finding out if a number is prime actually is a much easier problem then factoring. The security of RSA encryption depends upon this fact. https://en.wikipedia.org/wiki/RSA_(cryptosystem) – qzcx Jul 08 '15 at 15:29

9 Answers9

2

If you are only factorizing a single number n, then the naive approach of testing every number from 2 to sqrt(n) (square root of n) should give result quickly for numbers up to 1014. You are writing more code than necessary.

Slightly better approaches would be:

  • Test 2, then test every odd number
  • Test 2, 3, 5, then test all integers of the form 6k + 1 and 6k + 5 where k >= 1
  • The 2 approaches above are wheel factorization with n = 2 and n = 2*3 = 6 respectively. You can raise it up to to n = 2*3*5*7 = 210 (higher would not bring much efficiency).

Slightly better approach would be to generate primes via Eratosthenes sieve and test (no redundant test). There are even better approaches such as Pollard's rho factorization, but both methods are overkill unless you are working with more numbers.

nhahtdh
  • 55,989
  • 15
  • 126
  • 162
  • OP isn’t testing every single number; OP is only testing numbers 2–9. Also, OP isn’t looking for a prime number check, but for prime factorization, which is something else where “a bit more code” is really required. – poke Feb 10 '14 at 18:17
  • 1
    @poke: Prime factorization and prime check are almost the same (for small instances that is). But OP is really writing too much code. – nhahtdh Feb 10 '14 at 18:21
2

Finding the largest prime factor of a number really isn't as difficult as people make it out to be.

from itertools import takewhile
from math import floor, sqrt

def _prime_numbers_helper():
    yield 2
    yield 3
    i = 1
    while True:
        yield 6 * i - 1
        yield 6 * i + 1
        i += 1

def prime_numbers(ceiling=None):
    if ceiling is None:
        yield from _prime_numbers_helper()
    else:
        yield from takewhile(lambda number: number <= ceiling, _prime_numbers_helper())

def largest_prime_factor(number):
    if number % int(number) != 0:
        raise ValueError('The number must be an integer.')
    if number in (0, 1):
        raise ValueError('There is no largest prime factor of {}.'.format(number))

    while True:
        for i in prime_numbers(floor(sqrt(abs(number)))):
            if number % i == 0:
                number //= i
                break
        else:
            return number

The else statement only executes when the for statement executes to completion (i.e. when the number cannot be factored any further).

The for statement should be using a true prime number generator instead, but I'm too lazy to write an efficient implementation of it.

Note that this assumes that you are using Python 3.3 or later.

Out of curiosity is this for Project Euler Problem 3?

Tyler Crompton
  • 12,284
  • 14
  • 65
  • 94
  • another thing is, you restart the search after each factor is found. Consider factoring 125: `test 2, test 3, test 5, divide 5, result 25, test 2, test 3, test 5, divide 5, result 5, test 2, done`. All the repeated tests by 2 and 3 are redundant. You really could just continue searching from the same factor, 5. When this is implemented, there's no need for costly generation of primes to test by, as all the factors thus found are guaranteed to be prime anyway. Using your `helper` is good, it reduces the amount of candidates cheaply (this is a.k.a. prime wheel factorization BTW). :) – Will Ness Feb 11 '14 at 08:29
  • The idea behind starting the search over is that it is faster to factor out prime factors than to just keep going until you find the largest prime factor. Obviously, this makes no difference if the number itself is prime. I suppose I could start the search over at the largest prime factor found thus far since there wouldn't be a prime factor less than it at that point, but I'm too lazy to fix it. Lol. I agree that `prime_numbers` is misleading. The idea is that you would replace the implementation with a true prime number generator. I forget my theory, but is `1` considered prime? – Tyler Crompton Feb 11 '14 at 08:57
  • no, 1 is not considered a prime. restarting from the start will obviously always be slower than continuing, you perform extra tests that take time. Searching from the top is bad, as more numbers have smaller factors. True primes generator isn't needed, when factorizing only one number as in here. – Will Ness Feb 11 '14 at 08:57
  • I retract this: "True primes generator isn't needed, when factorizing only one number as in here." – Will Ness Aug 24 '14 at 09:01
2

It is easy to find the factors of n by trial division, as long as n isn't too large; the :: operator inserts f at the front of the fs linked list:

function factors(n)
    f, fs := 2, []
    while f * f <= n
        while n % f == 0
            n := n / f
            fs := f :: fs
        f := f + 1
    if n <> 1
        n :: fs
    return reverse(fs)

If you're interested in programming with prime numbers, or if you're looking for a library to help with the Project Euler problems that involve prime numbers, I modestly recommend this essay at my blog, which includes, among other things, a translation of the above pseudocode to Python:

def td_factors(n, limit=1000000):
    if type(n) != int and type(n) != long:
        raise TypeError('must be integer')
    fs = []
    while n % 2 == 0:
        fs += [2]
        n /= 2
    if n == 1:
        return fs
    f = 3
    while f * f <= n:
        if limit < f:
            raise OverflowError('limit exceeded')
        if n % f == 0:
            fs += [f]
            n /= f
        else:
            f += 2
    return fs + [n]
endolith
  • 25,479
  • 34
  • 128
  • 192
user448810
  • 17,381
  • 4
  • 34
  • 59
2

Here are 2 possibilities. One from this blog:

def gpf(n):
    """
    Find the greatest prime factor of n
    """
    if n < 2:
        raise ValueError('{} does not have a prime factorization'.format(n))
    divisor = 2
    while n > 1:
        if not n % divisor:
            n /= divisor
            divisor -= 1
        divisor += 1
    return divisor

your example:

In [15]: gpf(600851475143)
Out[15]: 6857

In [16]: timeit gpf(600851475143)
1000 loops, best of 3: 1.55 ms per loop

or using SymPy library:

from sympy import factorint
def gpf(n):
    return max(factorint(n).keys())

(note that this has defined behavior for n < 2)

Trang Oul
  • 134
  • 1
  • 8
endolith
  • 25,479
  • 34
  • 128
  • 192
2

Try:

number = whatever your number is
divisor = 2

while divisor < number:
    if number % divisor == 0:
        number /= divisor
    else:
        divisor += 1

You're dividing out numbers until it's not possible anymore - which is what they teach you to do in grade school for this type of problem (although they'd never ask you to do this trick on a 12 digit number). When you get to a number that

Seems weird at first, but it works: each pass you'll be both decreasing the size of the number that you're looking at and dividing out lesser primes. If the number is divisible by 32, for instance, you'll just divide 2 out 6 times before proceeding, and in doing so you'll shrink the pool of numbers that could be factors of number. In the case where your number is already its own largest prime factor, you'll still have to iterate up to it in order to verify that. In the normal case (number is a product of its largest prime and some composite number) you'll divide out all of its lesser factors before you even check that the largest prime divides it.

Another useful heuristic is to find the square root of your number and only check numbers less than that: it's impossible n > sqrt(number) for n that are (integer) factors of number. I like the first method, however.

sike, didn't see someone already posted a really similar solution.

Derek Janni
  • 2,397
  • 2
  • 13
  • 13
1

This is how I went about it:

def is_prime(m):
    """Checks if the argument is a prime number."""

    if m < 2: return False

    for i in xrange(2, m):
        if m % i == 0:
            return False

    return True

def get_largest_prime_factor(k):
    """Gets the largest prime factor for the argument."""

    prime_divide = (p for p in xrange(1, k))
    for d in prime_divide:

        if k % d == 0 and is_prime(k / d):
            return k / d

print get_largest_prime_factor(600851475143)
Asclepius
  • 57,944
  • 17
  • 167
  • 143
kaffuffle
  • 11
  • 1
1
n=int(input(""))

prime_factors=[]
for i in range(2,int(n**0.5+1)):
  if n%i==0:
    for x in range(2,int(i**0.5+1)): 
      if i%x==0:
        break
    else:
      prime_factors.append(i)
      
print("prime factors are:",prime_factors)
print("largest prime factor is",prime_factors[-1])

input : 600851475143
output: prime factors are: [71, 839, 1471, 6857]
largest prime factor is 6857

Preetham G
  • 11
  • 1
0

Actual Final Program: Uses long division old way to find factors and stores only the largest/current factor until last one is found.

I know this is an old question, but wanted to share my way of approaching the problem.

def prime_factors_old_fashioned_factorization(number):
    y=1
    for x in range(2,number):
        if(number%x==0):
            print("number=",number,"\t and X=",x)
            while(number%x==0):
                print("number=",number)
                number=number/x
            print("new number=",number)
            y=x
            x=x+1
            if((number==1) or (number<x)):
                break;
    print("largest prime factor=",y)
camelbrush
  • 236
  • 6
  • 16
  • is your answer better or worse than one already here, like [e.g. this one](http://stackoverflow.com/a/21685573/849891)? – Will Ness Jul 03 '14 at 07:03
  • to me its faster. if that helps. – camelbrush Jul 03 '14 at 18:07
  • It does. Gives output 1 as a flag to say the entered number is a prime number. eg: >>prime_factors_old_fashioned_factorization(31) >>Largest prime factor=1 which tells us that the number entered (31) is a prime. – camelbrush Aug 26 '14 at 17:30
0
# Largest prime factor of a number
def prime_fact(n):
    p=[]
    f=[]
    p.append(1)
    for d in range(2,n+1):
        if n%d==0:
            f.append(d)

    for v in f:
        if (v==1 or v==2):
            p.append(v)
        else:
            for i in range(2,v):
                if v%i ==0:
                    break

            else:
                p.append(v)
    print(f'Factors of the number are {f}')
    print(f'Primefactors are {p}')
    print(f'Largest prime factor is {p[-1]}')