在python中计算第一个具有500个以上除数的三角形数

在python中计算第一个具有500个以上除数的三角形数,python,performance,math,Python,Performance,Math,我正试图解决欧拉计划的第12个问题。我能在4分钟内计算出超过500个除数的数字。我怎样才能使它更快?这里是尝试 import time def main(): memo={0:0,1:1} i=2 n=200 while(1): if len(getD(getT(i)))>n: break i+=1 print(getT(i)) #returns the nth triangle numbe

我正试图解决欧拉计划的第12个问题。我能在4分钟内计算出超过500个除数的数字。我怎样才能使它更快?这里是尝试

import time

def main():
    memo={0:0,1:1}
    i=2
    n=200
    while(1):
        if len(getD(getT(i)))>n:
            break
        i+=1
    print(getT(i))

#returns the nth triangle number
def getT(n):
    if not n in memo:
        memo[n]=n+getT(n-1)
    return memo[n]

#returns the list of the divisors
def getD(n):
    divisors=[n]
    for i in xrange(1,int((n/2)+1)):
        if (n/float(i))%1==0:
            divisors.append(i)
    return divisors

startTime=time.time()
main()
print(time.time()-startTime)
使用decorator(由提供)保存以前计算的值,并使用列表理解生成decorator:

def memodict(f):
    """ Memoization decorator for a function taking a single argument """
    class memodict(dict):
        def __missing__(self, key):
            ret = self[key] = f(key)
            return ret 
    return memodict().__getitem__

@memodict
def trinumdiv(n):
    '''Return the number of divisors of the n-th triangle number'''
    numbers = range(1,n+1)
    total = sum(numbers)
    return len([j for j in range(1,total+1) if total % j == 0])

def main():
    nums = range(100000)
    for n in nums:
        if trinumdiv(n) > 200:
           print n
           break
结果:

In [1]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:def main():
:       nums = range(10000)
:       for n in nums:
:               if trinumdiv(n) > 100:
:                  print 'Found:', n
:                  break
:
:startTime=time.time()
:main()
:print(time.time()-startTime)
:--
Found: 384
1.34229898453


存储三角形数字不需要数组。可以使用单个int,因为只检查一个值。此外,使用三角形编号公式可能会有所帮助:
n*(n+1)/2
,您可以在其中找到
n
第个三角形编号

getD
也只需要返回一个数字,因为您只需要查找500个除数,而不是除数的值

但是,真正的问题在于for循环中的
n/2
。通过检查因子对,可以使用
sqrt(n)
。因此,只检查小于等于
sqrt(n)
的值。如果你检查到
n/2
,你会得到大量浪费的测试(数以百万计)

因此,您需要执行以下操作(
n
是查找除数的整数,
d
是可能的除数):

  • 确保
    n/d
    没有剩余部分
  • 确定是向除数添加1还是2
    • 一些评论

      正如Quincunx所写的,您只需要检查1..sqrt(n)的整数范围,对于xrange(1,sqrt(n)+1中的i,这将转换为如下内容:…。仅此优化就大大加快了速度

      你可以使用三角形数公式(我刚才才知道,谢谢你,梅花形),或者你可以使用另一种方法来找到三角形数,而不是递归和字典查找。您只需要序列中的下一个数字,因此保存它没有意义。函数调用在Python中涉及大量开销,因此通常不建议对数字处理使用递归。还有,为什么要转换成浮动,我不太明白

      我看到您已经在使用
      xrange
      而不是
      range
      来构建
      int
      流。我假设您知道
      xrange
      更快,因为它是作为生成器函数实现的。你也可以这样做。这也让事情变得更加顺利

      我已经试着这样做了,使用生成器,下面的代码在我的机器上16秒内找到第500个三角形数字(YMMV)。但我也使用了一个巧妙的技巧来找到除数,这就是

      这是我的密码:

      def triangle_num_generator():
          """ return the next triangle number on each call
              Nth triangle number is defined as SUM([1...N]) """
          n = 1
          s = 0
          while 1:
              s += n
              n += 1
              yield s
      
      
      def triangle_num_naive(n):
          """ return the nth triangle number using the triangle generator """
          tgen = triangle_num_generator()
          ret = 0
          for i in range(n):
              ret = tgen.next()
          return ret
      
      def divisor_gen(n):
          """ finds divisors by using a quadrativ sieve """
          divisors = []
          # search from 1..sqrt(n)
          for i in xrange(1, int(n**0.5) + 1):
              if n % i is 0:
                  yield i
                  if i is not n / i:
                      divisors.insert(0, n / i)
          for div in divisors:
              yield div
      
      
      def divisors(n):
          return [d for d in divisor_gen(n)]
      
      
      num_divs = 0
      i = 1
      while num_divs < 500:
          i += 1
          tnum = triangle_num_naive(i)
          divs = divisors(tnum)
          num_divs = len(divs)
      
      print tnum # 76576500
      
      使用三角形公式而不是简单的方法:

      real    0m3.437s
      user    0m3.424s
      sys     0m0.000s
      

      我为同样的任务编写了一个代码。它相当快。我使用了一种非常快速的因子查找算法来查找数字的因子。我还使用
      (n^2+n)/2
      查找三角形数字。代码如下:

      from functools import reduce
      import time
      start = time.time()
      n = 1
      list_divs = []
      while len(list_divs) < 500:
          tri_n = (n*n+n)/2 # Generates the triangle number T(n)
          list_divs = list(set(reduce(list.__add__,([i, int(tri_n//i)] for i in range(1, int(pow(tri_n, 0.5) + 1)) if tri_n % i == 0)))) # this is the factor generator for any number n
          n+=1
      print(tri_n, time.time() - start)
      
      从functools导入reduce
      导入时间
      开始=时间。时间()
      n=1
      列表_divs=[]
      而len(列表分区)<500:
      triu#n=(n*n+n)/2生成三角形数T(n)
      list_divs=list(如果tri_n%i==0,则为范围(1,int(pow(tri_n,0.5)+1))内的i设置(减少(list._添加)([i,int(tri_n//i)]))。#这是任意数字n的因子生成器
      n+=1
      打印(tri_n,time.time()-start)
      

      在一台正常的计算机上,它只需15秒钟就完成了这项工作。

      这是解决这个问题的另一个方法。在这篇文章中,我使用埃拉托斯烯筛来寻找素数,然后进行素数分解。 应用以下公式计算一个数的因子数: 因子总数=(n+1)*(m+1)

      其中数字=2^n*3^n

      我的最佳时间是1.9秒

      from time import time
      t=time()
      
      a=[0]*100
      c=0
      for i in range(2,100):
          if a[i]==0:
              for j in range(i*i,100,i):
                  continue
              a[c]=i
              c=c+1
      print(a)
      
      n=1
      ctr=0
      while(ctr<=1000):
          ctr=1
          triang=n*(n+1)/2
          x=triang
          i=0
          n=n+1
          while(a[i]<=x):
              b=1
              while(x%a[i]==0):
                  b=b+1
                  x=x//a[i];
              i=i+1
              ctr=ctr*b
      print(triang)
      print("took time",time()-t)
      
      从时间导入时间
      t=时间()
      a=[0]*100
      c=0
      对于范围(2100)内的i:
      如果a[i]==0:
      对于范围内的j(i*i,100,i):
      持续
      a[c]=i
      c=c+1
      印刷品(a)
      n=1
      ctr=0
      
      而(ctr这是我的答案,大约3秒钟就解决了。我认为跟踪除数或生成一个素数列表用作除数可以加快速度……但3秒钟对我来说已经足够快了

      import time
      
      def numdivisors(triangle):
        factors = 0
        for i in range(1, int((triangle ** 0.5)) + 1):
          if triangle % i == 0:
            factors += 1
        return factors * 2
      
      def maxtriangledivisors(max):
        i = 1
        triangle = 0
        while i > 0:
          triangle += i
          if numdivisors(triangle) >= max:
            print 'it was found number', triangle,'triangle', i, 'with total of ', numdivisors(triangle), 'factors'
            return triangle
          i += 1
      
      startTime=time.time()
      maxtriangledivisors(500)
      print(time.time()-startTime)
      

      重复项:,,一个细节:如你所说,如果
      i
      ,其中
      sqrt=Math.sqrt(n)
      是一个因子,那么
      i
      n/i
      都是因子。但是,如果
      sqrt
      是一个因子,那只会在总数中加上一个,因为
      sqrt==n/sqrt
      。因此因子总数是
      i的两倍,对不起,但我不明白。例如:
      28
      有以下内容除数:
      1,2,4,7,14,28
      28
      的平方根是
      5.2915026
      。因此,如果我数到平方根,我将错过
      7
      @lorussian
      4*7=28
      。因此,如果你查看sqrt,你会发现7。@Justin请详细说明你的评论。没有更多上下文,这没有意义。他也会错过14.你找到14,因为你找到2…所以当使用这个方法时,你必须意识到因子的数量大约是一半。假设平方根除数在所有可能除数的中心,所以在这个例子中它在4到7之间,
      from time import time
      t=time()
      
      a=[0]*100
      c=0
      for i in range(2,100):
          if a[i]==0:
              for j in range(i*i,100,i):
                  continue
              a[c]=i
              c=c+1
      print(a)
      
      n=1
      ctr=0
      while(ctr<=1000):
          ctr=1
          triang=n*(n+1)/2
          x=triang
          i=0
          n=n+1
          while(a[i]<=x):
              b=1
              while(x%a[i]==0):
                  b=b+1
                  x=x//a[i];
              i=i+1
              ctr=ctr*b
      print(triang)
      print("took time",time()-t)
      
      import time
      
      def numdivisors(triangle):
        factors = 0
        for i in range(1, int((triangle ** 0.5)) + 1):
          if triangle % i == 0:
            factors += 1
        return factors * 2
      
      def maxtriangledivisors(max):
        i = 1
        triangle = 0
        while i > 0:
          triangle += i
          if numdivisors(triangle) >= max:
            print 'it was found number', triangle,'triangle', i, 'with total of ', numdivisors(triangle), 'factors'
            return triangle
          i += 1
      
      startTime=time.time()
      maxtriangledivisors(500)
      print(time.time()-startTime)