Calculating the first triangle number to have over 500 divisors in python
我正在努力解决关于项目Euler的第12个问题。我可以在4分钟内计算出超过500个除数。我怎样才能更快呢?这是尝试;
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 | 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) |
您不需要数组来存储三角形数字。您可以使用单个int,因为您只检查一个值。另外,使用三角形数公式可能会有所帮助:
然而,您真正的问题在于for循环中的
所以您要做以下的操作(
- 确保
n/d 没有余数。 - 确定是在除数上加1还是2。
使用装饰器(由ActiveState配方提供)保存先前计算的值,并使用列表理解生成设计器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 |
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | In [2]: %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) > 200: : print 'Found:', n : break : :startTime=time.time() :main() :print(time.time()-startTime) :-- Found: 2015 220.681169033 |
一些评论。
正如QuinCunx所写,您只需要检查1..sqrt(n)的整数范围,它将转换为类似于此
你可以使用三角形数公式(直到现在我才知道,谢谢你梅花),或者你可以使用另一种方法来查找三角形数,而不是递归和字典查找。您只需要序列中的下一个数字,所以保存它没有意义。函数调用在Python中涉及大量开销,因此通常不建议使用递归进行数字计算。还有,为什么《江户记》〔11〕的演员,我还没有完全明白?
我看到你已经用
我试过这样做,使用生成器,下面的代码在我的机器(YMMV)上大约16秒内找到第500个三角形。但我也用了一个巧妙的技巧来找到除数,也就是二次筛。
这是我的代码:
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 | 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 |
运行它会在我的普通机器上产生以下输出:
1 2 3 4 5 6 | morten@laptop:~/documents/project_euler$ time python pr012.py 76576500 real 0m16.584s user 0m16.521s sys 0m0.016s |
使用三角形公式而不是简单的方法:
1 2 3 | real 0m3.437s user 0m3.424s sys 0m0.000s |
我为相同的任务编写了代码。它相当快。我使用了一种非常快速的因子查找算法来查找数字的因子。我还用
1 2 3 4 5 6 7 8 9 10 | 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) |
它在一台正常的计算机上15秒钟内完成工作。
这是另一个解决问题的方法,在这个方法中,我用埃拉托森筛来找到素数,然后进行素数分解。应用以下公式计算一个数的因子数:因子总数=(n+1)*(m+1)……
其中,数字=2^n*3^n……
我的最佳时间是1.9秒。
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 | 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) |