【问题标题】:Surprised about good recursion performance in python对 python 中良好的递归性能感到惊讶
【发布时间】:2016-12-11 13:47:42
【问题描述】:

我为素数分解写了这个相当糟糕的 Python 函数:

import math

def factor(n):
    for i in range(2, int(math.sqrt(n)+1)):
        if not n % i:
            return [i] + factor(n//i)
    return [n]

它按预期工作,现在我感兴趣的是使用迭代方法时性能是否会更好:

def factor_it(n):
    r = []
    i = 2
    while i < int(math.sqrt(n)+1):
        while not n % i:
            r.append(i)
            n //= i
        i +=1
    if n > 1:
        r.append(n)
    return r

但我观察到(虽然函数给出了相同的结果)是迭代函数需要更长的时间才能运行。至少我有这种感觉,当这样做时:

number = 31123478114123
print(factor(number))
print(factor_it(number))

所以我测量了:

setup = '''
import math

def factor(n):
    for i in range(2, int(math.sqrt(n)+1)):
        if not n % i:
            return [i] + factor(n//i)
    return [n]

def factor_it(n):
    r = []
    i = 2
    while i < int(math.sqrt(n)+1):
        while not n % i:
            r.append(i)
            n //= i
        i +=1
    if n > 1:
        r.append(n)
    return r
'''

import timeit

exec(setup)

number = 66666667*952381*290201
print(factor(number))
print(factor_it(number))

print(timeit.Timer('factor('+str(number)+')',setup=setup).repeat(1,1))
print(timeit.Timer('factor_it('+str(number)+')',setup=setup).repeat(1,1))

这就是我得到的:

[290201, 952381, 66666667]
[290201, 952381, 66666667]
[0.19888348945642065]
[0.7451271022307537]

为什么在这种情况下递归方法比迭代方法快?

我使用 WinPython-64bit-3.4.4.2 (Python 3.4.4 64bits)。

【问题讨论】:

    标签: python performance python-3.x recursion


    【解决方案1】:

    这是因为您每次都在重新计算 sqrt。此修改的运行速度与您的递归版本一样快:

    def factor_it2(n):
        r = []
        i = 2
        lim = int(math.sqrt(n)+1)
        while i < lim:
            while not n % i:
                r.append(i)
                n //= i
            lim = int(math.sqrt(n)+1)
            i += 1
        if n > 1:
            r.append(n)
        return r
    

    timeit 给了我这些时间:

    factor      0.13133816363922143
    factor_it   0.5705408816539869
    factor_it2  0.14267319543853973
    

    我认为剩下的微小差异是由于for … in range(…) 比等效的while 循环更快,因为for 循环可以使用生成器,而不必执行一堆比较。

    【讨论】:

    • 嘿,太好了!但有趣的是没有那么快;)
    • 尽管在两个函数中计算平方根的频率相同,但仍然存在性能差距
    • @Wolf 好吧,这取决于。您正在将“递归”函数与 3 调用进行比较。这是不公平的。只需选择像number = 2**8 * 3**4 * 4**3 * 5**2 * 7 * 11 * 13 * 15 这样的数字,然后转牌即可。
    • @MariusSiuram 好点!好吧,但是...在分解未知数字之前,我怎么知道这一点?但你是对的,也许我应该看看概率并接受这个答案。另一方面,可能有些语言具有廉价的递归和昂贵的变量。
    • @Wolf 我认为第一件事是:不要在 python 中使用factor——至少不要在 Python 解释器中,做一个 C 扩展、Cython 或类似的东西。在标题中,您问的是“递归性能”,我想说说 3 深度测试的“递归”有点出乎意料。当然你是对的,对于这个特定的例子,性能很奇怪,而且还有一些语言具有廉价的递归(也许是 Erlang?如果我没记错的话)。但是对于 Python 深度递归函数,换成迭代算法。
    猜你喜欢
    • 2015-10-30
    • 2014-05-09
    • 1970-01-01
    • 2022-06-11
    • 2017-04-10
    • 1970-01-01
    • 2010-09-26
    • 1970-01-01
    • 2018-01-09
    相关资源
    最近更新 更多