提起递归问题, 首先想到的是Fibonacci 和 n fractorial。为了提高算法效率, 我们常常避免使用递归。 fibonacci 可以通过DP的办法求解。因为Fibonacci 不仅具有overlapping subproblem, 而且还有最优子结构(optimal structure)。 但是n factorial 却不能使用DP办法解决。 下面给出具体原因。
n factorial :
def factorial(n):
if n == 0: return 1
return n*factorial(n-1)
Thus the problem of calculating
factorial(n)
depends on calculating the subproblem
factorial(n-1)
. This problem does
not
exhibit
overlapping
subproblems since
factorial
is called exactly once for each positive integer less than n.
fibonacci
def fib(n):
if n == 0: return 0
if n == 1: return 1
return fib(n-1) + fib(n-2)
计算过程如下:
fib(5) fib(4) + fib(3) fib(3) + fib(2) + fib(2) + fib(1) fib(2) + fib(1) + fib(1) + fib(0) + fib(1) + fib(0) + fib(1) fib(1) + fib(0) + fib(1) + fib(1) + fib(0) + fib(1) + fib(0) + fib(1)
对于那些无法使用DP的, 具有递归结构的问题, 如何去优化呢?
例如, 在这里, 求解n factorial:
对于n factorial , 我们可以使用迭代的办法:
d
递归和迭代的比较如下:
虽然n factorial 不能使用动态规划, 但是当我们的问题成为求解某一个需要提前求解出 1!, 2!, 3!, ...., 以便日后使用的时候, 我们可以(勉强)使用动态规划。 可以在网上搜到:
#include <iostream>
using namespace std;
// Factorial bottom-up dynamic programming.
// In bottom-up programming, programmer
// has to do the thinking by selecting values
// to calculate and order of calculation.
// This problem is not a good example because
// this problem does not exhibit overlapping subproblems
// since factorial is called exactly once
// for each positive integer less than n.
int fact_dp_bu(int n)
{
int result[n];
if (n >= 0) {
result[0] = 1;
for(int i=1; i<=n; ++i) {
result[i] = i * result[i-1];
}
return result[n];
}
else {
return numeric_limits<int>::min();
}
}
// Factorial top-down dynamic programming
// In top-down programming, recursive
// structure of original code is preserved, but
// unnecessary recalculation is avoided.
int factorial[100];
int fact_dp_td(int n)
{
if (n < 0)
return numeric_limits<int>::min();
if (factorial[n] > 1)
return factorial[n];
if (n == 0 || n == 1) // base cases first
return 1;
factorial[n] = n * fact_dp_td(n-1); // recursion case
return factorial[n];
}
/**
* Computes the factorial of the nonnegative integer n.
* @pre n >= 0.
* @post None.
* @param n The nonnegative integer to compute its factorial.
* @return The factorial of n; n is unchanged.
*/
int fact_recursion(int n) // factorial recursion
{
if (n == 0)
return 1; // base case
else
return n * fact_recursion(n-1); // n! = n(n-1)!
// recursion case
}
int fact_iteration(int n) // factorial iteration
{
if (n >= 0) {
int result = 1;
for(int i=1; i<=n; ++i) {
result = result * i;
}
return result;
}
else {
return numeric_limits<int>::min();
}
}
// 附在这里的是计算Fibonacci数列的dp 的一个优化后的解决方案
// Fibonacci bottom-up dynamic programming.
int fibonacci_dp_bu(int n)
{
int fib[n];
fib[1] = 1;
fib[2] = 1;
for (int j=3; j<=n; ++j)
fib[j] = fib[j-1] + fib[j-2];
return fib[n];
}