Dynamic programming (programming) is the mathematical method for solving the decision-making process (decision) optimization. Its name and dynamics are not related, Richard Bellman for bluffing.
Dynamic programming is mainly used to solve the optimization problem with overlapping sub-problems, and its basic strategy is to decompose the original problem into similar sub-problems by solving and saving the solution of the repeating sub-problem, and then gradually merging it into the solution of the original problem. The key of dynamic programming is to store the answer of repeating question by memory method, avoid the repetition solution, and exchange space for time.
The classic problems solved with dynamic programming are: Shortest path (shortest path), 0-1 knapsack problem (knapsack problem), traveling salesman problem (traveling sales person) and so on.
(Note: knapsack problem is divided into two kinds: if the object is inseparable, it is called 0-1 knapsack problem, such as taking a piece of bricks, if the object can be separated, it is called the general knapsack problem, such as how many grams of rice. General knapsack problem can be solved by greedy algorithm. The greedy algorithm can find the optimal solution at each stage, and the optimal state of each stage is obtained by the optimal state of the previous stage. )
The problem that can be solved by dynamic programming needs to have the following two main characteristics:
1) Overlapping sub-problem (overlapping subproblems): Some sub-problems will be counted repeatedly.
2) optimal substructure (Optimal substructure): The optimal solution of the problem can be obtained from the optimal solution of a sub-problem.
The following is an example of the Fibonacci sequence to see the implementation of the dynamic programming algorithm.
The following is a 1-5 Fibonacci sequence recursive tree:
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 (0)
As you can see, the FIB (5) is summed up by FIB (4) and FIB (3), and the FIB (4) is summed up by FIB (3) and FIB (2), and so on. Wherein, the FIB (3) is calculated 2 times, and the FIB (2) is calculated 3 times. There is a lot of repetitive calculations here.
Calculate this Fibonacci sequence using the recursive method mentioned in the previous blog (the Fibonacci sequence is computed with a recursive method), and then add the print ("fib called with", N) statement and look at the invocation of the FIB function:
def fib (n): Print ("fib calledwith", N) # See which fib function was called, that is, the first of the Fibonacci sequence is calculated. if n<2: return n else: Return (FIB (n-1) + fib (n-2))
To calculate the 5th of the Fibonacci sequence, try this:
Print (FIB (5))
The results of the operation are as follows:
Fib called with 5
Fib called with 4
Fib called with 3
Fib called with 2
Fib called with 1
Fib called with 0
Fib called with 1
Fib called with 2
Fib called with 1
Fib called with 0
Fib called with 3
Fib called with 2
Fib called with 1
Fib called with 0
Fib called with 1
5
It can be seen that 15 calls were made, where the FIB (3) was computed 2 times and the FIB (2) was calculated 3 times.
A dynamic programming algorithm is used to calculate this Fibonacci sequence, which runs faster. The code is as follows:
defFastfib (N,memo):#memo is a dictionary of settings Print("FIB1 called with", N)if notNinchMemo#if the nth value of the Fibonacci sequence is not in the dictionary, the value is computed recursively and placed in the dictionaryMemo[n]=fastfib (N-1,memo) +fastfib (n-2, Memo)returnMemo[n]#if the nth value of the Fibonacci sequence is in the dictionary, then the value in the dictionary is returned directlydefFIB1 (N): Memo={0:0,1:1}#Initialize a dictionary returnFASTFIB (N,memo)
Also calculate the 5th test of the Fibonacci sequence, and the results are as follows:
Fib1 called with 5
Fib1 called with 4
Fib1 called with 3
Fib1 called with 2
Fib1 called with 1
Fib1 called with 0
Fib1 called with 1
Fib1 called with 2
Fib1 called with 3
5
You can see that 9 calls have been made, and after one calculation, the subsequent calls are directly to the dictionary to get the value.
Specifically, the process of using the dynamic programming algorithm to calculate the Fibonacci sequence is this: first set an empty array, and when the solution to the sub-problem is needed, go to the array to find it first. If the problem has been solved before, then the value is returned directly, and if the problem has not been solved before, then the value is computed and the result is put into the array for later use.
There are two different ways to store these values:
1) Moji (from top to bottom)/memoization (top down)
2) Form method (bottom to top)/tabulation (Bottom up)
So should we use the Moji method or the tabular method?
If you need to solve all the sub-problems, then the table method is often better than the Moji method. This is because the table method has no additional consumption of recursion, and uses a pre-allocated array (preallocated array) instead of the hash (hash map).
If you just need to solve some of these sub-problems, then the Moji law is better.
Reference: MIT Open Class: An Introduction to Computer science and programming (13th episode)
To compute the Fibonacci sequence as an example, the Dynamic programming algorithm (programming algorithm overlapping subproblems Optimal substructure memoization Tabulation)