用python计算第一个除数超过500的三角形数

2024-06-02 20:27:02 发布

您现在位置:Python中文网/ 问答频道 /正文

我想解决欧拉计划的第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)

Tags: theinreturniftimemaindef计划
3条回答

不需要数组来存储三角形数字。您可以使用单个int,因为您只检查一个值。另外,使用三角形数公式可能会有帮助:在这里可以找到第n个三角形数。

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

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

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

  • 确保n/d没有余数。
  • 决定是在除数中加1还是2。

一些评论。

正如Quincunx所写,您只需要检查1..sqrt(n)之间的整数范围,该范围将转换为类似于for i in xrange(1, sqrt(n) + 1): ...的值。仅此优化就大大加快了速度。

你可以使用三角数公式(我直到现在才知道,谢谢你的梅花形),或者你可以使用另一种方法来找到三角数,而不是递归和字典查找。你只需要序列中的下一个数字,所以没有必要保存它。函数调用在Python中涉及很大的开销,因此递归通常不推荐用于数字处理。还有,为什么演员要float,我不太明白?

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

我试过这样做,使用生成器,下面的代码在我的机器(YMMV)上大约16秒内找到第500个三角形编号。但我也用了一个巧妙的技巧来找到除数,那就是quadratic sieve

这是我的代码:

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

在我简陋的机器上运行它会产生以下输出:

morten@laptop:~/documents/project_euler$ time python pr012.py 
76576500

real    0m16.584s
user    0m16.521s
sys     0m0.016s

使用三角形公式而不是简单的方法:

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

使用decorator(由activestate recipes提供)保存先前计算的值,并使用列表理解来生成设计器:

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

以及

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

相关问题 更多 >