为什么for循环阶乘函数比递归函数快?

时间:2018-07-24 13:29:10

标签: python python-3.x performance

我写了两个函数来计算组合。第一个使用for循环,另一个使用递归阶乘函数。为什么第一个比第二个快?

def combinations(n: int, k: int) -> int:
    # Collection >= Selection
    if n < k:
        raise ValueError(
            "The size of the collection we are selecting items from must be "
            "larger than the size of the selection."
        )
    # Sizes > 0
    if n < 0 or k < 0:
        raise ValueError(
            "Cannot work with negative integers."
        )
    # Compute with standard python only
    numerator = 1
    for i in range(n + 1 - k, n+1):
        numerator *= i
    denominator = 1
    for i in range(1, k+1):
        denominator *= i
    return int(numerator / denominator)

第二个函数需要定义为的阶乘函数:

def factorial(n: int) -> int:
    if n < 0:
        raise ValueError(
            "Cannot calculate factorial of a negative number."
        )
    # Recursive function up to n = 0
    return n * factorial(n - 1) if n - 1 >= 0 else 1

它的定义是:

def combinations2(n: int, k: int) -> int:
    # Collection >= Selection
    if n < k:
        raise ValueError(
            "The size of the collection we are selecting items from must be "
            "larger than the size of the selection."
        )
    return int(factorial(n) / (factorial(k) * factorial(n - k)))

当我在IPython控制台上运行以下测试时,很明显哪个测试速度更快

%timeit combinations(1000, 50)
16.2 µs ± 1.95 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit combinations2(1000, 50)
1.6 ms ± 129 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

新版本的组合2

好的,在评论之后,我同意combinations2正在执行更多操作。所以我重写了阶乘和组合函数,这是它们的版本:

def factorial(n: int, lower: int=-1) -> int:
    # n > 0
    if n < 0:
        raise ValueError(
            "Cannot calculate factorial of a negative number."
        )
    # Recursive function up to n = 0 or up to lower bound
    if n - 1 >= 0 and n - 1 >= lower:
        return n * factorial(n - 1, lower)
    return 1

现在可以有一个下限。注意,一般阶乘(a,b)=阶乘(a)/阶乘(b)。另外,这是Combines2函数的新版本:

def combinations2(n: int, k: int) -> int:
    if n < k:
        raise ValueError(
            "The size of the collection we are selecting items from must be "
            "larger than the size of the selection."
        )
    return int(factorial(n, n - k) / factorial(k))

但这又是他们的比较:

%timeit combinations(100, 50)
10.5 µs ± 1.67 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit combinations2(100, 50)
56.1 µs ± 5.79 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

1 个答案:

答案 0 :(得分:4)

只需计算操作次数:

combinations中,您要对分子进行(n+1) - (n+1-k)乘法,对分母进行(k+1) - 1乘法。

总计:2k个乘法

cominations2中,您要进行n + k + (n-k)乘法,即2n乘法。

您也正在进行2n函数调用以进行递归。

有了k=50n=1000,难怪第一个解决方案会更快。