Python - 快速因子功能

时间:2017-06-30 06:02:37

标签: python performance

我正在尝试为超快因子函数编写代码。我已经尝试了一点,并提出了以下三个候选人(除了math.factorial):

def f1():
    return reduce(lambda x,y : x * y, xrange(1,31))

def f2():
    result = 1
    result *= 2
    result *= 3
    result *= 4
    result *= 5
    result *= 6
    result *= 7
    result *= 8
    #and so-on...
    result *= 28
    result *= 29
    result *= 30
    return result

def f3():
    return 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

我已经计时了这些功能。结果如下:

In [109]: timeit f1()
100000 loops, best of 3: 11.9 µs per loop

In [110]: timeit f2()
100000 loops, best of 3: 5.05 µs per loop

In [111]: timeit f3()
10000000 loops, best of 3: 143 ns per loop

In [112]: timeit math.factorial(30)
1000000 loops, best of 3: 2.11 µs per loop
显然,f3()取得了成功。我试过实现这个。为了详细,我尝试编写生成如下字符串的代码: “1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 ........”然后使用eval来评估此字符串。 (承认'eval'是邪恶的)。然而,这种方法在时间上没有给我带来任何好处。事实上,我花了将近150微秒才完成。

请告知如何概括f3()。

4 个答案:

答案 0 :(得分:4)

f3只是快速,因为它在调用它时实际上并没有计算任何东西。整个计算在编译时进行优化,并替换为最终值,因此所有时间都是函数调用开销。

如果我们使用dis模块反汇编函数,这一点尤其明显:

>>> import dis
>>> dis.dis(f3)
  2           0 LOAD_CONST              59 (265252859812191058636308480000000L)
              3 RETURN_VALUE

不可能将此加速概括为一个接受参数并返回其阶乘的函数。

答案 1 :(得分:1)

f3()取得了进展,因为当函数被定义时,Python只是将乘法字符串优化到最终结果,并且f3()的有效定义变为:

def f3():
    return 8222838654177922817725562880000000

,因为调用函数时不需要计算,运行得非常快!

产生在数字列表之间放置*运算符的所有效果的一种方法是使用reduce模块中的functools。这有点像你正在寻找的吗?

from functools import reduce
def fact(x):
    return reduce((lambda x, y: x * y), range(1, x+1))

答案 2 :(得分:0)

我认为这些都不是很好的因子函数,因为没有一个参数给函数。最后一个运行良好的原因是因为它最小化了解释器步骤的数量,但这仍然不是一个好的答案:所有这些都具有相同的复杂性(与值的大小成线性关系)。我们可以做得更好:O(1)。

import math
def factorial(x):
    return math.gamma(x+1)

这会随着输入值不断扩大,牺牲一些精度。当性能很重要时,情况仍然会更好。

我们可以做一个快速的基准测试:

import math
def factorial_gamma(x):
    return math.gamma(x+1)

def factorial_linear(x):
    if x == 0 or x == 1:
        return 1
    return x * factorial_linear(x-1)


In [10]: factorial_linear(50)
Out[10]: 30414093201713378043612608166064768844377641568960512000000000000

In [11]: factorial_gamma(50)
Out[11]: 3.0414093201713376e+64

In [12]: %timeit factorial_gamma(50)
537 ns ± 6.84 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [13]: %timeit factorial_linear(50)
17.2 µs ± 120 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

因子50的增加30倍。不错。

https://docs.scipy.org/doc/scipy-0.19.0/reference/generated/scipy.misc.factorial.html

答案 3 :(得分:0)

正如其他人所说,f3()实际上并没有计算任何可以获得如此快速结果的原因。通过将其赋予功能,您无法实现同样的目标。

你也许想知道为什么math.factorial()如此之快,因为 math module's functions在C:

中实现
  

此模块始终可用。它提供对C标准

定义的数学函数的访问

通过在C中使用高效算法,您可以获得如此快速的结果。

这里你最好的选择是使用以下功能,但如果你纯粹需要性能

,那么我更喜欢使用math.factorial
def f3(x):
    ans=1
    for i in range(1,x+1):
        ans=ans*i
    return ans
print(f3(30))