【问题标题】:why is recursive tetration faster than iterative tetration in C and python?为什么递归四分法比 C 和 python 中的迭代四分法更快?
【发布时间】:2020-08-30 00:05:37
【问题描述】:

我在 Python 中写了以下两个四分法函数:

def recur_tet(b, n):
    if n == 1:
        return(b)
    else:
        return(b ** recur_tet(b, n - 1))

def iter_tet(b, n):
    ans = 1
    for i in range(n):
        ans = b ** ans
    return(ans)

而且,令人惊讶的是,递归版本稍微快了一点:

python3> %timeit recur_tet(2,4)
1 µs ± 12.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

python3> %timeit iter_tet(2,4)
1.15 µs ± 14.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

我认为这可能与 Python 解释它的方式有关,所以我做了一个 C 版本:

/* tetration.c */
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int recur_tet(int b, int n){
    if(n == 1){
        return(b);
    }
    else{
        return(pow(b, recur_tet(b, n - 1)));
    }
}

int iter_tet(int b, int n){
    int ans = 1;
    int i;
    for(i = 1; i <= n; i++){
        ans = pow(b, ans);
    }
    return(ans);
}

int main(int argc, char *argv[]){
    /* giving an argument of "1" will do a recursive tetration
    while an argument of "2" will do an iterative one */
    if(atoi(argv[1]) == 1){
        recur_tet(2,4);
    }
    else if(atoi(argv[1]) == 2){
        iter_tet(2,4);
    }
    return(0);
}

而且递归版本更快:

> gcc tetration.c -o tet.o
> time(while ((n++ < 100000)); do ./tet.o 1; done)

real    4m24.226s
user    1m26.503s
sys     1m32.155s
> time(while ((n++ < 100000)); do ./tet.o 2; done)

real    4m40.998s
user    1m30.699s
sys     1m37.110s

所以这种差异似乎是真实的。汇编的 C 程序(由gcc -S 返回)将recur_tet 表示为42 条指令,而iter_tet 是39 条指令,所以看起来递归的应该更长?但我对组装一无所知,所以谁知道。

不管怎样,有没有人知道为什么每个函数的递归版本更快,尽管关于递归与迭代的普遍看法?我是否以愚蠢的方式编写迭代版本,并且效率低下?

【问题讨论】:

  • 试试gcc -O2gcc -O3。不确定哪个会更快,但这是一个更好的比较。
  • 在使用在线 repl 测试您的 Python 代码时,使用 timeit 并为每个测试进行 1M 次迭代,但多次运行测试我的结果显示有时递归版本稍快,其他时候迭代稍快.
  • 是函数调用和返回(执行n-1,将pars复制到堆栈,有返回值跳转并返回)vs.for循环管理(递增和比较)。我不确定什么更快,但对我来说获胜者是递归听起来很奇怪......
  • 奇怪!在我的电脑上,1M 迭代的递归算法确实更快

标签: python c performance recursion


【解决方案1】:

Python 和 C 比较的问题在于递归和迭代算法并不真正等效(即使它们应该产生相同的结果)。

n1 时,递归版本将立即返回b,不执行求幂。但是在这种情况下,迭代版本正在做幂运算(Python 中的b**1 和C 中的pow(b, 1))。这是迭代版本速度较慢的原因。

因此,一般来说,迭代版本比递归版本多进行一次幂调用。

为了进行公平比较,要么在 n1 时更改递归版本以进行求幂,要么更改迭代版本以避免它。

【讨论】:

  • 啊哈!果然,就是这样。微妙的!非常感谢
  • @David 是的,这是一个微妙的区别;)如果他们都在做同样的事情,迭代通常比递归更快。
猜你喜欢
  • 2019-03-25
  • 2015-03-26
  • 1970-01-01
  • 2013-09-21
  • 2015-09-20
  • 2012-01-26
  • 1970-01-01
  • 2021-01-07
  • 1970-01-01
相关资源
最近更新 更多