分类:
2009-02-05 10:03:36
n! = 1 * 2 * 3 * .... * (n-2) * (n-1) * n可以根据定义写出如下迭代形式的实现
int fact(int n) {还可以写出递归形式的实现
int i;
int result;
result = 1;
for (i = 1; i <= n; i++) {
result = result * i;
}
return result;
}
int fact(int n) {比较这两个版本:
if (n == 1) return 1;
return n * fact(n - 1);
}
int foo(int x) {
if (x <= 0) return x;
return foo(x - 1);
}
int foo(int x) {
if (x <= 0) return x;
return bar(x);
}
int bar(int y) {
return foo(y - 1);
}
尾递归函数通常被描述为:“返回上次递归调用得到的值作为函数的返回值。”尾递归具有很大的价值,因为在(递归)计算过程中需要维护的信息量与递归调用次数是无关的。某些现代计算机系统在实际上通过迭代过程来完成尾递归函数的计算。
“声名狼藉"的级数函数通常被写成非尾递归的形式:
int fact (int n) { /* n >= 0 */级数函数也可以被写成尾递归形式:
if (n == 0) return 1;
return n * fact(n - 1);
}
注意到这里在每次递归调用返回时存在一个未决操作——乘法运算。只要存在未决操作,递归函数就是
非尾递归的。所有未决操作的相关信息必须被存储,因此是与递归调用的次数密切相关的。
int fact_aux(int n, int result) {
if (n == 1) return result;
return fact_aux(n - 1, n * result)
}
int fact(n) {
return fact_aux(n, 1);
}
辅助函数fact_aux被用来保证不需要改变fact(n)的调用形式。实际的递归函数是fact_aux,而不
是fact。注意fact_aux中不存在未决操作。通过递归调用得到的值被不加修改的直接返回。计算过程
中需要维持的信息量是常数(变量n和变量value的值),与递归调用的次数无关。
例如,“声名狼藉"的fact函数是线性递归。其未决操作是简单的乘以一个标量,并不涉及对fact的递归调用。
一个递归函数被称为树状递归(或非线性递归),如果未决操作也是递归调用的话。
Fibonacci函数为树状递归提供了一个经典的例子。Fibonacci数列按如下规则定义:
fib(n) = 0 if n is 0,例如,前7个Fibonacci数分别是:
= 1 if n is 1,
= fib(n-1) + fib(n-2) otherwise
Fib(0) = 0
Fib(1) = 1
Fib(2) = Fib(1) + Fib(0) = 1
Fib(3) = Fib(2) + Fib(1) = 2
Fib(4) = Fib(3) + Fib(2) = 3
Fib(5) = Fib(4) + Fib(3) = 5
Fib(6) = Fib(5) + Fib(4) = 8
int fib(int n) { /* n >= 0 */
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n - 1) + fib(n - 2);
}
例如,可以通过使用两个用于存放结果的辅助参数来实现一个尾递归的Fibonacci函数。原有的树状递归函数fib需要两个附注参数来存放结果,这并不奇怪,因为它涉及两个递归调用。要计算fib(n),则调用fib_ax(n 1 0)
int fib_aux(int n, int next, int result) {
if (n == 0) return result;
return fib_aux(n - 1, next + result, next);
}
References: Thomas A. Anastasio, Richard Chang.