1. 程式人生 > >動態規劃-矩陣鏈乘自頂向下和自底向上的Python實現

動態規劃-矩陣鏈乘自頂向下和自底向上的Python實現

問題背景:

由於矩陣乘法滿足結合律,所以計算矩陣連乘的連乘積可以用許多不同的計算次序,這種計算次序可以用加括號的方式來確定。我們的目標只是確定運算順序然後降低乘法的運算次數。

最優子結構:m[i,j]=0    當i=j;   min{m[i,k]+m[k+1,j]+p[i-1]*p[k]*p[j]}    當i<j。(k就是切割點的位置)

即i和j矩陣之間,k處新增括號(最後相乘的地方),最後一步乘法代價=i的行*k的列*j的列

1.自頂向下的實現:
#p是矩陣的列數,p[0]是第一個矩陣的行,p[1]是第一個矩陣的列,p[2]是第二個矩陣的列。因為矩陣可以相乘必然相容
#memozed_list儲存運算乘法的次數,s儲存矩陣Ai→Aj中最後一次相乘的地方,n是相乘矩陣數量
def matrix_memoed(p):
    n = len(p) - 1
    memozed_list = []
    s = []
    for i in range(n + 1):
        memozed_list.append([-1 for i in range(n + 1)])
        s.append([0 for i in range(n + 1)])
    matrix_help(memozed_list, p, s, 1, n)
    for i in memozed_list[1:]:
        print(i)
 matrix_print(s, 1, n)#構造最優解,就是輸出怎麼每一步打括號的

def matrix_help(memozed_list, p, s, i, j):
    if (memozed_list[i][j] >= 0):
        return memozed_list[i][j]
    if (i == j):
        memozed_list[i][j] = 0
        return memozed_list[i][j]
    memozed_list[i][j] = 99999999
    for k in range(i, j):
        q = matrix_help(memozed_list, p, s, i, k) + matrix_help(memozed_list, p, s, k + 1, j) + p[i - 1] * p[k] * p[j]
        if (q < memozed_list[i][j]):
            memozed_list[i][j] = q
            s[i][j] = k
    return memozed_list[i][j]

此演算法就是分治法,加儲存運算記錄。剛學沒多久,我只是談談我的思路。從1,n傳入後,k從1開始一直取值到n-1,然後對比,在內部遞迴矩陣A1→Ak與Ak+1→An。

2.自底向上的實現
def matrix_DowntoUp(p):
    n = len(p) - 1
    memozed_list = []
    s = []
    for i in range(n + 1):
        memozed_list.append([-1 for i in range(n + 1)])
        s.append([0 for i in range(n + 1)])
    for i in range(1, len(p)):
        memozed_list[i][i] = 0

    for l in range(2, len(p)):
        for i in range(1, n - l + 2):
            j = i + l - 1
            memozed_list[i][j] = 99999999
            for k in range(i, j):
                q = memozed_list[i][k] + memozed_list[k + 1][j] + p[i - 1] * p[k] * p[j]
                if (q < memozed_list[i][j]):
                    memozed_list[i][j] = q
                    s[i][j] = k
    for i in memozed_list[1:]:
        print(i)
    matrix_print(s,1,n)
    return memozed_list and s

同上的命名方式,自底向上,是這樣計算的。

第一步:將邊界確定,先定下A[i][i]的,因為相同一個矩陣的乘法數目是0

第二步:定一個子鏈l,先從長度為2開始,一直到子鏈長度為n。i取值1→n-l+1.如果子鏈長度是2,那i最大隻能是n-l+1,。比如n=6,子鏈長2,那麼i最大是5。所以j=i+l-1。就是說i,j確定了長為l的子鏈。

第三步:把要算的i,j,初始化為99999999,然後k取i→j-1的位置迴圈找出最小的k

3構造最優解
def matrix_print(s,i,j):
    if i==j:
        print("A[%d]"%(i),end="")
    else:
        print("(",end="")
        matrix_print(s,i,s[i][j])
        matrix_print(s,s[i][j]+1,j)
        print(")",end="")

這就是在s中遞迴,因為s二維數組裡儲存了Ai與Aj之間的最佳位置k,在k位置的右邊打右括號,在開始的位置打左括號即可

總結

我覺得動態規劃還是蠻難的,學識不深,對遞迴的理解和應用還很欠缺