算法導論 — 4.2 矩陣乘法的Strassen算法

筆記

給定兩個n×nn×n正方矩陣AABB,這兩個矩陣的乘法定義爲
      在這裏插入圖片描述
  其中
      在這裏插入圖片描述
  下面是矩陣乘法的僞代碼。
  在這裏插入圖片描述
  很顯然,執行SQUARE-MATRIX-MULTIPLY需要花費Θ(n3)Θ(n^3)時間。然而,有一種方法可以花費更少的時間,這就是Strassen算法,它本質上也是一種分治法,它的時間複雜度爲Θ(nlg7)=O(n2.81)Θ(n^{{\rm lg}7}) = O(n^{2.81})
  在介紹Strassen算法之前,我們先嚐試簡單的分治法來計算矩陣乘法C=ABC = A•B。假定三個矩陣均爲n×nn×n正方矩陣。並且爲簡化分析,假定nn22的冪。我們將ABA、BCC均分解爲44(n/2)×(n/2)(n/2)×(n/2)的子矩陣。
    在這裏插入圖片描述
  於是矩陣乘法可以表示爲
    在這裏插入圖片描述
  上面的矩陣乘法等價於下面44個式子。
      在這裏插入圖片描述
  上面每個式子都對應22(n/2)×(n/2)(n/2)×(n/2)矩陣乘法,以及11(n/2)×(n/2)(n/2)×(n/2)矩陣加法。根據以上分析,可以給出一個遞歸的分治算法。
  在這裏插入圖片描述
  現在分析這個簡單分治法的時間複雜度。調用SQUARE-MATRIX-MULTIPLY-RECURSIVE計算兩個n×nn×n矩陣乘法的運行時間用T(n)T(n)表示。對於n=1n = 1的初始情況,我們只需計算一次標量乘法,因此T(1)=Θ(1)T(1) = Θ(1)。當n>1n > 1時,根據上面的僞代碼,T(n)T(n)包含88(n/2)×(n/2)(n/2)×(n/2)矩陣乘法的時間和44(n/2)×(n/2)(n/2)×(n/2)矩陣加法的時間,所以T(n)=8T(n/2)+Θ(n2)T(n) = 8T(n/2) + Θ(n^2),這裏忽略了分解子矩陣的時間。於是,我們得到SQUARE-MATRIX-MULTIPLY-RECURSIVE的運行時間的遞時式爲
    在這裏插入圖片描述
  求解這個遞歸式得到T(n)=Θ(n3)T(n) = Θ(n^3)。可以看到,這個簡單的分治法並沒有帶來漸近運行時間的提升。
  下面介紹Strassen算法。Strassen算法同樣要將每個矩陣分解爲44(n/2)×(n/2)(n/2)×(n/2)子矩陣。而與簡單分治法不同,Strassen算法只需要遞歸爲77次,而不是88次。下面直接給出Strassen算法的流程。
  (1) 將輸入矩陣ABA、B以及輸出矩陣CC各分解爲44(n/2)×(n/2)(n/2)×(n/2)子矩陣。
  (2) 創建1010(n/2)×(n/2)(n/2)×(n/2)矩陣S1,S2,,S10S_1, S_2, …, S_{10},如下所示。由於需要進行1010(n/2)×(n/2)(n/2)×(n/2)矩陣的加減法,所以這一步花費Θ(n2)Θ(n^2)時間。
    在這裏插入圖片描述
  (3) 用步驟(1)分解得到的子矩陣和步驟(2)中創建的1010個矩陣,遞歸地計算77個矩陣乘積P1,P2,,P7P_1, P_2, …, P_7,如下所示。
    在這裏插入圖片描述
  (4) 利用矩陣P1,P2,,P7P_1, P_2, …, P_7進行加減運算,得到輸出矩陣CC的子矩陣C11,C12,C21,C22C_{11}, C_{12}, C_{21}, C_{22},如下所示。這一步需要進行88(n/2)×(n/2)(n/2)×(n/2)矩陣的加減法,所以花費時爲Θ(n2)Θ(n^2)
    在這裏插入圖片描述
  由於Strassen算法只需要遞歸爲77次,因此它的運行時間的遞歸式爲
    在這裏插入圖片描述
  求解這個遞歸式,可以得到Strassen算法的運行時間T(n)=Θ(nlg7)T(n) = Θ(n^{{\rm lg}7})

練習

4.2-1 使用Strassen算法計算如下矩陣乘法:
    在這裏插入圖片描述
  給出計算過程。
  
  (1) 分解輸入矩陣
    在這裏插入圖片描述
  (2) 計算矩陣S1,S2,,S10S_1, S_2, …, S_{10}
    在這裏插入圖片描述
  (3) 計算矩陣P1,P2,,P7P_1, P_2, …, P_7
    在這裏插入圖片描述
  (4) 計算輸出矩陣的44個子矩陣
    在這裏插入圖片描述
  最終結果爲
    在這裏插入圖片描述
  
4.2-2 爲Strassen算法編寫僞代碼。
  
  這裏還是假設了矩陣的寬高nn22的冪。下面給出僞代碼。
  在這裏插入圖片描述
  
4.2-3 如何修改Strassen算法,使之適應矩陣規模nn不是22的冪的情況?證明:算法的運行時間爲Θ(nlg7)Θ(n_{{\rm lg}7})
  
  爲了保證算法的通用性,需要考慮矩陣的寬高nn不爲22的冪的情況。分兩種情況討論。
  (1) nn爲偶數
  這種情況下n×nn×n矩陣可以直接分解爲44(n/2)×(n/2)(n/2)×(n/2)的子矩陣,因此可以直接應用Strassen算法。爲了計算矩陣乘法Cn×n=An×nBn×nC_{n×n} = A_{n×n}•B_{n×n},令m=n/2m = n/2,需要將矩陣分解爲
    在這裏插入圖片描述
  這種情況下,矩陣乘法所花費的時間T(n)=7T(n/2)+Θ(n2)T(n) = 7T(n/2) + Θ(n^2)
  (2) nn爲奇數
  這種情況不能直接應用Strassen算法。爲了計算矩陣乘法Cn×n=An×nBn×nC_{n×n} = A_{n×n}•B_{n×n},令m=n1m = n−1,將矩陣做如下分解
    在這裏插入圖片描述
  如上所示,每個n×nn×n矩陣被分解爲一個(n1)×(n1)(n−1)×(n−1)矩陣、一個(n1)×1(n−1)×1矩陣、一個1×(n1)1×(n−1)矩陣和一個1×11×1矩陣。相應地,矩陣乘法Cn×n=An×nBn×nC_{n×n} = A_{n×n}•B_{n×n}可以分解爲下面44個式子。
    在這裏插入圖片描述
  上面4個式子包含了8個不同規模的矩陣乘法,下面逐個進行分析。
  1) A11m×mB11m×mA11_{m×m}•B11_{m×m}:由於m=n1m = n−1是偶數,所以這個矩陣乘法可以直接應用Strassen算法。
  這一矩陣乘法所花費的時間爲T(n1)=7T((n1)/2)+Θ((n1)2)=7T(n/2)+Θ(n2)T(n-1)=7T((n-1)/2)+Θ((n-1)^2)=7T(⌊n/2⌋)+Θ(n^2)
  2) A12m×1B211×mA12_{m×1}•B21_{1×m}:採用樸素算法,需要做(n1)2(n−1)^2次乘法,因此運行時間爲Θ(n2)Θ(n^2)
  3) A11m×mB12m×1A11_{m×m}•B12_{m×1}:採用樸素算法,需要做(n1)2(n−1)^2次乘法和(n1)(n2)(n−1)(n−2)次加法,因此運行時間也爲Θ(n2)Θ(n^2)
  4) A12m×1B221×1A12_{m×1}•B22_{1×1}:採用樸素算法,需要做(n1)(n−1)次乘法,運行時間爲Θ(n)Θ(n)
  5) A211×mB11m×mA21_{1×m}•B11_{m×m}:採用樸素算法,需要做(n1)2(n−1)^2次乘法,以及(n1)(n2)(n−1)(n−2)次加法,運行時間爲Θ(n2)Θ(n^2)
  6) A221×1B211×mA22_{1×1}•B21_{1×m}:採用樸素算法,需要做(n1)(n−1)次乘法,運行時間爲Θ(n)Θ(n)
  7) A211×mB12m×1A21_{1×m}•B12_{m×1}:採用樸素算法,需要做(n1)(n−1)次乘法,以及(n2)(n−2)次加法,運行時間爲Θ(n)Θ(n)
  8) A221×1B221×1A22_{1×1}•B22_{1×1}:這僅僅是兩個元素的相乘,只花費Θ(1)Θ(1)時間。
  根據以上分析,除去A11m×mB11m×mA11_{m×m}•B11_{m×m}之外,其他77個矩陣乘法加起來的運行時間爲Θ(n2)Θ(n^2)。因此,當nn爲奇數時,n×nn×n矩陣乘法的運行時間爲
    T(n)=7T(n/2)+Θ(n2)T(n)=7T(⌊n/2⌋)+Θ(n^2)
  綜合以上兩種情況,無論nn爲奇數還是偶數,矩陣乘法的運行時間都爲T(n)=7T(n/2)+Θ(n2)T(n)=7T(⌊n/2⌋)+Θ(n^2)。忽略其中的⌊ ⌋符號,這與之前分析的Strassen算法的運行時間是一樣的。
  下面給出具備通用性的Strassen算法的僞代碼。
  在這裏插入圖片描述
  在這裏插入圖片描述
  
4.2-4 如果可以用kk次乘法操作(假定乘法的交換律不成立)完成兩個3×33×3矩陣相乘,那麼你可以在o(nlg7)o(n^{{\rm lg}7})時間內完成n×nn×n矩陣相乘,滿足這一條件的最大kk是多少?此算法的運行時間是怎樣的?
  
  仍然採用Strassen算法。我們現在分析該算法運行時間的遞歸式,不過在這裏需要以T(3)T(3)作爲邊界條件,遞歸式如下所示。
    在這裏插入圖片描述
  如果我們畫出遞歸樹,該遞歸樹一共有lg(n/3)lg(n/3)層。葉結點對應子問題T(3)T(3)。由於每層的結點數是上一層的77倍,因此第ii層包含7i7^i個結點。因此,葉結點一共有7lg(n/3)=7lgnlg3=7lgn/7lg3=nlg7/7lg37^{{\rm lg}(n/3)} =7^{{\rm lg}n-{\rm lg}3}=7^{{\rm lg}n}/7^{{\rm lg}3} =n^{{\rm lg}7}/7^{{\rm lg}3}個。因此所有葉結點的代價之和爲(nlg7/7lg3)T(3)=k(nlg7/7lg3)(n^{{\rm lg}7}/7^{{\rm lg}3})•T(3)=k•(n^{{\rm lg}7}/7^{{\rm lg}3})
  如果要在o(nlg7)o(n_{{\rm lg}7})時間內完成n×nn×n矩陣相乘,那麼必然有k(nlg7/7lg3)<nlg7k•(n^{{\rm lg}7}/7^{{\rm lg}3})<n^{{\rm lg}7},於是得到k<7lg321.85k<7^{{\rm lg}3}≈21.85。所以kk的最大值爲2121
  
4.2-5 V.Pan發現一種方法,可以用132464132 464次乘法操作完成68×6868×68的矩陣相乘,發現另一種方法,可以用143640143 640次乘法操作完成70×7070×70的矩陣相乘,還發現一種方法,可以用155424155 424次乘法操作完成72×7272×72的矩陣相乘。當用於矩陣相乘的分治算法時,上述哪種方法會得到最佳的漸近運行時間?與Strassen算法相比,性能如何?
  
  對於採用分治法的矩陣乘法算法來說,其運行時間都爲Θ(nd)Θ(n^d),其中dd爲一個正常數。現在分析題目所給的33種方法,其漸近運行時間中的dd分別爲多少。爲方便起見,假設33種方法的運行時間分別爲T1(n)=nd1T2(n)=nd2T_1(n)=n^{d_1},T_2(n)=n^{d_2}T3(n)=nd3T_3(n)=n^{d_3}
  用132464132 464次乘法操作完成68×6868×68的矩陣相乘,於是有
    T1(68)=68d1=132464T_1 (68)=68^{d_1}=132464
  得到d1=log681324642.795128d_1={\rm log}_{68}132464≈2.795128
  用143640143 640次乘法操作完成70×7070×70的矩陣相乘,於是有
    T2(70)=70d2=143640T_2 (70)=70^{d_2}=143640
  得到d2=log701436402.795122d_2={\rm log}_{70}143640≈2.795122
  用155424155 424次乘法操作完成72×7272×72的矩陣相乘,於是有
    T3(72)=72d3=155424T_3 (72)=72^{d_3}=155424
  得到d3=log721554242.795147d_3={\rm log}_{72}155424≈2.795147
  根據以上分析,第(2)種方法的漸近運行時間的指數d2d_2是最小的,所以第(2)種方法會得到最佳的漸近運行時間。
  Strassen算法的漸近運行時間爲Θ(nlg7)Θ(nlg7)lg72.807355>d2{\rm lg}7 ≈ 2.807355 > d_2,因此上述第(2)種方法的性能是優於Strassen算法的。
  
4.2-6 用Strassen算法作爲子過程來進行一個kn×nkn×n矩陣和一個n×knn×kn矩陣相乘,最快需要花費多長時間?對兩個輸入矩陣規模互換的情況,回答相同的問題。
  
  兩個矩陣Akn×nA_{kn×n}Bn×knB_{n×kn}相乘,得到矩陣Ckn×knC_{kn×kn}。如果要利用Strassen算法,則需要將矩陣ABA、BCC按下面的方式分解
    在這裏插入圖片描述
  矩陣CC的任意一個子矩陣Cij=AiBjC_{ij} = A_i • B_j, 這是一個n×nn×n矩陣乘法,採用Strassen算法,運行時間爲Θ(nlg7)Θ(n^{{\rm lg}7})。一共有k2k^2個這樣的n×nn×n矩陣乘法,所以總的運行時間爲Θ(k2nlg7)Θ(k^2•n^{{\rm lg}7})
  如果將輸入矩陣的規模互換,即矩陣An×knA_{n×kn}Bkn×nB_{kn×n}相乘,得到矩陣Cn×nCn×n,那麼需要將矩陣AABB按下面的方式分解
    在這裏插入圖片描述
  矩陣C=A1B1+A2B2++AkBkC = A1 • B1 + A2 • B2 + … + Ak • Bk。一共有kkn×nn×n矩陣乘法,並且還有(k1)(k−1)n×nn×n矩陣加法,所以總的運行時間爲Θ(knlg7)Θ(k•n^{{\rm lg}7})
  
4.2-7 設計算法,僅使用三次實數乘法即可完成複數a+bia+bic+dic+di相乘。算法需接收abca、b、cdd爲輸入,分別生成實部acbdac−bd和虛部ad+bcad+bc
  
  借鑑Strassen算法的思想,該問題可以按以下步驟解決。
  (1) 計算P1P2P_1、P_2P3P_3
    P1=adP_1 = ad
    P2=bcP_2 = bc
    P3=(ab)(c+d)=acbd+adbcP_3 = (a – b)(c + d) = ac – bd + ad – bc
  (2) 計算實部和虛部
    實部:P3P1+P2=acbdP_3 – P_1 + P_2 = ac−bd
    虛部:P1+P2=ad+bcP_1 + P_2 = ad+bc
  該算法只需要33次乘法即可。
  
  本節代碼鏈接:
  https://github.com/yangtzhou2012/Introduction_to_Algorithms_3rd/tree/master/Chapter04/Section_4.2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章