关于python:获取一个数字的所有除数的最佳方法是什么?

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

这是非常愚蠢的方式:

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

我想得到的结果和这个相似,但我想要一个更聪明的算法(这个算法太慢太笨了:-)

我可以很快找到基本因子及其多重性。我有一个发生器,它以这种方式产生因子:

(系数1,乘数1)(系数2,倍数2)(系数3,倍数3)等等……

即输出

1
2
for i in factorGenerator(100):
    print i

是:

1
2
(2, 2)
(5, 2)

我不知道这对我想做的事情有多有用(我为其他问题编码了它),不管怎样,我想要一个更聪明的方法

1
2
for i in divisorGen(100):
    print i

输出此:

1
2
3
4
5
6
7
8
9
1
2
4
5
10
20
25
50
100

更新:非常感谢Greg Hewgill和他的"聪明的方式":)计算100000000的除数时,他用0.01秒的速度计算了我机器上的39秒,非常酷:d

更新2:不要说这是这篇文章的副本。计算一个给定数的除数不需要计算所有的除数。这是一个不同的问题,如果你认为不是,那么在维基百科上寻找"除数函数"。在发帖前阅读问题和答案,如果你不理解主题是什么,不要添加不有用的和已经给出的答案。


考虑到factorgenerator函数,这里有一个除数器应该工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

该算法的整体效率完全取决于因子发生器的效率。


为了扩展Shimi所说的内容,您应该只运行从1到n的平方根的循环,然后找到这对,执行n / i,这将覆盖整个问题空间。

如前所述,这是一个NP或"困难"问题。详尽的搜索,你做它的方式,差不多是它得到的保证的答案。加密算法等使用这个事实来帮助保护它们。如果有人要解决这个问题,大多数(如果不是全部)我们当前的"安全"通信都会变得不安全。

python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
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))

它应该输出如下列表:

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


我想你可以停在江户一号〔4〕而不是2号。

我给你举个例子,这样你就容易理解了。现在,sqrt(28)5.29,所以ceil(5.29)将是6。所以如果我在6点停下来,我就能得到所有的除数。怎么用?

首先看到代码,然后看到图像:

1
2
3
4
5
6
7
8
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))

现在,请参见下图:

假设我已经将1添加到除数列表中,我从i=2开始。

Divisors of a 28

所以在所有迭代的末尾,当我把商和除数添加到我的列表中时,28的所有除数都被填充了。

来源:如何确定一个数的除数


虽然已经有很多解决方案,但我真的要发布这个:)

这个是:

  • 可读性
  • 短的
  • 独立、复制和粘贴就绪
  • 快速(在有很多素数因子和除数的情况下,比公认的解快10倍以上)
  • python3、python2和pypy兼容

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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


我喜欢Greg解决方案,但我希望它更像Python。我觉得它会更快更易读;经过一段时间的编码之后,我发现了这个。

前两个函数用于生成列表的笛卡尔积。并且可以在不出现此问题的情况下重复使用。顺便说一下,我必须自己编写这个程序,如果有人知道这个问题的标准解决方案,请随时与我联系。

"factorgenerator"现在返回字典。然后字典被输入"除数",除数首先用来生成一个列表列表,其中每个列表都是p^n和p prime形式的因子列表。然后我们得到这些列表的笛卡尔积,最后用格雷格解生成除数。我们把它们分类,然后把它们退回。

我测试过它,它似乎比以前的版本快一点。我把它作为一个更大程序的一部分进行了测试,所以我不能说它到底快了多少。

Pietro Speroni(Pietrosperoni点状)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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)

附笔。这是我第一次向StackOverflow发帖。我期待任何反馈。


改编自codereview,这里有一个与num=1一起工作的变体!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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))


我只想添加一个稍微修改过的版本的anivarth(我相信这是最Python)供将来参考。

1
2
3
4
5
6
7
8
from math import sqrt

def divisors(n):
    divs = {1,n}
    for i in range(2,int(sqrt(n))+1):
        if n%i == 0:
            divs.update((i,n//i))
    return divs

在纯Python3.6中,对于10*16及以下的数字,这是一种智能且快速的方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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))


老问题,但我的看法是:

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

您可以代理:

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

注意:对于支持的语言,这可能是尾部递归。


如果你只关心使用列表理解,而对你来说没有其他重要的东西!

1
2
3
4
5
6
7
8
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)


这是我的解决方案。它看起来很蠢,但效果很好……我试图找到所有合适的除数,所以循环从i=2开始。

1
2
3
4
5
6
7
8
9
10
11
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


假设factors函数返回n的因子(例如,factors(60)返回列表[2,2,3,5]),这里有一个函数来计算n的除数:

1
2
3
4
5
6
7
8
9
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


对我来说,这很好,也很干净(python 3)

1
2
3
4
5
6
7
8
9
def divisors(number):
    n = 1
    while(n<number):
        if(number%n==0):
            print(n)
        else:
            pass
        n += 1
    print(number)

不是很快,但可以按需要逐行返回除数,也可以执行list.append(n)和list.append(number)操作


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