矩陣連乘:給定n個矩陣:A1,A2,...,An,其中Ai與Ai+1是可乘的,i=1,2...,n-1。確定計算矩陣連乘積的計算次序,使得依此次序計算矩陣連乘積需要的數乘次數最少。輸入數據為矩陣個數和每個矩陣規模,輸出結果為計算矩陣連乘積的計算次序和最少數乘次數。
若A是一個p*q的矩陣,B是一個q*r的矩陣,則其乘積C=AB是一個p*r的矩陣。數乘次數是p*q*r.
動態規划算法與分治法類似,其基本思想也就是將待求解的問題分解成若干個子問題,先求解子問題,然后從這些子問題的解得到原問題的解,簡單概括為自頂向下分解,自底向上求解。與分治法不同的是,適合於用動態規划法求解的問題,經分解得到的子問題往往不是相互獨立的,換句話說,就是前面解決過的子問題,在后面的子問題中又碰到了前面解決過的子問題,子問題之間是有聯系的。如果用分治法,有些同樣的子問題會被重復計算幾次,這樣就很浪費時間了。所以動態規划是為了解決分治法的弊端而提出的,動態規划的基本思想就是,用一個表來記錄所有已經解決過的子問題的答案,不管該子問題在以后是否會被用到,只要它被計算過,就將其結果填入表中,以后碰到同樣的子問題,就可以從表中直接調用該子問題的答案,而不需要再計算一次。具體的動態規划的算法多種多樣,但他們都具有相同的填表式。
順便說一下動態規划的適用場合,一般適用於解最優化問題,例如矩陣連乘問題、最長公共子序列、背包問題等等,通常動態規划的設計有4個步驟,結合矩陣連乘分析:
(1).找出最優解的性質,並刻畫其結構特征
這是設計動態規划算法的第一步,我們可以將矩陣連乘積AiAi+1……Aj記為A[i:j]。問題就是計算A[1:n]的最優計算次序。設這個計算次序在矩陣Ak和Ak+1之間將矩陣鏈斷開,1<=k<n,使其完全加括號方式為((A1……Ak)(AK+1……An)),這樣就將原問題分解為兩個子問題,,按此計算次序,計算A[1:n]的計算量就等於計算A[1:k]的計算量加上A[k+1:n]的計算量,再加上A[1:k]和A[k+1:n]相乘的計算量。計算A[1:n]的最優次序包含了計算A[1:k]和A[k+1:n]這兩個子問題的最優計算次序,以此類推,將A[1:k]和A[k+1:n]遞歸的分解下去,求出每個子問題的最優解,子問題的最優解相乘便得到原問題的最優解。
(2).遞歸地定義最優值
這是動態規划的第二步,對於矩陣連乘積的最優計算次序的問題,設計算A[i:j],1<=i<=j<=n,所需要的最小數乘次數為m[i][j],則原問題的最優值為m[1][n]。
當i=j時,A[i:j]=Ai為單一的矩陣,則無需計算,所以m[i][j]=0,i=j=1,2,……,n。即對應的二維表對角線上的值全為0。
當i<j時,這就需要用步驟(1)的最優子結構性質來計算m[i][j]。若計算A[i:j]的最優次序在Ak和Ak+1之間斷開,i<=k<j,則m[i][j]=m[i][k]+m[k+1][j]+pi-1*pk*pj,k的位置只有j-i種可能,即k屬於集合{i,i+1,……,j-1},所以k是這j-i個位置中使計算量達到最小的那個位置。
所以m[i][j]可以遞歸地定義為 m[i][j]={ 0 i=j
min{m[i][k]+m[k+1][j]+pi-1*pk*pj } i<j ,i<=k<j }
將對應於m[i][j]的斷開位置k記為s[i][j],在計算出最優值m[i][j]后,可遞歸地由s[i][j]構造出相應的最優解
(3).以自底向上的方式計算出最優值
動態規划的一大好處是,在計算的過程中,將已解決的子問題答案保存起來,每個子問題只計算一次,而后面的子問題需要用到前面已經解決的子問題,就可以從表中簡單差出來,從而避免了大量的重復計算
import random
from pandas import *
input = int(input("輸入矩陣數:"))
matrix = [[0] * 2 for i in range(input)]
for i in range(input): #生成矩陣
if i == 0:
matrix[i][0] = random.randrange(100)
matrix[i][1] = random.randrange(100)
else:
matrix[i][0] = matrix[i-1][1]
matrix[i][1] = random.randrange(100)
m = [[0] * input for i in range(input)] #記錄連乘次數
s = [[0] * input for j in range(input)] #記錄括號位置
def MatrixMultiplication(inp):
for i in range(inp):
m[i][i] = 0
for r in range(1, inp):
for i in range(inp-r):
j = i + r
m[i][j] = m[i+1][j] + matrix[i][0] * matrix[i][1] * matrix[j][1]
s[i][j] = i+1
for k in range(i+1, j):
judge = m[i][k] + m[k+1][j] + matrix[i][0] * matrix[k][1] * matrix[j][1]
if judge < m[i][j]:
m[i][j] = judge
s[i][j] = k+1
def printmatrix(left, right):
if left == right:
print("A"+str(left+1), end='')
else:
print("(", end='')
printmatrix(left, s[left][right]-1)
printmatrix(s[left][right], right)
print(")", end='')
MatrixMultiplication(input)
dm = DataFrame(m, index=list(range(1, input+1)), columns=list(range(1, input+1)))
ds = DataFrame(s, index=list(range(1, input+1)), columns=list(range(1, input+1)))
print(matrix)
print("數乘次數:\n", dm)
print("括號位置:\n", ds)
print("最終結果:")
printmatrix(0, input-1)
