动态规划:矩阵连乘问题

大家好,我是连人。本期我们分享矩阵连乘问题。

在矩阵乘法中,不同的加括号方式的计算顺序可能会有不同的计算次数。

例如A:10×100,B:100×5,C:5×50

以(AB)C计算时,需要10×100×5+10×5×50=7500

以A(BC)计算时,需要100×5×50+10×100×50=75000

显然第一种计算方式计算次数更少,矩阵连乘问题就是求出最佳匹配的顺序。

我们将矩阵的维数记录到数组p中。为了节省空间,且根据矩阵乘法的规则,我们将前矩阵的列数和后矩阵的行数合并,例如上例的A,B,C,记录到p中就变成了:

p:[10, 100, 5, 50]

p[0],p[1]即为A矩阵的维,p[1],p[2]为B矩阵的维。依此类推。

此外,我们再创建两个二维数组m和s,行号为i,列号为j,m[ i ][ j ]是指从 i 到 j 的最小乘次,s[ i ][ j ]记录这次乘法从哪里断开。

由于j一定要大于i,所以两个数组都使用右上方。本次算法的主要思想是由小区域直接被大区域查找并决定最后采用何处断开才能获取最小乘次。因此方向是由小区域到大区域,从做下到右上。

下面进入正题,我将课本上的例子掏出来,大家也好理解。

A1A2A3A4A5A6
30×3535×1515×55×1010×2020×25

数组p = [30, 35, 15, 5, 10, 20, 25]

下图是数组m和数组s的计算次序。是由底层到高层的。

在这里插入图片描述

为了保证每次是在同一条对角线上,我们引入r作为第一个循环参数,跑动在每一条对角线上的 i 理所应当的成为第二个循环参数。

下表是r, i, j的关系。

rij
112
123
134
415
426
516

按照规律大家应该知道外侧循环该怎么写了8.

接下来介绍m[ i ] [ j ]的计算方法:
若i = j,m[ i ][ j ] = 0;
在其他情况下,取k∈[i, j]循环,取
m[ i ][ k ] + m[ k + 1][ j ] + pi-1*pk*pj的最小值

这个式子的意思是,在当前区域内每个断点都断开一次,计算每种情况取最小值。同时在s[ i ][ j ]中记录在何处断开。

举个例子,要计算A2A3A4A5的最小乘次,即m[2][5]。

A2(A3A4A5) :m[2][2]+m[3][5]+p1p2p5 = 13000
(A2A3)(A4A5):m[2][3]+m[4][5]+p1p3p5 = 7125
(A2A3A4)A5 :m[2][4]+m[5][5]+p1p4p5 = 11375

得知最小值7125,记录到m[2][5]中,同时断点为3,s[2][5] = 3。

在这之中,k成为了第3个循环参数,所以该算法的时间复杂度为o(n3)
在这里插入图片描述
在这里插入图片描述
接下来是代码部分:

def matrix_chain(p, n, m, s):
    for r in range(1, n):             # r是作为标记当前对角线方向的线i和j之间的关系
        for i in range(1, n-r+1):     # i是行号,在每轮对角线中,i所能触及的最大的行号依次递减
            j = i + r                 # j是列号,由i和r共同确定
            m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j]     # m[i][j]初始值是自身×之后的结果
            s[i][j] = i                                # s[i][j]初始值是从自身断开
            for k in range(i, j):                                 # k在i和j之间循环,找到最小值记录
                t = m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]
                if t < m[i][j]:
                    m[i][j] = t
                    s[i][j] = k


def traceback(i, j, s):
    if i == j:
        print(i, end='')
        return
    print('(', end='')
    traceback(i, s[i][j], s)
    traceback(s[i][j]+1, j, s)
    print(')', end='')


if __name__ == '__main__':
    p = [30, 35, 15, 5, 10, 20, 25]
    m = []
    s = []
    for i in range(0, 7):
        m.append([])
        s.append([])
        for j in range(0, 7):
            m[i].append(0)
            s[i].append(0)
    matrix_chain(p, 6, m, s)
    traceback(1, 6, s)


运行结果:
在这里插入图片描述

转载注明出处。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值