“递归”的开销,并不总是很大
今天跟群友讨论,发现很多朋友对递归存在一定偏见,其中有位朋友给出代码对比,证实递归性能比循环要差,递归的代码大概是这样:
int fib_recur1(int n) {
if (n == 1)
return 1;
if (n == 2)
return 1;
return fib_recur1(n - 1) + fib_recur1(n - 2);
}
“竞争对手”循环的代码大概是这样:
int fib_loop(int n) {
int n1, n2, i, tmp;
n1 = 1;
n2 = 1;
for (i = 2; i < n; i++) {
tmp = n2;
n2 = n1 + n2;
n1 = tmp;
}
return n2;
}
这种写法下,循环版肯定快了一个数量级。但这个并不是“递归”和“循环”的对比,而是两种不同计算方式的对比。
如果要使用相同的计算方式,递归代码应该是下面这样:
int fib_recur2_(int n1, int n2, int cnt, int target) {
if (cnt < target)
return fib_recur2_(n2, n1 + n2, cnt + 1, target);
else
return n2;
}
为了对外提供一样的接口,另用一个函数包装一下:
int fib_recur2(int n) {
return fib_recur2_(1, 1, 2, n);
}
补上一个main函数:
int main() {
printf("fib_recur1(10): %d\n", fib_recur1(10));
printf("fib_loop(10): %d\n", fib_loop(10));
printf("fib_recur2(10): %d\n", fib_recur2(10));
return 0;
}
跑跑看:
$ gcc a.c && ./a.exe
fib_recur1(10): 55
fib_loop(10): 55
fib_recur2(10): 55
感兴趣的朋友可以做个性能测试。
在没有尾调用优化的情况下, 第二个递归版本空间开销肯定更大(存在多重栈帧),但是时间开销上应该是和循环版本差不多的。
希望这个小例子能够消除一部分人对递归的偏见,毕竟很多地方,递归可能就是唯一解,这个技能是需要达到一定熟练度的。