133

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.

Community
  • 1
  • 1
Andrea Ambu
  • 38,188
  • 14
  • 54
  • 77
  • The reason that it was suggested that this question was almost a duplicate of the "Algorithm to calculate the number of divisors of a given number" was that the suggested first step in that question was to _find all of the divisors_, which I believe is exactly what you were trying to do? – Andrew Edgecombe Oct 05 '08 at 22:43
  • 4
    Andrew in order to find how many divisors there are you simply have to find the prime factors and then use them to count how much divisors there might be. Finding divisors isn't needed in that case. – Loïc Faure-Lacroix Aug 13 '11 at 23:25
  • 1
    @Andrea Ambu, please correct you function names – minerals Jan 29 '17 at 11:56
  • 12
    Hey you reading this 12 years later, what you want is [`sympy.divisors`](https://docs.sympy.org/latest/modules/ntheory.html#sympy.ntheory.factor_.divisors) – baqyoteto Feb 26 '21 at 18:07

18 Answers18

83

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.

vvvvv
  • 25,404
  • 19
  • 49
  • 81
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 2
    wow it took 0.01 for calculating all divisors of 100000000 against the 39s that took the dumb way (stopping at n/2) very cool, thank you! – Andrea Ambu Oct 05 '08 at 10:20
  • 59
    For those of us who don't understand Pythonese, what is this actually doing? – Matthew Scharley Oct 07 '08 at 13:24
  • 4
    monoxide: this computes all multiplicative combinations of the given factors. Most of it should be self-explanatory; the "yield" line is like a return but keeps going after returning a value. [0]*nfactors creates a list of zeros of length nfactors. reduce(...) computes the product of the factors. – Greg Hewgill Oct 07 '08 at 22:33
  • 1
    @SpeckiniusFlecksis: No reason, `operator.mul` would work equally well there. – Greg Hewgill Oct 23 '12 at 18:46
  • 3
    This is of course dramatically better than dividing by every number up to n/2 or even sqrt(n), but this particular implementation has two drawbacks: quite innefective: tons of multiplication and exponentiation, repeatedly multiplying the same powers etc. Looks Pythonic, but I don't think Python is about killing performance. Problem two: the divisors are not returned in order. – Tomasz Gandor Dec 10 '14 at 14:37
  • Thanks Greg. Your algorithm inspired my more functional version here: http://rosettacode.org/wiki/Proper_divisors#Python:_From_prime_factors – Paddy3118 Dec 16 '14 at 06:18
42

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]
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
  • 2
    Because, once you have a list of elements between 1..10, you can generate any of the elements between 11..100 trivialy. You get {1, 2, 4, 5, 10}. Divide 100 by each of these elements and you {100, 50, 20, 25, 10}. – Matthew Scharley Oct 05 '08 at 11:40
  • 3
    Factors are ALWAYS generated in pairs, by virtue of the definition. By only searching to sqrt(n), you're cutting your work by a power 2. – Matthew Scharley Oct 05 '08 at 11:42
  • It's very faster than the version in my post, but it's still too slow than the version using prime factors – Andrea Ambu Oct 05 '08 at 12:24
  • I agree this isn't the best solution. I was simply pointing out a 'better' way of doing the 'dumb' search that would already save alot of time. – Matthew Scharley Oct 05 '08 at 12:39
  • Factorization has not been shown to be NP-hard. http://en.wikipedia.org/wiki/Integer_factorization And the problem was to find all the divisors given that the prime factors (the hard part) had already been found. – Jamie Oct 11 '08 at 00:46
  • Which means factorisation is an NP-hard algorithm by extension. The fact they already have the prime factors is beside the point. The example given was exhaustive search, which is slow and hard. – Matthew Scharley Oct 11 '08 at 09:00
  • if i is not n / i: should be if i != n / i: , for values whose square root is greater than 256 'is' will not work. – Abhishek Jun 28 '14 at 08:51
  • for small numbers, it's way faster than the version using prime factors – Kukosk Jan 08 '15 at 15:33
36

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

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Anivarth
  • 619
  • 5
  • 19
24

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
Tomas Kulich
  • 14,388
  • 4
  • 30
  • 35
16

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
ppw0
  • 343
  • 3
  • 11
10

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.

Pietro Speroni
  • 3,131
  • 11
  • 44
  • 55
  • In Python 2.6 there is a itertools.product(). – jfs Dec 17 '08 at 01:07
  • A version that use generators instead of list.append everywhere could be cleaner. – jfs Dec 17 '08 at 01:10
  • Sieve of Eratosthenes could be used to generate prime numbers less then or equal sqrt(n) http://stackoverflow.com/questions/188425/project-euler-problem#193605 – jfs Dec 17 '08 at 01:17
  • 1
    Coding style: exponents = [k**x for k, v in factors.items() for x in range(v+1)] – jfs Dec 17 '08 at 01:23
  • For listexponents: [[k**x for x in range(v+1)] for k,v in factors.items()] – klenwell Sep 09 '12 at 16:24
6

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))
Bruno Astrolino
  • 401
  • 5
  • 3
  • Whats the name of the algorithm used to find the primes and to factorize? Because I want to implement this in C# .. – Kyu96 Sep 09 '18 at 03:23
3

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.

  • 1
    Nice idea, but when you posted this, we already had [sympy.divisors](https://docs.sympy.org/latest/modules/ntheory.html?highlight=divisors#sympy.ntheory.factor_.divisors) which should choose the most efficient way to compute it. – Max Apr 03 '21 at 16:07
2

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))
Community
  • 1
  • 1
YvesgereY
  • 3,778
  • 1
  • 20
  • 19
  • 2
    I seem to be getting an error: `NameError: global name 'prime_factors' is not defined`. None of the other answers, nor the original question, defines what this does. – AnnanFay Aug 26 '16 at 18:43
2

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.

joksnet
  • 2,305
  • 15
  • 18
  • This recursive approach is very simple and clean! I was just about to add a similar answer but there is no need. – Mihai.Mehe Aug 15 '22 at 16:10
1
#slow but pretty
return [x for x in range(1,n+1) if n/x==int(n/x)]
Romeo Kienzler
  • 3,373
  • 3
  • 36
  • 58
0

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
user448810
  • 17,381
  • 4
  • 34
  • 59
  • Is that python ? Anyway, it's not python 3.x for sure. – GinKin Mar 20 '14 at 18:04
  • It's pseudocode, which ought to be simple to translate to python. – user448810 Mar 20 '14 at 18:23
  • 3 years late, better late than never :) IMO, this is the simplest, shortest code to do this. I don't have a comparison table, but I can factorize and compute divisors for up to a million in 1s on my i5 portable laptop. – Riyaz Mansoor May 17 '17 at 19:20
0

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
Amber Xue
  • 143
  • 3
0

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)
Sadiq
  • 184
  • 11
0

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
Eugene
  • 93
  • 4
0

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)
Arvind Pant
  • 401
  • 5
  • 9
0
def divisorGen(n): v = n last = [] for i in range(1, v+1) : if n % i == 0 : last.append(i)
4b0
  • 21,981
  • 30
  • 95
  • 142
amiralidev
  • 31
  • 1
  • 7
  • 2
    While this code snippet may be the solution, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-‌​code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – E. Zeytinci Oct 06 '21 at 11:38
0

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))
Rodrigo V
  • 434
  • 1
  • 7
  • 14