递归是计算机领域的一个重要的概念。
所谓的递归, 直白的说, 就是函数调用自己实现(Functions call themselves)。 可以用递归解决的经典问题就是求Fibonacci 数列求和问题。
递归的定义有两部分组成:
(1)base case, 也就是产生集合中其他元素的基本元素, base case 是递归必须的。 用递归方法解决问题的时候, 必须明确base case是什么, 这是我们解决问题的关键。没有base case 的所谓的“递归”会无限运行下去, 直至我们的我们的程序run out of stack memory, 造成 stack overflow. 程序crash down。 例如下面的程序是错误的(crash down):
#include <iostream>
using namespace std;
void foo() {
cout << "**" << endl;
foo();
}
int main() {
foo();
return 0;
}
(2).rules thatreduce all other cases toward the base case. 翻译为: 由基本元素或者已有的对象产生新对象的构造规则。
明白了这递归的含义, 说说Fibonacci sequence, 数列的第n项的构造规则为:
fib(n) = fib(n -1 ) + fib(n -2),
其中base case 为 fib(0) = 1, fib(1) = 1。
求出Fibonacci 第n 项的C++ 实现函数如下:
int fibonacci(int n) {
if(n == 0||n == 1) { //base case
return 1;
}
else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
一个完整的测试程序如下:
#include <iostream>
using namespace std;
int fibonacci(int n) {
if(n == 0||n == 1) { //base case
return 1;
}
else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
int main() {
cout << fibonacci(4) << endl;
return 0;
}
运行结果如下:
再举一个计算factorial 的例子:
#include <iostream>
using namespace std;
int factorial(int x) {
if(x == 1) return 1; // base case
return x * factorial(x -1);
}
int main()
{
cout << "5! = " << factorial(5) << endl;
return 0;
}
运行结果如下:
另外, 我们可以使用迭代的办法计算上面的n factorial problem。
递归算法思想清晰易懂。 但是容易带来效率的问题. 与迭代算法的比较如下:
使用递归的办法解决Fibonacci(n)的时间复杂度为exponential time O(2^n)。 如何改进呢?
generally, 改进的办法有三种:
(1)迭代(iteration)
(2) dynamic programming(动态规划)
(3)other strategies
对于Fibonacci , 我们可以使用动态规划。
下面给出另一个个可以用递归解决问题的例子:
Towers of Hanoi(汉诺塔)(问题解决的步骤数目记为T(n)) //n disks
3个pegs 如下, rule: disks从下到上面积递增。 小的disk必须在大的disk 之上, 否则就unbalanced。 最开始均位于peg A 之上。 我们的目标就是通过使用中介peg B, 将peg A 的所有的disk均移动到peg C上而不违背rule。
下面举个例子说明解决思路:
N = 2 :
step 1 :
得到:
step 2:
step 3:
N = 3 disks:
step1: place 1 to C:
step 2: place disk 2 to B:
step 3: move disk 1 from C to B:
step 4: move disk 3 from A to C:
step: 5: move disk one from B to A:
step 6: move disk 2 from B to C:
step7: move disk 1 from A to C:
于是, 我们花费了7步1去解决了这个问题。
那么如果disk 数目为4, 5 甚至更大的时候, 该如何办呢? 需要多少步解决呢?
算法如下(复杂度: T(n) = T(n-1) + 1 + T(n-1) = 2 T(n -1 ) + 1.):
例如N = 4, 我们已经知道可以到达下面的状态:
接下来, 将nth disk 移动到C中:
接下来, 我们可以使用A作为intermediate peg, 将B的3个disk 移动到C上面:
关于上述问题的递归的观察:
N = 2:
N = 3:(T(3)) step 1: we have known how to move 2 disk from A to B(T(2))
得到:
step2: move 1 disk(largest) from A to C (1 step):
sep3: we of course know how to move 2 disks from B to C usiing A(T(2)):
所以我们得到如下公式:
T(n) = T(n-1) + 1 + T(n-1) = 2 T(n -1 ) + 1.
不难求解得到:
T(n) = 2 ^n - 1
程序如下:
#include <iostream>
using namespace std;
void HanoiMove(int, int, int, int);
int main() {
int num;
cout << "Enter the number of disks: ";
cin >> num;
cout << endl;
HanoiMove(num, 1, 3, 2);
return 0;
}
void HanoiMove(int Count, int n1, int n3, int n2) {
if (Count > 0) {
HanoiMove(Count - 1, n1, n2, n3);
cout << "Move disk " << Count << " from " << n1 << " to " << n3
<< "." << endl;
HanoiMove(Count - 1, n2, n3, n1);
}
}
运行结果如下: