28

I am trying to implement a function primeFac() that takes as input a positive integer n and returns a list containing all the numbers in the prime factorization of n.

I have gotten this far but I think it would be better to use recursion here, not sure how to create a recursive code here, what would be the base case? to start with.

My code:

def primes(n):
    primfac = []
    d = 2
    while (n > 1):
         if n%d==0:
             primfac.append(d)
    # how do I continue from here... ?
MSeifert
  • 145,886
  • 38
  • 333
  • 352
Snarre
  • 597
  • 5
  • 14
  • 22

17 Answers17

54

A simple trial division:

def primes(n):
    primfac = []
    d = 2
    while d*d <= n:
        while (n % d) == 0:
            primfac.append(d)  # supposing you want multiple factors repeated
            n //= d
        d += 1
    if n > 1:
       primfac.append(n)
    return primfac

with O(sqrt(n)) complexity (worst case). You can easily improve it by special-casing 2 and looping only over odd d (or special-casing more small primes and looping over fewer possible divisors).

jfs
  • 399,953
  • 195
  • 994
  • 1,670
Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • daniel, what does this mean exactly ( n /= d )... sorry – Snarre Jun 08 '13 at 05:34
  • 2
    It means "divide `n` by `d` and let `n` refer to the quotient henceforth". Just like `+=`, only with division instead of addition. – Daniel Fischer Jun 08 '13 at 05:36
  • i like your answer, and im trying to decipher it. So why do you write d*d ? what is the point with doubling d? – Snarre Jun 08 '13 at 05:42
  • and why do I need two while loops? – Snarre Jun 08 '13 at 05:54
  • @Snarre the inner loop counts each factor up to its multiplicity - eg, it causes `primes(12)` to give `[2, 2, 3]` instead of `[2, 3]`, and `primes(27)` to give `[3, 3, 3]` instead of `[3]`. – lvc Jun 08 '13 at 09:37
  • @Snarre for the `d*d`, notice that this doesn't *double* `d`, it *squares* it. The condition is equivalent to `d <= sqrt(n)`. It is provable that any `n` has at most one prime factor that *doesn't* meet this. The nested loops divide out each prime factor as it is found, leaving the quotient in `n`. That means at the point where the loop exits, if there is a factor left (and the only options are one or none), it will be `n`. OTOH, if *all* of the factors have been found, they will all have been divided out, and `n` must be 1. – lvc Jun 08 '13 at 10:01
  • `if(x): while(x):` is redundant, though I don't understand why [that suggestion](http://stackoverflow.com/review/suggested-edits/2990038) was rejected – Tobias Kienzler Sep 24 '13 at 07:25
  • 3
    This can be sped up to run in about half the time just by factoring out 2 at the beginning, starting `f` at 3, and then incrementing it by 2 instead of 1 every time. – Taylor Lopez Jun 16 '16 at 16:09
  • prime(18) returns [2, 3, 3] as opposed to [2, 3, 5, 7, 11, 13, 17] – Stan Bashtavenko May 22 '17 at 00:25
  • 4
    @StanBashtavenko The name of the function (which comes from the OP) may be misleading, but its purpose is to give the prime factorisation of the argument, not the list of primes up to the argument. – Daniel Fischer May 22 '17 at 07:13
  • May I ask what's the reason for using `d * d <= n` instead of `d <= n` here? The power of a prime factor `p` may be just 1 and `d * d <= n` will let go of `p`. I saw you have an additional catcher `if n > 1: ...` at the end, but in my opinion the most natural way should be setting `d <= n` instead as the loop condition from the outset. – Vim Jan 20 '19 at 08:50
17

The primefac module does factorizations with all the fancy techniques mathematicians have developed over the centuries:

#!python

import primefac
import sys

n = int( sys.argv[1] )
factors = list( primefac.primefac(n) )
print '\n'.join(map(str, factors))
brian d foy
  • 129,424
  • 31
  • 207
  • 592
  • Here's another example: `[factor for factor in primefac.primefac(2016)]` returns a list of factors: `[2, 2, 2, 2, 2, 3, 3, 7]` – Bob Stein Feb 22 '16 at 12:36
  • 2
    If you want to turn a generator into a list, you can use `list(primefac.primefac(2016))` – Simon May 23 '16 at 12:20
  • 2
    The version on PyPi does not appear to be compatable with Python 3. There is a fork on github that is ([here](https://github.com/elliptic-shiho/primefac-fork)), which can be installed with `pip3 install git+git://github.com/elliptic-shiho/primefac-fork@master` – lnNoam Jun 03 '17 at 23:41
  • 1
    If you want PyPI and no problems with Python3: https://testpypi.python.org/pypi/pyprimesieve/0.1.6c0 - pyprimesieve also looks promising. – Tomasz Gandor Nov 14 '17 at 22:50
  • @lnNoam Meanwhile, primefac is Python3 compatible. – Wolf Apr 12 '22 at 15:22
15

This is a comprehension based solution, it might be the closest you can get to a recursive solution in Python while being possible to use for large numbers.

You can get proper divisors with one line:

divisors = [ d for d in xrange(2,int(math.sqrt(n))) if n % d == 0 ]

then we can test for a number in divisors to be prime:

def isprime(d): return all( d % od != 0 for od in divisors if od != d )

which tests that no other divisors divides d.

Then we can filter prime divisors:

prime_divisors = [ d for d in divisors if isprime(d) ]

Of course, it can be combined in a single function:

def primes(n):
    divisors = [ d for d in range(2,n//2+1) if n % d == 0 ]
    return [ d for d in divisors if \
             all( d % od != 0 for od in divisors if od != d ) ]

Here, the \ is there to break the line without messing with Python indentation.

Rahul K P
  • 15,740
  • 4
  • 35
  • 52
deufeufeu
  • 998
  • 5
  • 11
  • ok, so why ( d for d ) i dont understand what this does? like i said earlier, i am very new to python.... i really appriciate your help – Snarre Jun 08 '13 at 05:52
  • [ d for d in l if P(d) ] constructs the list of elements in d such that P(d) holds. For example, [ n for n in range(20) where n % 2 == 0 ] constructs the list of even numbers < 20. – deufeufeu Jun 08 '13 at 06:12
  • 3
    but this algorithm won't repeat primes. for the input of `4`, it returns `[2]`. – Janus Troelsen Oct 23 '13 at 14:46
  • 1
    Missing closing parentheses on the first one liner – Chris_Rands Oct 26 '16 at 07:45
  • Nice one. Can you please break down this line `all( d % od != 0 for od in divisors if od != d )` step by step explaining what it does. It is really complex for someone new to python like me. – Goutham Feb 06 '19 at 02:25
6

I've tweaked @user448810's answer to use iterators from itertools (and python3.4, but it should be back-portable). The solution is about 15% faster.

import itertools

def factors(n):
    f = 2
    increments = itertools.chain([1,2,2], itertools.cycle([4,2,4,2,4,6,2,6]))
    for incr in increments:
        if f*f > n:
            break
        while n % f == 0:
            yield f
            n //= f
        f += incr
    if n > 1:
        yield n

Note that this returns an iterable, not a list. Wrap it in list() if that's what you want.

Chris Cogdon
  • 7,481
  • 5
  • 38
  • 30
5

Here is my version of factorization by trial division, which incorporates the optimization of dividing only by two and the odd integers proposed by Daniel Fischer:

def factors(n):
    f, fs = 3, []
    while n % 2 == 0:
        fs.append(2)
        n /= 2
    while f * f <= n:
        while n % f == 0:
            fs.append(f)
            n /= f
        f += 2
    if n > 1: fs.append(n)
    return fs

An improvement on trial division by two and the odd numbers is wheel factorization, which uses a cyclic set of gaps between potential primes to greatly reduce the number of trial divisions. Here we use a 2,3,5-wheel:

def factors(n):
    gaps = [1,2,2,4,2,4,2,4,6,2,6]
    length, cycle = 11, 3
    f, fs, nxt = 2, [], 0
    while f * f <= n:
        while n % f == 0:
            fs.append(f)
            n /= f
        f += gaps[nxt]
        nxt += 1
        if nxt == length:
            nxt = cycle
    if n > 1: fs.append(n)
    return fs

Thus, print factors(13290059) will output [3119, 4261]. Factoring wheels have the same O(sqrt(n)) time complexity as normal trial division, but will be two or three times faster in practice.

I've done a lot of work with prime numbers at my blog. Please feel free to visit and study.

user448810
  • 17,381
  • 4
  • 34
  • 59
  • Thanks for the great solution there. I hope you don't mind that I tweaked it a bit to use itertools (and python3.4). Can't paste code here, so I"ll add it a little later in response. – Chris Cogdon Aug 29 '15 at 01:58
  • Nice one. If possible can you please break down the second function step by step? It's really hard to understand for someone new to python. Please. – Goutham Feb 06 '19 at 02:59
  • It's a simple implementation of a [prime wheel](https://en.wikipedia.org/wiki/Wheel_factorization), with the wheel stored in `gaps`. The while loops implements trial division, but the next trial divisor `f` is incremented according to the wheel. The current position on the wheel is `nxt`, which is incremented each time but then cycles back at the end of the wheel. – user448810 Feb 06 '19 at 14:03
5

Most of the above solutions appear somewhat incomplete. A prime factorization would repeat each prime factor of the number (e.g. 9 = [3 3]).

Also, the above solutions could be written as lazy functions for implementation convenience.

The use sieve Of Eratosthenes to find primes to test is optimal, but; the above implementation used more memory than necessary.

I'm not certain if/how "wheel factorization" would be superior to applying only prime factors, for division tests of n.

While these solution are indeed helpful, I'd suggest the following two functions -

Function-1 :

def primes(n):
    if n < 2: return
    yield 2
    plist = [2]
    for i in range(3,n):
        test = True
        for j in plist:
            if j>n**0.5:
                break
            if i%j==0:
                test = False
                break
        if test:
            plist.append(i)
            yield i

Function-2 :

def pfactors(n):
    for p in primes(n):
        while n%p==0:
            yield p
            n=n//p
            if n==1: return

list(pfactors(99999))
[3, 3, 41, 271]

3*3*41*271
99999

list(pfactors(13290059))
[3119, 4261]

3119*4261
13290059
Mike Lisanke
  • 51
  • 1
  • 5
3
def get_prime_factors(number):
    """
    Return prime factor list for a given number
        number - an integer number
        Example: get_prime_factors(8) --> [2, 2, 2].
    """
    if number == 1:
        return []

    # We have to begin with 2 instead of 1 or 0
    # to avoid the calls infinite or the division by 0
    for i in xrange(2, number):
        # Get remainder and quotient
        rd, qt = divmod(number, i)
        if not qt: # if equal to zero
            return [i] + get_prime_factors(rd)

    return [number]
kadi
  • 184
  • 8
  • If rd stands for remainder and qt stand for quotient, you've mixed up both variables. Divmod gives the quotient first and the remainder second. You also have to check if the remainder is equal to zero (it is fully dividable by the current range value), not the quotient. Your solution works as expected, but the variable names are switched. – Martin Majewski Nov 06 '21 at 20:29
1

Most of the answer are making things too complex. We can do this

def prime_factors(n):
    num = []

    #add 2 to list or prime factors and remove all even numbers(like sieve of ertosthenes)
    while(n%2 == 0):
        num.append(2)
        n /= 2

    #divide by odd numbers and remove all of their multiples increment by 2 if no perfectlly devides add it
    for i in xrange(3, int(sqrt(n))+1, 2):
        while (n%i == 0):
            num.append(i)
            n /= i

    #if no is > 2 i.e no is a prime number that is only divisible by itself add it
    if n>2:
        num.append(n)

    print (num)

Algorithm from GeeksforGeeks

Amit Tripathi
  • 7,003
  • 6
  • 32
  • 58
1

prime factors of a number:

def primefactors(x):
    factorlist=[]
    loop=2
    while loop<=x:
        if x%loop==0:
            x//=loop
            factorlist.append(loop)
        else:
            loop+=1
    return factorlist

x = int(input())
alist=primefactors(x)
print(alist)

You'll get the list. If you want to get the pairs of prime factors of a number try this: http://pythonplanet.blogspot.in/2015/09/list-of-all-unique-pairs-of-prime.html

MSeifert
  • 145,886
  • 38
  • 333
  • 352
0
def factorize(n):
  for f in range(2,n//2+1):
    while n%f == 0:
      n //= f
      yield f

It's slow but dead simple. If you want to create a command-line utility, you could do:

import sys
[print(i) for i in factorize(int(sys.argv[1]))]
Jonas Byström
  • 25,316
  • 23
  • 100
  • 147
  • why is two // in for loop "n//2"? – DevC Nov 20 '15 at 11:11
  • try it with 600851475143, Project euler 3rd problem.. range will bust – DevC Nov 23 '15 at 11:00
  • @DevC: The `//` is a python 3 construct, where range is the same as xrange in py2. Would have been self-evident if you had looked at the second chunk of code, as print in list comprehension doesn't work in py2. – Jonas Byström Feb 04 '16 at 10:23
0

Here is an efficient way to accomplish what you need:

def prime_factors(n): 
  l = []
  if n < 2: return l
  if n&1==0:
    l.append(2)
    while n&1==0: n>>=1
  i = 3
  m = int(math.sqrt(n))+1
  while i < m:
    if n%i==0:
      l.append(i)
      while n%i==0: n//=i
    i+= 2
    m = int(math.sqrt(n))+1
  if n>2: l.append(n)
  return l

prime_factors(198765430488765430290) = [2, 3, 5, 7, 11, 13, 19, 23, 3607, 3803, 52579]

jfalcon
  • 19
  • 2
-1
    from sets import Set
    # this function generates all the possible factors of a required number x
    def factors_mult(X):
        L = []
        [L.append(i) for i in range(2,X) if X % i == 0]
        return L

    # this function generates list containing prime numbers upto the required number x 
    def prime_range(X):
        l = [2]
        for i in range(3,X+1):
            for j in range(2,i):
               if i % j == 0:
               break
            else:    
               l.append(i)
        return l

    # This function computes the intersection of the two lists by invoking Set from the sets module
    def prime_factors(X):
        y = Set(prime_range(X))
        z = Set(factors_mult(X))
        k = list(y & z)
        k = sorted(k)

        print "The prime factors of " + str(X) + " is ", k

    # for eg
    prime_factors(356)
-1

Simple way to get the desired solution

def Factor(n):
    d = 2
    factors = []
    while n >= d*d:
        if n % d == 0:
            n//=d
            # print(d,end = " ")
            factors.append(d)
        else:
            d = d+1
    if n>1:
        # print(int(n))
        factors.append(n)
    return factors
Unamata Sanatarai
  • 6,475
  • 3
  • 29
  • 51
Md. Rezwanul Haque
  • 2,882
  • 7
  • 28
  • 45
-1

This is the code I made. It works fine for numbers with small primes, but it takes a while for numbers with primes in the millions.

def pfactor(num):
div = 2
pflist = []
while div <= num:
    if num % div == 0:
        pflist.append(div)
        num /= div
    else:
        div += 1
# The stuff afterwards is just to convert the list of primes into an expression
pfex = ''
for item in list(set(pflist)):
    pfex += str(item) + '^' + str(pflist.count(item)) + ' * '
pfex = pfex[0:-3]
return pfex
  • 1
    Please indent your code properly. Also in Python 3.x `num /= div` produces a float, not an integer. And testing all divisors instead of 2 and odd numbers is not the most efficient strategy. – Mr. T Jan 30 '18 at 02:44
-1

I would like to share my code for finding the prime factors of number given input by the user:

a = int(input("Enter a number: "))

def prime(a):
    b = list()
    i = 1
    while i<=a:
        if a%i ==0 and i!=1 and i!=a:
            b.append(i)
        i+=1
    return b

c = list()
for x in prime(a):
    if len(prime(x)) == 0:
        c.append(x)

print(c)
-1
def prime_factors(num, dd=2):
    while dd <= num and num>1:
        if num % dd == 0:
            num //= dd
            yield dd
        dd +=1

Lot of answers above fail on small primes, e.g. 3, 5 and 7. The above is succinct and fast enough for ordinary use.

print list(prime_factors(3))

[3]

jorjun
  • 115
  • 1
  • 2
  • 6
-2

You can use sieve Of Eratosthenes to generate all the primes up to (n/2) + 1 and then use a list comprehension to get all the prime factors:

def rwh_primes2(n):
    # http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
    """ Input n>=6, Returns a list of primes, 2 <= p < n """
    correction = (n%6>1)
    n = {0:n,1:n-1,2:n+4,3:n+3,4:n+2,5:n+1}[n%6]
    sieve = [True] * (n/3)
    sieve[0] = False
    for i in xrange(int(n**0.5)/3+1):
      if sieve[i]:
        k=3*i+1|1
        sieve[      ((k*k)/3)      ::2*k]=[False]*((n/6-(k*k)/6-1)/k+1)
        sieve[(k*k+4*k-2*k*(i&1))/3::2*k]=[False]*((n/6-(k*k+4*k-2*k*(i&1))/6-1)/k+1)
    return [2,3] + [3*i+1|1 for i in xrange(1,n/3-correction) if sieve[i]]

def primeFacs(n):
    primes = rwh_primes2((n/2)+1)
    return [x for x in primes if n%x == 0]

print primeFacs(99999)
#[3, 41, 271]
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504