zoukankan      html  css  js  c++  java
  • 动态规划之多矩阵乘积

    算法原理请参考《算法导论》,因为算法这东西千篇一律,关键还是实现和理解,这里只提几个关键点,帮助大家理解。

    1. 为什么需要动态规划?

    比如矩阵A是p x q大小,矩阵B是q x r大小,很明显,得到的矩阵C是p x r大小,其中花费的时间必定是p*q*r。这只是两个矩阵,如果存在N个矩阵需要算其乘积呢?那么就需要用到动态规划了,比如A(p x q), B(q x r), C(r x l)这三个矩阵相乘。如果不规划,那么花费的时间是A*B=p*q*r,然后再乘以C,还需要额外花费p*r*l时间。但有可能B*C先乘,然后再乘以A,这样花费的时间最少。以此推广到N个矩阵相乘。这就是动态规划的原因!!!

    2. 为了帮助大家理解《算法导论》中的算法,这里我给大家讲解下数组p。比如A(a x b), B(b x c), C(c x d)这三个矩阵,那么p数组的内容是p[]={a,b,c,d},书上因为没有采用C语言数组,所以下标从1开始,但是这里我给出下标从0开始的数组p,这样矩阵A可以用p[0] x p[1] 表示,那么推广到N个矩阵,矩阵A:i的维度是p[i] x p[i+1] (所以,书上给出的伪代码需要做一些下标调整,包括算法)

    下面是代码部分

    matric_free函数,内存回收

    template <int p>
    void matric_free(int **C) {
        for (int i = 0; i < p; i++)
            delete[] C[i];
        delete[] C;
    }

    matric_show函数,打印矩阵,默认值为-1时,表示未使用,则不打印。

    template <int p, int q>
    void matric_show(int **A) {
        for (int i = 0; i < p; i++) {
            for (int j = 0; j < q; j++)
                if (A[i][j] == -1)
                    printf("%-04s", " ");
                else
                    printf("%-04d ", A[i][j]);
            printf("
    ");
        }
    }

    matric_multiply函数,这个是任意两个矩阵相乘,从代码也可以推出来,任意两个矩阵相乘花费的时间是p*q*r

    template <int p, int q, int r>
    int **matric_multiply(int A[p][q], int B[q][r]) {
        int **C = new int *[p];
        for (int i = 0; i < r; i++)
            C[i] = new int[r];
        for (int i = 0; i<p; i++)
            for (int j = 0; j < r; j++) {
                C[i][j] = 0;
                for (int k = 0; k < q; k++)
                    C[i][j] += A[i][k] * B[k][j];
            }
        return C;
    }

    matric_optimal函数,通过传入数组p(构建原理,已经在上面的关键点中提到了),length参数代表数组p长度,实际上,数组p会多一个元素出来,所以真实构建的m和s是length-1,由于下标通通改用了C语言形式,所以代码我做了调整

    template <int length>
    void matric_optimal(int *p, int ***m, int ***s) {
        int n = length - 1, q;
        if (n < 2) return;//至少两个矩阵
        *m = new int *[n];
        *s = new int *[n];
        for (int i = 0; i < n; i++) {//生成m, s对应的二维数组
            (*m)[i] = new int[n];
            (*s)[i] = new int[n];
        }
        //初始化
        for (int i=0;i<n;i++)
            for (int j = 0; j < n; j++) 
                (*m)[i][j] = (*s)[i][j] = -1;//-1表示未使用
        for (int i = 0; i < n; i++)
            (*m)[i][i] = 0;
        for (int l = 1; l <= n; l++) //这里针对C语言数组,做了改变
            for (int i = 0; i < n - l + 1; i++)
            {
                int j = i + l - 1;
                for (int k = i; k <j; k++) {
                    q = (*m)[i][k] + (*m)[k + 1][j] + p[i] * p[k+1] * p[j+1];//这里针对C语言数组做了改变,原因很简单,因为数组下标不能取-1,所以需要修改
                    if ((*m)[i][j]==-1 || q < (*m)[i][j]) {
                        (*m)[i][j] = q;
                        (*s)[i][j] = k;//保留k值,方便输出
                    }
                }
            }
    }

    另一个版本是递归版本,是recursive_matric_optimal函数。

    int recursive_matric_optimal(int *p, int ***m, int ***s, int i, int j) {
        int q, n;
        if (i == j) return 0;
        if (*m == NULL && *s == NULL) {
            n = j - i + 1;
            if (n < 2) return -1;//至少两个矩阵
            *m = new int *[n];
            *s = new int *[n];
            for (int i = 0; i < n; i++) {//生成m, s对应的二维数组
                (*m)[i] = new int[n];
                (*s)[i] = new int[n];
            }
            //初始化
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    (*m)[i][j] = (*s)[i][j] = -1;
        }
        for (int k = i; k < j; k++) {
            q = recursive_matric_optimal(p, m, s, i, k) + recursive_matric_optimal(p, m, s, k + 1, j) + p[i] * p[k + 1] * p[j + 1];
            if ((*m)[i][j] == -1 || q < (*m)[i][j]) {
                (*m)[i][j] = q;
                (*s)[i][j] = k;
            }
        }
        return (*m)[i][j];
    }

    matric_show_optimal函数,生成动态规划后的结果,直观看得出哪个矩阵先乘,时间最短。

    void matric_show_optimal(int **s, int i, int j) {
        if (i == j)
            printf("A:%d", i);
        else {
            printf("(");
            matric_show_optimal(s, i, s[i][j]);
            matric_show_optimal(s, s[i][j] + 1, j);
            printf(")");
        }
    }

    数据录入

    A0 30x35 
    A1 35x15
    A2 15x5 
    A3 5x10 
    A4 10x20
    A5 20x25

    转换为数组p

    int p[] = { 30,35,15,5,10,20,25 };//A(i)的维数表示p(i) x p(i+1)

    结果图

    这个结果对应书上的结果图(书上旋转了),是一样的

    所有代码均经过测试,结果正确!!!

  • 相关阅读:
    UE4 ios环境搭建备忘
    我所理解的打击感 逐帧分析过几十款游戏的开发者经验分享
    可重入锁和不可重入锁
    Java中JDK和JRE的区别是什么?它们的作用分别是什么?
    Java开发岗位面试题
    详解手把手Maven搭建SpringMVC+Spring+MyBatis框架(超级详细版)
    docker:轻量级图形页面管理工具Portainer
    Docker整合dockerfly实现UI界面管理(单机版)
    docker for mac的JSON配置文件中的hosts项修改后无法生效
    Maven [ERROR] 不再支持源选项 5。请使用 6 或更高版本
  • 原文地址:https://www.cnblogs.com/dalgleish/p/9100149.html
Copyright © 2011-2022 走看看