算法导论4-2

读书笔记

本章主要讲述了用于矩阵乘法的Strassen算法;

首先使用暴力算法给出了矩阵乘法的一个解,由于使用了三重循环,所以时间复杂度为\(\theta(n^3)\)

然后使用分治法给出一个解,但是时间复杂度并没有优于暴力算法,仍然是\(\theta(n^3)\);

最后引出了Strassen算法,能在\(\theta(n^{\lg{7}})\)的时间复杂度内,完成对矩阵的乘法;

在本章中,如何使用Strassen算法讲的很清楚,但是为什么能够奏效讲的不够清楚。

  1. 将输入矩阵\(A, B\)和输出矩阵\(C\)分解为\(\frac{n}{2}\ast\frac{n}{2}\)的子矩阵。采用下标计算方式;

数组图解

  1. 创建10个\(\frac{n}{2}\ast\frac{n}{2}\)的矩阵\(S_1\)..\(S_{10}\),并且按照下方规则进行赋值:

\[\begin{matrix} & S_1 = B_{12} - B_{22} & S_2 = A_{11} + A_{12} \\[2ex] & S_3 = A_{21} + A_{22} & S_4 = B_{21} - B_{11} \\[2ex] & S_5 = A_{11} + A_{22} & S_6 = B_{11} + B_{22} \\[2ex] & S_7 = A_{12} - A_{22} & S_8 = B_{21} + B_{22} \\[2ex] & S_9 = A_{11} - A_{21} & S_{10} = B_{11} + B_{12} \end{matrix} \]

  1. 然后根据上述所创建的10个矩阵,进行7次递归计算,形成7个新的矩阵

\[\begin{aligned} & P_1 = A_{11} \ast S_1 \\[2ex] & P_2 = S_2 \ast B_{22} \\[2ex] & P_3 = S_3 \ast B_{11} \\[2ex] & P_4 = A_{22} \ast S_4 \\[2ex] & P_5 = S_5 \ast S_6 \\[2ex] & P_6 = S_7 \ast S_8 \\[2ex] & P_7 = S_9 \ast S_{10} \end{aligned} \]

  1. 最后根据上述生成的7个矩阵,生成输出矩阵\(C\):

\[\begin{aligned} & C_{11} = P_5 + P_4 - P_2 + P_6 \\[2ex] & C_{12} = P_1 + P_2 \\[2ex] & C_{21} = P_3 + P_4 \\[2ex] & C_{22} = P_5 + P_1 - P_3 - P_7 \end{aligned} \]

在章节4-5中会详细讲解算法原理。

课后练习

4.2-1

使用Strassen算法计算如下矩阵乘法

\[\begin{bmatrix} 1 & 3 \\ 7 & 5 \end{bmatrix} \begin{bmatrix} 6 & 8 \\ 4 & 2 \end{bmatrix} \]

首先使用常规算法算出结果以验证Strassen算法的正确性;

\[\begin{aligned} \begin{bmatrix}1 & 3 \\7 & 5\end{bmatrix} \begin{bmatrix}6 & 8 \\4 & 2\end{bmatrix} &= \begin{bmatrix} 1\ast{6} + 3 \ast{4} & 1\ast{8}+3\ast{2}\\ 7\ast{6} + 5\ast{4} & 7\ast{8} + 5\ast{2} \end{bmatrix}\\[2ex] &= \begin{bmatrix} 18 & 14\\ 62 & 66 \end{bmatrix} \end{aligned} \]

使用Strassen算法的过程:

  1. 分割输入矩阵\(A, B\)

\[\begin{matrix} A_{11} = [1] \quad& A_{12} = [3]\\ A_{21} = [7] \quad& A_{22} = [5]\\ \\ B_{11} = [6] \quad& B_{12} = [8] \\ B_{21} = [4] \quad& B_{22} = [2] \end{matrix} \]

  1. 生成10个中间矩阵

\[\begin{matrix} & S_1 = B_{12} - B_{22}=[6] & S_2 = A_{11} + A_{12}=[4] \\[2ex] & S_3 = A_{21} + A_{22}=[12] & S_4 = B_{21} - B_{11}=[-2] \\[2ex] & S_5 = A_{11} + A_{22}=[6] & S_6 = B_{11} + B_{22}=[8] \\[2ex] & S_7 = A_{12} - A_{22}=[-2] & S_8 = B_{21} + B_{22}=[6] \\[2ex] & S_9 = A_{11} - A_{21}=[-6] & S_{10} = B_{11} + B_{12}=[14] \end{matrix} \]

  1. 进行7次递归运算,生成7个性的矩阵

\[\begin{aligned} & P_1 = A_{11} \ast S_1 &= [6]\\[2ex] & P_2 = S_2 \ast B_{22} &= [8]\\[2ex] & P_3 = S_3 \ast B_{11} &= [72]\\[2ex] & P_4 = A_{22} \ast S_4 &= [-10]\\[2ex] & P_5 = S_5 \ast S_6 &= [48] \\[2ex] & P_6 = S_7 \ast S_8 &= [-12] \\[2ex] & P_7 = S_9 \ast S_{10} &= [-84] \end{aligned} \]

  1. 生成输出矩阵\(C\)

\[\begin{aligned} C_{11} &= P_5 + P_4 - P_2 + P_6 \\[2ex] &= [48]+[-10]-[8]+[-12] = [18]\\[2ex] C_{12} &= P_1 + P_2 \\[2ex] &= [14] \\[2ex] C_{21} &= P_3 + P_4 \\[2ex] &=[62] \\[2ex] C_{22} &= P_5 + P_1 - P_3 - P_7 \\[2ex] &=[48] + [6] - [72] - [-84] = [66] \end{aligned} \]

所以结果矩阵为

\[\begin{bmatrix} 18 & 14\\ 62 & 66 \end{bmatrix} \]

与常规解法结果相同,所以结果正确;

为Strassen算法编写伪代码

这里我直接引用了大峰子的伪代码

square-Matrix-Strassen (A, B)
    n = A.row     
    let C be a new n*n matrix
    if n == 1 //base case
        C11 = A11 • B11
    else //recursive case
        partition A, B, and C as in equations 4.9
        let S1, S2, S3, S4, S5, S6, S7, S8, S9, S10 be new n/2 * n/2 matrixs
        S1 = B12 - B22
        S2 =  A11 + A12
        S3 = A21 + A22
        S4 = B21 - B11
        S5 = A11 + A22
        S6 = B11 + B22
        S7 = A12 - A22
        S8 = B21 + B22
        S9 = A11 - A21
        S10 = B11 + B12
        P1 = square-Matrix-Strassen (A11, S1)
        P2 =  square-Matrix-Strassen (S2, B22)
        P3 =  square-Matrix-Strassen (S3, B11)
        P4 =  square-Matrix-Strassen (A22, S4)
        P5 =  square-Matrix-Strassen (S5, S6)
        P6 =  square-Matrix-Strassen (S7, S8)
        P7 =  square-Matrix-Strassen (S9, S10)
    C11 = P5 + P4 - P2 + P6
    C12 = P1 + P2
    C21 = P3 + P4
    C22 = P5 + P1 - P3 - P7
    return C

4.2-2

如何修改Strassen算法,使之适应矩阵规模不是2的幂的情况?并且证明算法的运行时间为\(\theta{n^{lg{7}}}\)

对于第一小问,添加额外的行或列使之成为2的幂的方阵,添加的行或列均为0即可。

对于第二小问,//todo

4.2-3

如果可以用\(k\)次乘法操作(假定乘法交换律不成立)完成3*3矩阵相乘,那么你可以在\(o(n^{\lg{7}})\)时间内完成\(n\ast {n}\)矩阵相乘,满足这一条件的最大的\(k\)是多少? 此算法的运行时间是怎样的?

\(3\ast{3}\)并不满足Strassen算法的条件,所以先将其补全为\(4\ast{4}\),因此\(k=4^{\log_2^7}=49\)

运行时间应该大于49,因为还有许多的加法;

4.2-4

V.Pan 发现一种方法,可以用132464次乘法操作完成\(68\ast{68}\)的矩阵相乘,发现另一种方法,可以用143640次乘法操作完成\(70\ast{70}\)的矩阵相乘,还发现一种方法,可以用155424次乘法操作完成\(72\ast{72}\)的矩阵相乘。但用于矩阵现成的分治算法时,上述哪种方法会得到最佳的渐近运行时间?与Strassen算法相比,性能如何?

\(72\ast{72}\)的方法会得到最佳的渐进运行时间,因为72最接近128;

通过计算,Strassen算法对应的次数为823543,823543,823543;所以V.Pan的方法会更优;

4.2-5

用Strassen算法作为子进程来进行一个\(kn\ast{n}\)矩阵和一个\(n\ast{kn}\)矩阵相乘,最快需要花费多长时间?对两个输入矩阵规模互换的情况,回答相同的问题。

先将两个举证补全为\(kn\ast{kn}\),然后再补全为二次幂;规模互换,解法不变;花费时间其实说不好,在于\(k\)作为系数到底有多大,还有\(kn\)距离右侧的二次幂有多远。

4.2-6

设计算法,仅使用三次实数算法即可完成复数\(a+bi\)\(c+di\)相乘。算法需接收\(a,b,c,d\)作为输入,分别生成实部\(ac-bd\)和虚部\(ad+bc\)

算法如下

虚数乘法

posted @ 2021-01-14 18:23  ijkzen  阅读(382)  评论(0编辑  收藏  举报