Matrix chain product - dynamic programming

Ordinary Matrix Multiplication Algorithm

//普通矩阵相乘
#define ROW_A 2
#define COL_A 2
#define COL_B 3
void matrix_mul(int matA[ROW_A][COL_A], int matB[2][COL_B], int C[ROW_A][COL_B])
{

    for (int i = 0; i < ROW_A; i++) {
        for (int j = 0; j < COL_B; j++) {
            //A的行必须等于B的列
            for (int k = 0; k < COL_A; k++) {
                C[i][j] += (matA[i][k] * matB[k][j]);
            }
        }
    }
}

Multiplication of two compatible matrices means that the rows of matrix A must be equal to the columns of matrix B.

Calculated using the definition of matrix multiplication, Cij = ∑ Aik * Bkj (k = 1...A.col).

It should be noted that after multiplying two matrices of size Aik and Bhj, the size of the matrix will become Cij, and the cost of multiplying the two matrices is i * j * k.

All subsequent algorithms use the size and cost of the matrix.

Recursive Matrix Chain Multiplication Algorithm

The convention Ai...j represents a matrix chain in which the matrix Ai is multiplied up to Aj

First find the optimal substructure.

  1. Make a choice, choose n matrices to divide at the kth position
  2. Suppose I already know that the cost is optimal after partitioning at the kth position
  3. Given a choice that yields an optimal solution, determine which subproblems the choice will produce, (subproblems that produce the chain product of two matrices Ai...k and Ak+1.....j), and how to characterize the subproblem solution space
  4. The cut-and-paste technique proves that the optimal solution of the original problem contains the optimal solution of the sub-problems. Because if the solution of the sub-problem is not the optimal solution, then substituting the optimal solution of the sub-problem into the original problem can get a better solution of the original problem, so it is proved that the solution of the sub-problem must also be optimal.
  5. The form of the solution space of the two sub-problems generated by the original problem is not the same, such as A1…k, Ak+1….j, the latter’s form is different from that of the original problem A1….j, and the sub-problem i must be allowed, j is variable, so use m[i][j] to record the solution space.

The embodiment of optimal substructure

How many subproblems does the optimal solution to the original problem involve?

How many options need to be considered in determining which subproblems to use for the optimal solution?

The answer is: the optimal solution uses two sub-problems, and it is necessary to examine several options.

The two sub-problems are the respective optimal solutions of Ai...k, and Ak+1....j, and also consider the optimal value of k between i and j.

Define the optimal solution of the original problem recursively with the solutions of the subproblems

m[i][j] represents the minimum cost of multiplying Ai....j matrices, and the optimal solution is m[1][n]—that is, the cost of multiplying from the first matrix to the last matrix.

  • Definition of recursion m[i][j]

When i = j, it is a matrix multiplied by itself, and the cost is 0

When i < j, divide at k and examine all choices of k, m[i][j] = min(m[i][k] + m[k+1][j] + pi-1 * pk * pj) (k = i....j-1)

  • In this question, because the matrix cost given is a p array and is regular, the cost of multiplying the two matrices before and after can be expressed by p.

The above is the most important part of solving dynamic regression, basically you can directly write the recursive method↓

//矩阵规模,例如A1=30*35,A2=35*15
int p[] = { 30,35,15,5,10,20,25 };
#define N  6        //矩阵长度
int m[N+1][N+1] = { 0 };
int s[N + 1][N + 1] = {0};
//直接递归法,i=1,j=6
int recursive(int p[], int i, int j)
{
    if (i == j) {
        return 0;
    }
    m[i][j] = _CRT_INT_MAX;
    for (int k = i; k < j; k++) {
        int q = recursive(p, i, k) + recursive(p, k + 1, j) + p[i - 1] * p[k] * p[j];
        if (q < m[i][j]) {
            m[i][j] = q;
        }
    }
    return m[i][j];
}

Recursive Algorithm with Memo

When the recursive call encounters the subproblem for the first time, find its solution and record it. In the future, you will encounter a direct look-up table return.

//带备忘的递归
void memorize(int m[N + 1][N + 1]) {
    for (int i = 0; i < N + 1; i++) {
        for (int j = 0; j < N + 1; j++) {
            m[i][j] = _CRT_INT_MAX;
        }
    }
}
int lookup(int p[], int i, int j)
{
    //查表
    if (m[i][j] < _CRT_INT_MAX) {
        return m[i][j];
    }
    if (i == j) {
        return 0;
    }
    for (int k = i; k < j; k++) {
        int q = recursive(p, i, k) + recursive(p, k + 1, j) + p[i - 1] * p[k] * p[j];
        if (q < m[i][j]) {
            m[i][j] = q;
        }
    }

    return m[i][j];
}

Self-improvement

In order to implement the bottom-up approach, it must be determined which entries are accessed when computing m[i][j]. From the above recursive definition, one calculation of m[i][j] needs to find the optimal solution of all k selected sub-problems, and k takes a value in i...j, which means that the sub-problems need to be accessed. The matrix size of the problem must be smaller than the original problem. Therefore, the design solution order is from short to long according to the length of the matrix chain.

Moreover, it is necessary to find all sub-problem solutions from the bottom up, and the legal solution space of m[i][j] will be filled, so the loop considers each length, the matrix chain of each start and end point, and finally returns the optimal cost is m[1][N].

//自底向上
int matrix_order(int p[])
{
    //数组下标一律和矩阵序号对齐
    for (int k = 1; k <= N; k++) {
        m[k][k] = 0;
    }
    //设计求解顺序,按长度
    for (int len = 2; len <= N; len++) {
        //i开始矩阵下标,j和i间隔len个矩阵
        for (int i = 1; i <= N - len+1; i++) {
            int j = i + len - 1;
            m[i][j] = _CRT_INT_MAX;
            for (int k = i; k < j; k++) {
                int q = m[i][k] + m[k+1][j] + p[i - 1] * p[k] * p[j];
                if (q < m[i][j]) {
                    m[i][j] = q;
                    s[i][j] = k;
                }
            }
        }

    }
    return m[1][N];
}
//构造最优解
void print(int s[N+1][N+1], int i, int j)
{
    if (i == j) {
        printf("A%d", i);
    }
    else {
        printf("(");
        print(s, i, s[i][j]);
        print(s, s[i][j]+1, j);
        printf(")");
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325904829&siteId=291194637