Python程序员面试算法宝典---解题总结: 第4章 数组 4.20 如何获取最好的矩阵链相乘方法

# -*- coding: utf-8 -*-

'''
Python程序员面试算法宝典---解题总结: 第4章 数组 4.20 如何获取最好的矩阵链相乘方法

题目:
给定一个矩阵序列,找到最有效的方式将这些矩阵相乘在一起。给定表示矩阵链
的数组p,使得第i个矩阵Ai的维数为p[i-1]*p[i]。编写一个函数
MatrixChainOrder(),该函数应该返回乘法运算所需的最小乘数。
输入: p=(40, 20, 30, 10, 30)
输出: 26000
有4个大小为40 * 20, 20 * 30, 30 * 10和10 * 30的矩阵。
假设这四个矩阵为A, B, C, D。该函数的执行方法可以使得
执行乘法运算的次数最少。
_

分析:
问题的关键就是求矩阵运算的时候,括号放置的顺序问题
(AB)C=A(BC)
但是相乘的次数不同
假设A,B,C分别为
40 * 20, 20 * 30, 30 * 10的矩阵
那么
(AB)C中
(AB)相乘的次数=40 * 30 * 20 = 24000
此时矩阵(AB)变成了 40 * 30的矩阵
(AB)*C = 40 * 10 * 30 = 12000

另外要明白:
Am*n Bn*p 相乘的次数 = m * n * p
举例:
A是1*2的矩阵
[a b]
B是2*3的矩阵
[c d e]
[f g h]
A * B =
[ac+bf, ad+bg, ae+bh]
所以相乘次数=1*2*3=6

问题的关键就是如何设置矩阵相乘的括号来决定运算顺序。
令f[i]

关键:
1 书上解法
设m[i][j]表示矩阵链Ai到Aj的最小相乘次数
那么我们的目标就是求m[1][n],
假设数组p是矩阵的维度的下标列表
假设s[i][j]记录矩阵链Ai到Aj的最小相乘次数时的最优分割点k
则m[i][j] = m[i][k] + m[k + 1][j] + p[i-1] * p[k] * p[j]
m[i][i] = 0
每次调整矩阵链的长度l,从2到N
那么j=i + l - 1
然后计算k在i与j范围内的最小值m[i][j]
等到计算完成后,令i累加即可

2 我没想到
是因为忘记这种有起始范围的最优值,
那么设计的动态规划数组也必须是二维数组。
另外需要考虑矩阵链的长度从2到n变化

参考:
Python程序员面试算法宝典
'''

from __future__ import print_function

def multipltMatrix(p):
    if not p:
        return
    size = len(p)
    m = [[None]*(size + 1) for i in range(size + 1)]
    s = [[None]*(size + 1) for i in range(size + 1)]
    for i in range(size + 1):
        m[i][i] = 0
    # 遍历矩阵链的长度
    n = size - 1
    for l in range(2, n + 1):
        # j = i + l - 1 <=n 所以 i <= n - l + 1
        # 遍历起始点的下标
        for i in range(1, n - l + 2):
            j = i + l - 1
            m[i][j] = float('inf')
            # 寻找最佳分割点
            for k in range(i, j):
                value = m[i][k] + m[k + 1][j] + p[i-1] * p[k] * p[j]
                if value < m[i][j]:
                    m[i][j] = value
                    s[i][j] = k
    return m[1][n], s


def printResult(i, j, s):
    if i ==j:
        print("A{i}".format(i=i), end='')
    else:
        print("(", end='')
        printResult(i, s[i][j], s)
        printResult(s[i][j] + 1, j, s)
        print(")", end='')


def process():
    p = [40, 20, 30, 10, 30]
    result, s = multipltMatrix(p)
    print(result)
    i = 1
    j = len(p) - 1
    printResult(i, j, s)


if __name__ == "__main__":
    process()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值