【问题标题】:What is the best way to get all the divisors of a number?获得一个数字的所有除数的最佳方法是什么?
【发布时间】:2010-09-15 08:46:49
【问题描述】:

这是非常愚蠢的方式:

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)
等等……

的输出
for i in factorGenerator(100):
    print i

是:

(2, 2)
(5, 2)

我不知道这对我想做的事情有多大用处(我为其他问题编写了代码),无论如何我想要一种更聪明的方法

for i in divisorGen(100):
    print i

输出这个:

1
2
4
5
10
20
25
50
100

更新:非常感谢 Greg Hewgill 和他的“聪明的方式” :) 计算 100000000 的所有除数需要 0.01 秒,而笨方法在我的机器上采用 39 秒,非常酷:D

更新 2: 不要再说这是 this 帖子的副本。计算给定数的除数不需要计算所有除数。这是一个不同的问题,如果您认为不是,请在维基百科上查找“除数函数”。在发布之前阅读问题和答案,如果您不明白主题是什么,请不要添加无用且已经给出的答案。

【问题讨论】:

  • 之所以建议这个问题几乎是“计算给定数的除数的算法”的重复,是因为该问题中建议的第一步是 找到所有除数,我相信这正是你想要做的?
  • Andrew 为了找到有多少除数,您只需找到主要因素,然后使用它们来计算可能有多少除数。在这种情况下不需要寻找除数。
  • @Andrea Ambu,请更正你的函数名称
  • 嘿,你在 12 年后阅读这篇文章,你想要的是 sympy.divisors

标签: python algorithm math


【解决方案1】:

说明性的 Pythonic 单线:

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))

但更好的是,只需使用 sympy:

from sympy import divisors

【讨论】:

  • nitpick:重复的整数平方根,例如除数 (16) 或除数 (100)。
【解决方案2】:

我不明白为什么这个问题有这么多复杂的解决方案。

这是我的看法:

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))

【讨论】:

    【解决方案3】:
    def divisorGen(n): v = n last = [] for i in range(1, v+1) : if n % i == 0 : last.append(i)
    

    【讨论】:

    • 虽然此代码 sn-p 可能是解决方案,但 including an explanation 确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
    【解决方案4】:

    尝试计算给定数字的平方根,然后循环 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)
    

    【讨论】:

      【解决方案5】:

      我通过生成器函数的解决方案是:

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

      【讨论】:

        【解决方案6】:

        如果您的 PC 有大量内存,使用 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])
        

        在我的慢速 PC 上耗时不到 1 秒。

        【讨论】:

        • 好主意,但是当您发布此内容时,我们已经有了sympy.divisors,它应该选择最有效的计算方式。
        【解决方案7】:

        鉴于您的 factorGenerator 函数,这里有一个应该可以工作的 divisorGen

        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
        

        这个算法的整体效率将完全取决于factorGenerator的效率。

        【讨论】:

        • 哇,用 0.01 来计算 100000000 的所有除数对 39 采取愚蠢的方式(停止在 n/2)非常酷,谢谢!
        • 对于我们这些不懂 Pythonese 的人来说,这实际上是在做什么?
        • 一氧化碳:计算给定因子的所有乘法组合。大部分内容应该是不言自明的; “yield”行就像一个返回,但在返回一个值后继续。 [0]*nfactors 创建一个长度为 nfactors 的零列表。 reduce(...) 计算因子的乘积。
        • @SpeckiniusFlecksis:没有理由,operator.mul 在那里也同样有效。
        • 这当然比除以 n/2 甚至 sqrt(n) 的每个数字要好得多,但是这种特殊的实现有两个缺点: 非常不实用:大量的乘法和取幂,重复乘法相同的权力等。看起来像 Pythonic,但我不认为 Python 是为了扼杀性能。问题二:除数没有按顺序返回。
        【解决方案8】:

        我认为您可以停在math.sqrt(n) 而不是 n/2。

        我会给你一个例子,这样你就可以很容易地理解它。现在sqrt(28)5.29,所以ceil(5.29) 将是6。所以如果我停在6,那么我可以得到所有的除数。怎么样?

        先看代码,再看图片:

        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 开始

        所以在所有迭代结束时,我已将商和除数添加到我的列表中,所有 28 的除数都被填充。

        来源:How to determine the divisors of a number

        【讨论】:

        • 不错,不错!! math.sqrt(n) instead of n/2 是优雅的必需品
        • 这是不正确的。你忘了 n 本身是可整除的。
        • 不错的答案。简单明了。但是对于 python 3,有 2 个必要的更改: n/i 应该使用 int(n/i) 键入,因为 n/i 产生浮点数。此外 rangex 在 python 3 中已被弃用,并已被 range 取代。
        • @GeoffroyCALA 他也可以使用n//i
        【解决方案9】:

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

        这个是:

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

        代码:

        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
        

        【讨论】:

        • 我会将while i*i &lt;= nn 替换为while i &lt;= limit,其中limit = math.sqrt(n)
        【解决方案10】:

        如果您只关心使用列表推导而不关心其他任何事情!

        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)
        

        【讨论】:

          【解决方案11】:

          对于纯 Python 3.6 中高达 10**16 左右的数字,这是一种智能且快速的方法,

          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))
          

          【讨论】:

          • 用于查找素数和分解的算法的名称是什么?因为我想在 C# 中实现这个..
          【解决方案12】:

          老问题,但这是我的看法:

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

          您可以使用以下代理:

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

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

          【讨论】:

            【解决方案13】:

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

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

            Python 代码:

            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、2、4、5、10、20、25、50、100]

            【讨论】:

            • 因为,一旦有了 1..10 之间的元素列表,您就可以轻松生成 11..100 之间的任何元素。你得到 {1, 2, 4, 5, 10}。将 100 除以每个元素,得到 {100, 50, 20, 25, 10}。
            • 因子总是成对生成的,根据定义。通过仅搜索 sqrt(n),您的工作量减少了 2 次方。
            • 比我帖子里的版本快很多,但还是比使用素数的版本慢
            • 我同意这不是最好的解决方案。我只是指出了一种“更好”的方式来进行“愚蠢”的搜索,这已经可以节省很多时间。
            • 因式分解没有被证明是 NP 难的。 en.wikipedia.org/wiki/Integer_factorization 问题是要找到所有除数,因为已经找到了主要因素(困难的部分)。
            【解决方案14】:

            这是我的解决方案。它似乎很愚蠢,但效果很好……我试图找到所有合适的除数,所以循环从 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
            

            【讨论】:

            • 错字:返回事实 => 返回 faclist
            【解决方案15】:

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

            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)

            【讨论】:

              【解决方案16】:
              return [x for x in range(n+1) if n/x==int(n/x)]
              

              【讨论】:

              • 提问者要求更好的算法,而不仅仅是更漂亮的格式。
              • 你需要使用 range(1,n+1) 来避免被零除。此外,如果使用 Python 2.7,则需要使用 float(n) 进行第一次除法,此处为 1/2 = 0
              【解决方案17】:

              假设factors函数返回n的因子(例如factors(60)返回列表[2,2,3,5]),这里有一个函数来计算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
              

              【讨论】:

              • 那是蟒蛇吗?无论如何,它肯定不是 python 3.x。
              • 是伪代码,应该很容易翻译成python。
              • 迟到 3 年,迟到总比没有好 :) IMO,这是最简单、最短的代码。我没有比较表,但我可以在 i5 便携式笔记本电脑上分解和计算高达 1 秒内的除数。
              【解决方案18】:

              改编自CodeReview,这是一个适用于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))
              

              【讨论】:

              • 我似乎遇到了一个错误:NameError: global name 'prime_factors' is not defined。其他答案和原始问题都没有定义它的作用。
              【解决方案19】:

              我喜欢 Greg 解决方案,但我希望它更像 python。 我觉得它会更快,更具可读性; 所以经过一段时间的编码后,我得出了这个结论。

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

              “Factorgenerator”现在返回一个字典。然后将字典输入“除数”,后者使用它首先生成一个列表列表,其中每个列表是具有 p 素数的 p^n 形式的因子列表。 然后我们制作这些列表的笛卡尔积,最后我们使用 Greg 的解决方案来生成除数。 我们对它们进行排序,然后返回它们。

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

              Pietro Speroni (pietrosperoni 点)

              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 上发帖。 我期待任何反馈。

              【讨论】:

              • 在 Python 2.6 中有一个 itertools.product()。
              • 在任何地方都使用生成器而不是 list.append 的版本可能会更干净。
              • 埃拉托色尼筛法可用于生成小于或等于 sqrt(n) stackoverflow.com/questions/188425/project-euler-problem#193605 的素数
              • 编码风格:exponents = [k**x for k, v in factors.items() for x in range(v+1)]
              • 对于列表指数:[[k**x for x in range(v+1)] for k,v in factors.items()]
              猜你喜欢
              • 2023-03-15
              • 2017-11-16
              • 1970-01-01
              • 1970-01-01
              • 2011-08-14
              • 2022-06-20
              • 1970-01-01
              • 2010-10-17
              • 2016-08-31
              相关资源
              最近更新 更多