Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为什么循环阶乘函数比递归函数更快?_Python_Python 3.x_Performance - Fatal编程技术网

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

Python 为什么循环阶乘函数比递归函数更快?,python,python-3.x,performance,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 "

我已经编写了两个函数来计算组合。第一个使用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)

新版本的组合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

只需计算操作数:

组合中
对分子进行
(n+1)-(n+1-k)
乘法,对分母进行
(k+1)-1
乘法

总计:
2k
乘法

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

您还进行了递归的
2n
函数调用


使用
k=50
n=1000
,难怪第一个解决方案更快。

简短版本:递归非常昂贵。python中的递归并不快。递归仅在(某些)其他语言中是快速的,因为编译器优化了尾部递归的递归部分,基本上将其转化为循环。尝试以不同的方式实现阶乘:而不是
返回n*factorial(n-1)…
定义一个辅助函数,该函数计算所有参数,即
def factorial\u help(n:int,acc:int)->int:如果n-1>0,则返回factorial\u help(n-1,n*acc),否则acc
并将factorial重写为
。。。返回阶乘帮助(n,1)
。还是慢得多吗?@TobiasBrösamle afaik Python没有尾部调用optimization@dashiell很高兴知道。我本以为这种方法会比“正常”递归方法更快,但在这种情况下,我认为不会。
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)))
%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)
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
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)