What is the best way to get all the divisors of a number?

PythonAlgorithmMath

Python Problem Overview


Here's the very dumb way:

def divisorGenerator(n):
    for i in xrange(1,n/2+1):
        if n%i == 0: yield i
    yield n

The result I'd like to get is similar to this one, but I'd like a smarter algorithm (this one it's too much slow and dumb :-)

I can find prime factors and their multiplicity fast enough. I've an generator that generates factor in this way:

(factor1, multiplicity1)
(factor2, multiplicity2)
(factor3, multiplicity3)
and so on...

i.e. the output of

for i in factorGenerator(100):
    print i

is:

(2, 2)
(5, 2)

I don't know how much is this useful for what I want to do (I coded it for other problems), anyway I'd like a smarter way to make

for i in divisorGen(100):
    print i

output this:

1
2
4
5
10
20
25
50
100

UPDATE: Many thanks to Greg Hewgill and his "smart way" :) Calculating all divisors of 100000000 took 0.01s with his way against the 39s that the dumb way took on my machine, very cool :D

UPDATE 2: Stop saying this is a duplicate of this post. Calculating the number of divisor of a given number doesn't need to calculate all the divisors. It's a different problem, if you think it's not then look for "Divisor function" on wikipedia. Read the questions and the answer before posting, if you do not understand what is the topic just don't add not useful and already given answers.

Python Solutions


Solution 1 - Python

Given your factorGenerator function, here is a divisorGen that should work:

def divisorGen(n):
    factors = list(factorGenerator(n))
    nfactors = len(factors)
    f = [0] * nfactors
    while True:
        yield reduce(lambda x, y: x*y, [factors[x][0]**f[x] for x in range(nfactors)], 1)
        i = 0
        while True:
            f[i] += 1
            if f[i] <= factors[i][1]:
                break
            f[i] = 0
            i += 1
            if i >= nfactors:
                return

The overall efficiency of this algorithm will depend entirely on the efficiency of the factorGenerator.

Solution 2 - Python

To expand on what Shimi has said, you should only be running your loop from 1 to the square root of n. Then to find the pair, do n / i, and this will cover the whole problem space.

As was also noted, this is a NP, or 'difficult' problem. Exhaustive search, the way you are doing it, is about as good as it gets for guaranteed answers. This fact is used by encryption algorithms and the like to help secure them. If someone were to solve this problem, most if not all of our current 'secure' communication would be rendered insecure.

Python code:

import math

def divisorGenerator(n):
    large_divisors = []
    for i in xrange(1, int(math.sqrt(n) + 1)):
        if n % i == 0:
            yield i
            if i*i != n:
                large_divisors.append(n / i)
    for divisor in reversed(large_divisors):
        yield divisor

print list(divisorGenerator(100))

Which should output a list like:

[1, 2, 4, 5, 10, 20, 25, 50, 100]

Solution 3 - Python

I think you can stop at math.sqrt(n) instead of n/2.

I will give you example so that you can understand it easily. Now the sqrt(28) is 5.29 so ceil(5.29) will be 6. So I if I will stop at 6 then I will can get all the divisors. How?

First see the code and then see image:

import math
def divisors(n):
    divs = [1]
    for i in xrange(2,int(math.sqrt(n))+1):
	    if n%i == 0:
		    divs.extend([i,n/i])
    divs.extend([n])
    return list(set(divs))

Now, See the image below:

Lets say I have already added 1 to my divisors list and I start with i=2 so

Divisors of a 28

So at the end of all the iterations as I have added the quotient and the divisor to my list all the divisors of 28 are populated.

Source: How to determine the divisors of a number

Solution 4 - Python

Although there are already many solutions to this, I really have to post this :)

This one is:

  • readable
  • short
  • self contained, copy & paste ready
  • quick (in cases with a lot of prime factors and divisors, > 10 times faster than the accepted solution)
  • python3, python2 and pypy compliant

Code:

def divisors(n):
    # get factors and their counts
    factors = {}
    nn = n
    i = 2
    while i*i <= nn:
        while nn % i == 0:
            factors[i] = factors.get(i, 0) + 1
            nn //= i
        i += 1
    if nn > 1:
        factors[nn] = factors.get(nn, 0) + 1

    primes = list(factors.keys())

    # generates factors from primes[k:] subset
    def generate(k):
        if k == len(primes):
            yield 1
        else:
            rest = generate(k+1)
            prime = primes[k]
            for factor in rest:
                prime_to_i = 1
                # prime_to_i iterates prime**i values, i being all possible exponents
                for _ in range(factors[prime] + 1):
                    yield factor * prime_to_i
                    prime_to_i *= prime

    # in python3, `yield from generate(0)` would also work
    for factor in generate(0):
        yield factor

Solution 5 - Python

An illustrative Pythonic one-liner:

from itertools import chain
from math import sqrt

def divisors(n):
    return set(chain.from_iterable((i,n//i) for i in range(1,int(sqrt(n))+1) if n%i == 0))

But better yet, just use sympy:

from sympy import divisors

Solution 6 - Python

I like Greg solution, but I wish it was more python like. I feel it would be faster and more readable; so after some time of coding I came out with this.

The first two functions are needed to make the cartesian product of lists. And can be reused whnever this problem arises. By the way, I had to program this myself, if anyone knows of a standard solution for this problem, please feel free to contact me.

"Factorgenerator" now returns a dictionary. And then the dictionary is fed into "divisors", who uses it to generate first a list of lists, where each list is the list of the factors of the form p^n with p prime. Then we make the cartesian product of those lists, and we finally use Greg' solution to generate the divisor. We sort them, and return them.

I tested it and it seem to be a bit faster than the previous version. I tested it as part of a bigger program, so I can't really say how much is it faster though.

Pietro Speroni (pietrosperoni dot it)

from math import sqrt


##############################################################
### cartesian product of lists ##################################
##############################################################

def appendEs2Sequences(sequences,es):
    result=[]
    if not sequences:
        for e in es:
            result.append([e])
    else:
        for e in es:
            result+=[seq+[e] for seq in sequences]
    return result


def cartesianproduct(lists):
    """
    given a list of lists,
    returns all the possible combinations taking one element from each list
    The list does not have to be of equal length
    """
    return reduce(appendEs2Sequences,lists,[])

##############################################################
### prime factors of a natural ##################################
##############################################################

def primefactors(n):
    '''lists prime factors, from greatest to smallest'''  
    i = 2
    while i<=sqrt(n):
        if n%i==0:
            l = primefactors(n/i)
            l.append(i)
            return l
        i+=1
    return [n]      # n is prime


##############################################################
### factorization of a natural ##################################
##############################################################

def factorGenerator(n):
    p = primefactors(n)
    factors={}
    for p1 in p:
        try:
            factors[p1]+=1
        except KeyError:
            factors[p1]=1
    return factors

def divisors(n):
    factors = factorGenerator(n)
    divisors=[]
    listexponents=[map(lambda x:k**x,range(0,factors[k]+1)) for k in factors.keys()]
    listfactors=cartesianproduct(listexponents)
    for f in listfactors:
        divisors.append(reduce(lambda x, y: x*y, f, 1))
    divisors.sort()
    return divisors



print divisors(60668796879)

P.S. it is the first time I am posting to stackoverflow. I am looking forward for any feedback.

Solution 7 - Python

Here is a smart and fast way to do it for numbers up to and around 10**16 in pure Python 3.6,

from itertools import compress

def primes(n):
    """ Returns  a list of primes < n for n > 2 """
    sieve = bytearray([True]) * (n//2)
    for i in range(3,int(n**0.5)+1,2):
        if sieve[i//2]:
            sieve[i*i//2::i] = bytearray((n-i*i-1)//(2*i)+1)
    return [2,*compress(range(3,n,2), sieve[1:])]

def factorization(n):
    """ Returns a list of the prime factorization of n """
    pf = []
    for p in primeslist:
      if p*p > n : break
      count = 0
      while not n % p:
        n //= p
        count += 1
      if count > 0: pf.append((p, count))
    if n > 1: pf.append((n, 1))
    return pf
   
def divisors(n):
    """ Returns an unsorted list of the divisors of n """
    divs = [1]
    for p, e in factorization(n):
        divs += [x*p**k for k in range(1,e+1) for x in divs]
    return divs

n = 600851475143
primeslist = primes(int(n**0.5)+1) 
print(divisors(n))

Solution 8 - Python

If your PC has tons of memory, a brute single line can be fast enough with numpy:

N = 10000000; tst = np.arange(1, N); tst[np.mod(N, tst) == 0]
Out: 
array([      1,       2,       4,       5,       8,      10,      16,
            20,      25,      32,      40,      50,      64,      80,
           100,     125,     128,     160,     200,     250,     320,
           400,     500,     625,     640,     800,    1000,    1250,
          1600,    2000,    2500,    3125,    3200,    4000,    5000,
          6250,    8000,   10000,   12500,   15625,   16000,   20000,
         25000,   31250,   40000,   50000,   62500,   78125,   80000,
        100000,  125000,  156250,  200000,  250000,  312500,  400000,
        500000,  625000, 1000000, 1250000, 2000000, 2500000, 5000000])

Takes less than 1s on my slow PC.

Solution 9 - Python

Adapted from CodeReview, here is a variant which works with num=1 !

from itertools import product
import operator
   
def prod(ls):
   return reduce(operator.mul, ls, 1)

def powered(factors, powers):
   return prod(f**p for (f,p) in zip(factors, powers))


def divisors(num) :

   pf = dict(prime_factors(num))
   primes = pf.keys()
   #For each prime, possible exponents
   exponents = [range(i+1) for i in pf.values()]
   return (powered(primes,es) for es in product(*exponents))

Solution 10 - Python

Old question, but here is my take:

def divs(n, m):
    if m == 1: return [1]
    if n % m == 0: return [m] + divs(n, m - 1)
    return divs(n, m - 1)

You can proxy with:

def divisorGenerator(n):
    for x in reversed(divs(n, n)):
        yield x

NOTE: For languages that support, this could be tail recursive.

Solution 11 - Python

Assuming that the factors function returns the factors of n (for instance, factors(60) returns the list [2, 2, 3, 5]), here is a function to compute the divisors of n:

function divisors(n)
    divs := [1]
    for fact in factors(n)
        temp := []
        for div in divs
            if fact * div not in divs
                append fact * div to temp
        divs := divs + temp
    return divs

Solution 12 - Python

Here's my solution. It seems to be dumb but works well...and I was trying to find all proper divisors so the loop started from i = 2.

import math as m 

def findfac(n):
    faclist = [1]
    for i in range(2, int(m.sqrt(n) + 2)):
	    if n%i == 0:
		    if i not in faclist:
			    faclist.append(i)
			    if n/i not in faclist:
				    faclist.append(n/i)
    return facts

Solution 13 - Python

If you only care about using list comprehensions and nothing else matters to you!

from itertools import combinations
from functools import reduce

def get_devisors(n):
    f = [f for f,e in list(factorGenerator(n)) for i in range(e)]
    fc = [x for l in range(len(f)+1) for x in combinations(f, l)]
    devisors = [1 if c==() else reduce((lambda x, y: x * y), c) for c in set(fc)]
    return sorted(devisors)

Solution 14 - Python

Try to calculate square root a given number and then loop range(1,square_root+1).

number = int(input("Enter a Number: "))
square_root = round(number ** (1.0 / 2))
print(square_root)
divisor_list = []
for i in range(1,square_root+1):
    if number % i == 0: # Check if mod return 0 if yes then append i and number/i in the list
        divisor_list.append(i)
        divisor_list.append(int(number/i))

print(divisor_list)

Solution 15 - Python

def divisorGen(n): v = n last = [] for i in range(1, v+1) : if n % i == 0 : last.append(i)

Solution 16 - Python

I don´t understand why there are so many complicated solutions to this problem.

Here is my take on it:

def divisors(n):
  lis =[1]
  s = math.ceil(math.sqrt(n))
  for g in range(s,1, -1):
     if n % g == 0:
        lis.append(g)
        lis.append(int(n / g))
  return (set(lis))

Solution 17 - Python

return [x for x in range(n+1) if n/x==int(n/x)]

Solution 18 - Python

For me this works fine and is also clean (Python 3)

def divisors(number):
	n = 1
	while(n<number):
		if(number%n==0):
			print(n)
		else:
			pass
		n += 1
	print(number)

Not very fast but returns divisors line by line as you wanted, also you can do list.append(n) and list.append(number) if you really want to

Solution 19 - Python

My solution via generator function is:

def divisor(num):
    for x in range(1, num + 1):
        if num % x == 0:
            yield x
    while True:
        yield None

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndrea AmbuView Question on Stackoverflow
Solution 1 - PythonGreg HewgillView Answer on Stackoverflow
Solution 2 - PythonMatthew ScharleyView Answer on Stackoverflow
Solution 3 - PythonAnivarthView Answer on Stackoverflow
Solution 4 - PythonTomas KulichView Answer on Stackoverflow
Solution 5 - Pythonppw0View Answer on Stackoverflow
Solution 6 - PythonPietro SperoniView Answer on Stackoverflow
Solution 7 - PythonBruno AstrolinoView Answer on Stackoverflow
Solution 8 - PythonMathieu VillionView Answer on Stackoverflow
Solution 9 - PythonYvesgereYView Answer on Stackoverflow
Solution 10 - PythonjoksnetView Answer on Stackoverflow
Solution 11 - Pythonuser448810View Answer on Stackoverflow
Solution 12 - PythonAmber XueView Answer on Stackoverflow
Solution 13 - PythonSadiqView Answer on Stackoverflow
Solution 14 - PythonArvind PantView Answer on Stackoverflow
Solution 15 - PythonamiralidevView Answer on Stackoverflow
Solution 16 - PythonRodrigo VView Answer on Stackoverflow
Solution 17 - Pythonuser3697453View Answer on Stackoverflow
Solution 18 - Pythontomekch6View Answer on Stackoverflow
Solution 19 - PythonEugeneView Answer on Stackoverflow