一、问题定义

1.1 矩阵乘法

对于矩阵 U p × q U_{p\times q} Up×q V q × r V_{q\times r} Vq×r Z p × r = U V Z_{p\times r}=UV Zp×r=UV,共需计算 p q r pqr pqr次标量乘法,时间复杂度为 Θ ( p q r ) \Theta (pqr) Θ(pqr)

矩阵乘法满足结合律,即 ( U V ) W = U ( V W ) (UV)W=U(VW) (UV)W=U(VW),选择不同的结合顺序,会对计算的次数产生巨大的影响。


1.2 矩阵链乘法问题

n n n个矩阵相乘称为矩阵链乘法,问题在于如何确定相乘顺序,以提高计算效率?

形式化定义如下:

输入:

  • n n n个矩阵组成的矩阵链 U 1.. n = < U 1 , U 2 , . . . , U n > U_{1..n}=<U_1,U_2,...,U_n> U1..n=<U1,U2,...,Un>
  • 矩阵链 U 1.. n U_{1..n} U1..n对应的维度数分别为 p 0 , p 1 , . . . , p n p_0,p_1,...,p_n p0,p1,...,pn U i U_i Ui的维度为 p i − 1 × p i p_{i-1}\times p_i pi1×pi

输出:

  • 找到一种添加括号的方式,以确定矩阵链乘法的计算顺序,使得最小化矩阵链标量乘法的次数


二、求解策略

显然,最优矩阵链乘法加括号的组合中,任意一部分均为最优,具有最优子结构性质,尝试动态规划求解。


2.1 分析问题结构

形式化表示问题:

  • D [ i , j ] D[i,j] D[i,j]:计算矩阵链 U i . . j U_{i..j} Ui..j所需标量乘法的最小次数

原问题:

  • D [ i , n ] D[i,n] D[i,n]:计算矩阵链 U 1.. n U_{1..n} U1..n所需标量乘法的最小次数

2.2 建立递推关系

以长度为4的矩阵链为例辅助思考, U = U 1 U 2 U 3 U 4 U=U_1U_2U_3U_4 U=U1U2U3U4

对其进行加1个括号的操作,有以下几种方式:
U = ( U 1 ) ( U 2 U 3 U 4 ) ⇒ D [ 1 , 4 ] = D [ 1 , 1 ] + D [ 2 , 4 ] + p 0 p 1 p 4 U = ( U 1 U 2 ) ( U 3 U 4 ) ⇒ D [ 1 , 4 ] = D [ 1 , 2 ] + D [ 3 , 4 ] + p 0 p 2 p 4 U = ( U 1 U 2 U 3 ) ( U 4 ) ⇒ D [ 1 , 4 ] = D [ 1 , 3 ] + D [ 4 , 4 ] + p 0 p 3 p 4 U=(U_1)(U_2U_3U_4)\Rightarrow D[1,4]=D[1,1]+D[2,4]+p_0p_1p_4\\ U=(U_1U_2)(U_3U_4)\Rightarrow D[1,4]=D[1,2]+D[3,4]+p_0p_2p_4\\ U=(U_1U_2U_3)(U_4)\Rightarrow D[1,4]=D[1,3]+D[4,4]+p_0p_3p_4 U=(U1)(U2U3U4)D[1,4]=D[1,1]+D[2,4]+p0p1p4U=(U1U2)(U3U4)D[1,4]=D[1,2]+D[3,4]+p0p2p4U=(U1U2U3)(U4)D[1,4]=D[1,3]+D[4,4]+p0p3p4
D [ 1 , 4 ] D[1,4] D[1,4]结果只需要在这3种方式中取得最大值即可。

我们不妨来看一下,求得 D [ 1 , 4 ] D[1,4] D[1,4]需要哪些数据:

1234
1目标
2
3
4

再次观察,对角线上的元素表示矩阵自身,不需要进行运算,所以值均为0。而左下三角的矩阵并没有实际含义,因为 j ≥ i j\ge i ji,故当前表格更新为下表所示:

1234
10目标
2NULL0
3NULLNULL0
4NULLNULLNULL0

相信大家已经看出递推关系。

对于矩阵链 U i . . j U_{i..j} Ui..j,求解 D [ i , j ] D[i,j] D[i,j]时,为了不遗漏最优分割位置,需要枚举所有可能位置 i , i + 1 , . . , j − 1 i,i+1,..,j-1 i,i+1,..,j1,一共 j − i j-i ji种。

假设在 k k k处进行分割,分为 U i . . k U_{i..k} Ui..k U k + 1.. j U_{k+1..j} Uk+1..j两部分,前者的维度为 p i − 1 × p k p_{i-1} \times p_k pi1×pk,后者的维度为 p k × p j p_k\times p_j pk×pj,将两部分进行乘积时,需要额外增加 p i − 1 × p k × p j p_{i-1}\times p_k \times p_j pi1×pk×pj的计算次数。

因此, D [ i , j ] = min ⁡ i ≤ k < j { D [ i , k ] + D [ k + 1 , j ] + p i − 1 p k p j } D[i,j]=\min_{i\leq k <j} \left \{ D[i,k]+D[k+1,j]+p_{i-1}p_kp_j \right \} D[i,j]=minik<j{D[i,k]+D[k+1,j]+pi1pkpj}


2.3 自底向上计算

采用图中的方向进行计算:

在这里插入图片描述

许多教材中习惯将其逆时针旋转45度,以方便观察,在于个人习惯。

在这里插入图片描述


2.4 追踪最优方案

构造追踪数组 R e c [ 1.. n , 1.. n ] Rec[1..n,1..n] Rec[1..n,1..n] R e c [ i , j ] Rec[i,j] Rec[i,j]表示矩阵链 U i . . j U_{i..j} Ui..j的最优分割位置(一次分割)。

在计算 D [ i , j ] D[i,j] D[i,j]的过程中,选出最小的 k k k,记录 R e c [ i , j ] = k Rec[i,j]=k Rec[i,j]=k

追踪时,从 R e c [ 1 , n ] Rec[1,n] Rec[1,n]出发,假设 R e c [ 1 , n ] = k Rec[1,n]=k Rec[1,n]=k,则说明在 U k U_k Uk处进行了分割,分为矩阵链 D [ 1 , k ] D[1,k] D[1,k] D [ k + 1 , n ] D[k+1,n] D[k+1,n],再分别查看 R e c [ 1 , k ] Rec[1,k] Rec[1,k] R e c [ k + 1 , n ] Rec[k+1,n] Rec[k+1,n],便找到两部分的分割地方。如此寻找直至对角线部分。

在这里插入图片描述

在这里插入图片描述



三、算法分析

3.1 伪代码

计算及记录部分:

在这里插入图片描述

在这里插入图片描述

追踪方案部分:

在这里插入图片描述


3.2 时间复杂度

时间复杂度 O ( n 3 ) O(n^3) O(n3)

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐