Fibonacci series and Dynamic Planning
What is a Fibonacci series?
The Fibonacci series refers to such a series.
{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89,144, ...}
Its 0th items are 0, and the 1st items are the first 1. From the second item, each item is equal to the sum of the first two items. It can be expressed in C language:
//Fib(n) = Fib(n-1) + fib(n-2)int Fib(int n) { return(2 > n) ? n : Fib(n-1) + Fib(n-2);}
This code can calculate the nth entry of the Fibonacci series. However, as N grows, the time consumed is gradually unacceptable. The time complexity can be calculated as follows:
T (0) = T (1) = 1; t (n) = T (n-1) + T (n-2) + 1, n> 1;
Make S (n) = [T (n) + 1]/2
Then S (0) = 1 = fib (1); s (1) = 1 = fib (2)
So S (n) = S (n-1) + S (n-2) = fib (n + 1)
T (n) = 2 * S (n)-1 = 2 * fib (n + 1)-1 = O (FIB (n + 1) = O (phi n) = O (2n)
This isIndexLevel of complexity.
How big is O (Φn?
Phi 43 = 230 = 109 Flo = 1 sec
Phi 67 = 1014flo = 105 sec = 1 day
Phi 92 = 1019flo = 1010 sec = 105 day = 3 Century
If the above algorithm is used to calculate the 67th requirements of the Fibonacci series1 dayTo obtain 92nd items, you need to completeSansheng III!
Why is it so big?
Use recursive tracing to draw the call relationship of Fib (5), for example:
It can be found that many of the items are repeatedly called multiple times. In total, there are only O (n) Types of recursive instances after deduplication.
How can we improve it?
MethodMemory Method(Memoization): query the results tabulation of instances that have been computed. The Code is as follows:
int memoir[100];int Fib(int n) { if(memoir[n] < 0) memoir[n]= (2 > n) ? n : Fib(n-1) + Fib(n-2); returnmemoir[n];}
Method BDynamic Planning: Reversing the calculation direction: the top-down recursion is changed to the bottom-up iteration. The Code is as follows:
int Fib(int n) { intf = 0; intg = 1; while(1 < n--) { g= g + f; f= g - f; } returng;}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
The time complexity of these two improved algorithms is O (n), the space complexity of the memory method is O (n), and the space complexity of dynamic planning is O (1 ).
LCs and Dynamic Planning
What is LCS?
LCS is the abbreviation of longest commonsubsequence, that is, the longest common subsequence. A sequence is the longest common subsequence if it is a subsequence of two or more known sequences and the longest of all subsequences.
How to find LCS?
There are three scenarios for sequence a [0, N], sequence B [0, m], and LCS (A [0, N], and sequence B [0, m ]).
If n =-1 or M =-1, the null sequence "" is obtained ""
If a [n] = B [m] = 'x', take LCS (A [0, n), B [0, m ))(Governance by subtraction)
If a [n] = B [m], then in LCS (A [0, N], B [0, m) and LCS (A [0, n ), B [0, m]) for Elders (Divide and conquer)
Each solution of LCS corresponds to a monotonous path between (0, 0) and (n, m), and vice versa. For example, the LCS ("advantage", "educational") solution process is as follows:
LCSMonotonic: In any case, the scale of the original problem must be reduced after each comparison. As two input sequences, the length of at least one of them is shortened by one unit.
What is the performance of LCS?
Based on the three branches mentioned above,RecursionThe code for the LCS algorithm is as follows:
int LCS(const char *A, const char *B, int n, int m) {if (n < 0 || m < 0)return 0;if (A[n] == B[m])return LCS(A, B, n-1, m-1) + 1;int result1 = LCS(A, B, n-1, m);int result2 = LCS(A, B, n, m-1);return result1 > result2 ? result1 : result2;}int main() {const char *A = "aadvantage";const char *B = "educational";printf("%d", LCS(A, B, strlen(A)-1, strlen(B)-1));return 0;}In the best case, this code only takes O (N + M) time. However, in the worst case, it takes O (2n) time. The reason is that when a [n] is less than B [m], the original problem will become two subproblems. What's worse, they are likely to have the same subproblems in further export. For example, many branches are created by the branches on the right and below:
How to Improve LCS?
Similar to the Fibonacci series, the LCS algorithm can also be converted from Recursion TO iteration, and is estimated from the back of the prefix. In this way, the time complexity can be reduced from O (2n) to O (M * n ). To solve this problem, you only need to list all the sub-problems into one table, and reverse the calculation direction. Starting from LCS (A [0], B [0]), all the items are calculated accordingly, as shown in:
IterationThe LCS code is as follows:
int table[20][20]; int LCS(const char *A, const char *B, intn, int m) { inti, j, max; for(i=0; i<=m; i++) { if(A[0] == B[i]) table[0][i]= 1; else table[0][i]= 0; } for(j=0; j<=n; j++) { if(B[0] == A[j]) table[j][0]= 1; else table[j][0]= 0; } for(i=1; i<=m; i++) { for(j=1; j<=n; j++){ max= table[j-1][i] > table[j][i-1] ? table[j-1][i] : table[j][i-1]; if(B[i] == A[j]) table[j][i]= max + 1; else table[j][i]= max; } } return table[n][m];}
Summary: Recursion can find a feasible solution, but sometimes it is not
EfficientSo dynamic planning is required to make it
Practical.
Note: This document is a MOOC course "Data Structure" from instructor Deng Junhui of Tsinghua University. If you are interested, click here to select a course.
[MOOC Notes] Chapter 1 XA Dynamic Planning (data structure)