The principle logic code of dynamic programming (algorithm that exchanges space for time) is super detailed! Reference from "Introduction to Algorithms"

Dynamic programming (algorithm that exchanges space for time)-examples and detailed usage explanations

This blog is based on the dynamic programming algorithm in Chapter 15 of " Introduction to Algorithms ". It quotes a lot of content and examples in the book, and gives python code reproduction based on the pseudocode in the book , explaining in detail the core logic and implementation process of the algorithm .

Dynamic programming (DP) thinking

The core idea of ​​the dynamic programming (Dynamic Programming) algorithm is: a processing algorithm that divides large problems into overlapping sub-problems to solve, thereby obtaining the optimal solution step by step.

Dynamic programming is similar to the divide-and-conquer method in that it solves the original problem by combining solutions to sub-problems ("programming" here refers to a table method, not writing a computer program). But the divide-and-conquer method divides the problem into disjoint sub-problems, solves the sub-problems recursively, and then combines their solutions to find the solution to the original problem. In contrast, dynamic programming is applied when sub-problems overlap , that is, different sub-problems have common sub-problems (the solution of the sub-problems is performed recursively, dividing it into smaller sub-sub-problems).

In this case, the divide-and-conquer algorithm does a lot of unnecessary work by repeatedly solving common sub-subproblems. The dynamic programming algorithm only solves each sub-subproblem once and saves its solution in a table, thereby eliminating the need to recalculate each time a sub-subproblem is solved, avoiding this unnecessary calculation work.

Examples

Dynamic programming methods are often used to solve optimization problems. Two examples are given below to illustrate: the first is to cut the steel bars into short steel bars so that the total value is the highest; the second is to solve how to use the least scalar multiplication operation Complete a matrix chain multiplication operation

Steel bar cutting problem

Here is the background to the steel bar cutting problem:

Problem background description
For example, the figure below shows all possible cutting solutions for a 4-inch steel bar:
Cutting plan
the number above the steel bar is the price corresponding to each section of steel bar. Different cutting solutions correspond to different prices. We found that cutting a steel bar with a length of 4 inches into two steel bars with a length of 2 inches each will produce P 2 P_2P2+ P 2 P_2 P2The income of =5+5=10 is the optimal solution, which corresponds to plan C in the figure above.

Next, we gradually analyze the recursive formula of the optimal revenue of the steel bar cutting problem:
Insert image description here
a more general revenue formula, the maximum revenue after cutting a steel bar of length n r_nrnExpressed as follows:
Insert image description here
A simpler recursive method: where, pi p_i
Insert image description here
in (15.2)piRefers to the price corresponding to the steel bar with length i, which can be obtained directly from the table without cutting.

If solved with the traditional recursive method , once the size of n becomes slightly larger, the running time of the program will become quite long, because this method repeatedly calls itself recursively with the same parameter values, that is, it repeatedly solves the same sub-problem . Time increases exponentially . The figure below shows the calling process of the recursive tree when n=4:
Insert image description here
the running time of the dynamic programming method is polynomial order.
Insert image description here
There are two equivalent implementation methods of dynamic programming: The code implementation process of the bottom-up method
Insert image description here
is given
Insert image description here
: In the above pseudocode, the input is two variables, the price list array and the length n; the output is a steel bar of length n the optimal benefit obtained.

Calculate the maximum profit from steel bar cutting when the length is 9:

import numpy as np

def Bottom_Up_Cut_Rod(p, n):
    r = []
    r.insert(0, 0)
    for j in range(1, n + 1):
        q = float('-inf')
        for i in range(1, j + 1):
            q = max(q, p[i - 1] + r[j - i])
        r.insert(j, q)
    return r[n]

if __name__ == '__main__':
    # 出售长度为i(i=1,2,3,,,10)的钢条所对应的价格样表
    p = [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]
    n = 9
    result = Bottom_Up_Cut_Rod(p, n)
    print("长度为{}时,".format(n))
    print("对应的最优收益值为{}。".format(result))

Insert image description here
The picture below shows the corresponding optimal cutting plan and profit for lengths 1, 2, 3....
Insert image description here

Idea: In order to solve the original problem of size n, we first solve the sub-problem with exactly the same form but smaller size. That is, after the first cutting is completed, we regard the two sections of steel bars as two independent instances of the steel bar cutting problem. We form the optimal solution to the original problem by combining the optimal solutions of two related sub-problems and selecting the one with the greatest combined benefit among all possible two-section cutting solutions.

We say that the steel bar cutting problem satisfies the optimal substructure property: the optimal solution of the problem is composed of the optimal solutions of related sub-problems, and these sub-problems can be solved independently.

Matrix chain multiplication problem

The matrix chain multiplication problem is the problem of multiplying multiple matrices to find the fastest calculation order.

Insert image description here
Insert image description here

The way you add parentheses to the matrix chain will have a huge impact on the cost of the product operation. The following example illustrates:

Insert image description here
Therefore, if A is a matrix of pXq and B is a matrix of qXr, then the product C is a matrix of pXr. The time required to calculate C is determined by the number of scalar multiplications, i.e. pqr. Below we will express the computational cost in terms of the number of scalar multiplications .

Insert image description here

Traditional approach: Exhausting all possible bracketing solutions will not be an efficient algorithm because its running time will still increase exponentially .

The calculation cost formula of the matrix chain problem is defined below. Let m[i,j] represent the calculation matrix A i , . . . , j A_{i,...,j}Ai,...,jMinimum number of scalar multiplications required.

Insert image description hereInsert image description here
Insert image description here
Assuming that the optimal split point k is unknown, the recursive solution formula becomes:
Insert image description here
The following is an example to illustrate:
Insert image description here
The picture above is an example written by me to help understanding. Among them, A 1 A_1A1:2 × \times × 3, meansA 1 A_1A1The dimensions of are 2 rows and 3 columns. The P list stores the dimensions of four matrices, where the number of columns of the previous matrix is ​​equal to the number of rows of the latter matrix. Ask for A 1 A_1A1 A 2 A_2 A2 A 3 A_3 A3 A 4 A_4 A4The calculation cost of((A1A2)(A3A4)) . Among them,( A 1 A 2 ) (A_1A_2)(A1A2The calculation cost of ) corresponds to the matrix C in the above figure,( A 3 A 4 ) (A_3A_4)(A3A4) corresponds to the matrix D in the figure above. Finally, multiply C and D, and the final calculation cost (total number of times) is 48.

The process MATRIX-CHAIN-ORDER given below implements the bottom-up table method:
The meaning of each input variable is as follows:
Insert image description here
Insert image description here
The output variables are the optimal cost matrix m and the optimal split point matrix k.

The calculation process of the code is given below to help understand. Assume n=4, p = [ p 0 , p 1 , p 2 , p 3 , p 4 ] p = [p_0,p_1,p_2,p_3,p_4]p=[p0,p1,p2,p3,p4],求 A 1 A 2 A 3 A 4 A_1A_2A_3A_4 A1A2A3A4The optimal cost is to find m[1,4]. The calculation process is as follows:
Insert image description here
Insert image description here
Insert image description here

The left side is the optimal cost matrix , and the right side is the optimal split point matrix .

Taking the data in Figure 15-5 as an example, the python code is as follows:

import numpy as np

def MATRIX_CHAIN_ORDER(p):
    n = len(p) - 1
    # 定义计算代价矩阵m
    m = np.zeros((n, n))
    # 定义最优分割点矩阵s
    s = np.zeros((n, n))
    for l in range(2, n + 1):
        for i in range(1, n - l + 2):
            j = i + l - 1
            m[i - 1, j - 1] = float('inf')
            for k in range(i, j):
                q = m[i - 1, k - 1] + m[k, j - 1] + p[i - 1] * p[k] * p[j]
                if q < m[i - 1, j - 1]:
                    m[i - 1, j - 1] = q
                    s[i - 1, j - 1] = k
    print(m)  # 每个元素对应长度为j-i+1的矩阵所需要最小计算代价
    print(s)  # 对应长度为j-i+1的矩阵最优分割点
    return m, s

if __name__ == '__main__':
    p = [30, 35, 15, 5, 10, 20, 25]
    MATRIX_CHAIN_ORDER(p)

The running results are as follows:

Insert image description here
It can be seen that the results are exactly the same as the two matrices in the book. (It’s just that the matrix is ​​rotated along the main diagonal in the book)

Idea: Any solution to a non-trivial matrix chain multiplication problem instance requires chain partitioning, and any optimal solution is composed of optimal solutions to sub-problem instances. Therefore, in order to construct an optimal solution to an instance of the matrix chain multiplication problem, we can divide the problem into two subproblems ( A i A i + 1 ⋅ ⋅ ⋅ A k A_iA_{i+1}···A_kAiAi+1⋅⋅⋅AkSum A k + 1 ⋅ ⋅ ⋅ A j A_{k+1}···A_jAk+1⋅⋅⋅Aj) the optimal solution to the sub-problem instance, and then combine the optimal solutions to the sub-problems. We must ensure that when determining the dividing point, we have examined all possible dividing points, so as to ensure that the optimal solution will not be missed.

Conditions and scenarios that the application meets

Optimization problems solved by dynamic programming methods should have two elements: optimal substructure and subproblem overlap .

Insert image description here
Insert image description here

Dynamic programming algorithms can be used to solve optimization problems. In addition to the two examples given in this article, common scenarios include solving Fibonacci sequences, solving the longest common subsequence, 01 knapsack problem and optimal binary search trees, etc. . If you want to have an in-depth understanding of the principles and application scenarios of this algorithm, you can read " Introduction to Algorithms ". I have an electronic version. Brothers and sisters who need it can send me a private message to get it.

To sum up, problems that satisfy the overlapping nature of the optimal substructure and subproblems can be modeled using dynamic programming ideas. This method will open up memory, store previous calculation results, and call them directly the next time they encounter them without repeating the calculations. , thus greatly saving time, it is a classic algorithm that exchanges space for time. In most cases, projects tend to have stricter time requirements, which are much looser than memory usage.

Guess you like

Origin blog.csdn.net/golden_knife/article/details/132152321