关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 算法设计 »算法题系列:矩阵链乘问题

    算法题系列:矩阵链乘问题

    2015-05-21 00:00:00 出处:小明知道
    分享

    矩阵链乘问题是最典型的动态规划问题,要理解下面的内容请先阅读这篇动态规划的总结。

    1.问题描述

    矩阵链乘问题的描述如下,就是说要确定一个完全加括号的形式使得矩阵链乘需要进行的标量计算数目最少,矩阵Ai的维数为pi 1×pi,假如穷举所有可能形式的话,时间复杂度是指数级的!因为该问题满足最优子结构,并且子问题存在重叠,所以我们可以借助动态规划来求解。

    2.问题分析

    我们需要确定一个递归式来将大家要求解的问题表示出来,下面摘自算法导论,介绍地非常详细

    最后给出的递归式如下,就是说大家要怎样确定从第i个矩阵到第j个矩阵组成的矩阵链的最优解。假如i和j相等,那么就是一个矩阵,不需要运算;假如i小于j,那么肯定要从它们中间的某个位置分开来,那从哪里分开来呢 这个我们可以尝试下所有可能的选择,也就是尝试不同的位置k,k满足条件(i <= k < j),在位置k将矩阵链进行分开,看看它需要的计算次数,然后我们从这些可能的k中选择使得计算次数最小的那个k进行分开,分开了之后我们的问题就变成了2个小问题,确定矩阵链从i到k 和另一个矩阵链从k+1到j的最优解。假如我们一开始设置i=1(第一个矩阵),j=n(最后一个矩阵),那么,经过上面的递归即可得到我们需要的解。这就是递归的思想!

    3.代码实现

    根据上面的思想我们很快就可以写出一个递归版本的矩阵链承法的实现代码,输出的结果也没有错,给出的加括号的方式是( ( A1 ( A2 A3 ) ) ( ( A4 A5 ) A6 ) )。[问题的数据是算法导论中的问题的数据,值是30,35,15,5,10,20,25]。

    def matrixchain_rec(p,i,j):
        if i==j:
            return 0
        for k in range(i,j):
            q=matrixchain_rec(p,i,k)+matrixchain_rec(p,k+1,j)+p[i-1]*p[k]*p[j]
            if q<m[i][j]:
                m[i][j]=q
                s[i][j]=k
        return m[i][j]
    
    def showmatrixchain(s,i,j):
        if i==j:
            print 'A%d'%(i),
        else:
            print '(',
            showmatrixchain(s,i,s[i][j])
            showmatrixchain(s,s[i][j]+1,j)
            print ')',
    
    n=6
    p=[30,35,15,5,10,20,25]
    m=[[sys.maxint for i in range(n+1)] for j in range(n+1)]
    s=[[0 for i in range(n+1)] for j in range(n+1)]
    # pprint.pprint(m)
    result=matrixchain_rec(p,1,6)
    print(result) #15125
    showmatrixchain(s,1,6) #( ( A1 ( A2 A3 ) ) ( ( A4 A5 ) A6 ) )

    上面的代码运行没有问题,但是,它不够完美!为什么呢 很明显,矩阵链乘问题子问题存在重叠,下面这张图很形象地显示了哪些子问题被重复计算了,所以我们需要改进,改进的方法就是使用带备忘录的递归形式!

    要改成带备忘录的很简单,但是,这次我们不能直接使用原来的装饰器,因为Python中的dict不能对list对象进行hash,所以大家要简单地修改下我们key值的构建,也很简单,看下代码就明白了:

    from functools import wraps
    
    def memo(func):
        cache={}
        @wraps(func)
        def wrap(*args):
            #build new key!!!
            key=str(args[1])+str(args[2])
            if key not in cache:
                cache[key]=func(*args)
            return cache[key]
        return wrap
    
    @memo
    def matrixchain_rec(p,i,j):
        if i==j:
            return 0
        for k in range(i,j):
            q=matrixchain_rec(p,i,k)+matrixchain_rec(p,k+1,j)+p[i-1]*p[k]*p[j]
            if q<m[i][j]:
                m[i][j]=q
                s[i][j]=k
        return m[i][j]
    
    def showmatrixchain(s,i,j):
        if i==j:
            print 'A%d'%(i),
        else:
            print '(',
            showmatrixchain(s,i,s[i][j])
            showmatrixchain(s,s[i][j]+1,j)
            print ')',
    
    n=6
    p=[30,35,15,5,10,20,25]
    m=[[sys.maxint for i in range(n+1)] for j in range(n+1)]
    s=[[0 for i in range(n+1)] for j in range(n+1)]
    # pprint.pprint(m)
    result=matrixchain_rec(p,1,6)
    print(result) #15125
    showmatrixchain(s,1,6) #( ( A1 ( A2 A3 ) ) ( ( A4 A5 ) A6 ) )

    接下来的一个问题是,我们怎么实现迭代版本呢 迭代版本关键在于顺序!我们怎么保证我们在计算$A{i…j}的最优解时,所有可能的k的选择需要求解的子问题A{i…k}以及A_{(k+1)…j}$是已经求解出来了的呢 一个简单但是有效的想法就是看矩阵链的长度,我们先计算矩阵链短的最优解,然后再计算矩阵链长的最优解,后者计算时所需要求解的子问题肯定已经求解完了,对不对 于是就有了迭代版本的实现,需要注意的就是其中的i,j,k的取值范围。

    import sys
    def matrixchain_iter(p):
        n=len(p)-1 #total n matrices 6
        #to solve the problem below, so initialize to n+1!!!
        m=[[0 for i in range(n+1)] for j in range(n+1)]
        s=[[0 for i in range(n+1)] for j in range(n+1)]
        # for i in range(n): #for matrix with len=1
            # m[i][i]=0
        # pprint.pprint(m)
        for l in range(2,n+1): #iterate the length, max is n
            for i in range(1,n-l+2): #i max is n-l+1
                j=i+l-1 #j is always l away from i
                m[i][j]=sys.maxint #initial to infinity
                for k in range(i,j):
                    #attention to python array when index < 0!!!
                    #solution is using more space with useless values
                    q=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]
                    if q<m[i][j]:
                        m[i][j]=q
                        s[i][j]=k
            # print('when len is %d ' % (l))
            # pprint.pprint(m)
        return m,s
    
    print('')
    m,s=matrixchain_iter(p)
    print(m[1][6]) #15125
    showmatrixchain(s,1,6) #( ( A1 ( A2 A3 ) ) ( ( A4 A5 ) A6 ) )

    实现的时候需要注意一点,在Python中取list中的值时,假如索引是负值的话会从后面往前数返回对应的元素,而以前我们用其他语言的时候肯定是提示越界了,所以代码中用来存储结果的数数组是(n+1)x(n+1),而不是nxn的,这样的话就能够保证返回的是0,而不是从后往前数得到的结果。

    得到的数组m如下,m[1,6]就是我们需要的解。

    [[0, 0, 0, 0, 0, 0, 0],
     [0, 0, 15750, 7875, 9375, 11875, 15125],
     [0, 0, 0, 2625, 4375, 7125, 10500],
     [0, 0, 0, 0, 750, 2500, 5375],
     [0, 0, 0, 0, 0, 1000, 3500],
     [0, 0, 0, 0, 0, 0, 5000],
     [0, 0, 0, 0, 0, 0, 0]]

    数组s如下:

    [[0, 0, 0, 0, 0, 0, 0],
     [0, 0, 1, 1, 3, 3, 3],
     [0, 0, 0, 2, 3, 3, 3],
     [0, 0, 0, 0, 3, 3, 3],
     [0, 0, 0, 0, 0, 4, 5],
     [0, 0, 0, 0, 0, 0, 5],
     [0, 0, 0, 0, 0, 0, 0]]

    将这个两个数组旋转下,并且只看上三角部分的数字,就可以得到算法导论中给出的那张三角图形了,非常类似杨辉三角

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Edge浏览器百度被劫持/篡改怎么办,地址后边跟着尾巴#tn=68018901_7_oem_dg

    Google Chrome 在 iPhone 上新增了 Safari 数据导入选项

    Windows 11专业版 KMS工具激活产品密钥的方法

    DEDECMS安全策略官方出品

    Microsoft Text Input Application 可以关闭吗?

    新版本QQ如何关闭自带的浏览器?

    C++编程语言中continue的用法和功能,附举例示范代码

    c++ map 的数据结构、基本操作以及其在实际应用中的使用。

    C语言如何避免内存泄漏、缓冲区溢出、空指针解引用等常见的安全问题

    C语言中的break语句详解

    IT头条

    马斯克2026最新采访总结:2040年,全球机器人数量将突破100亿台

    23:52

    专家解读|规范人工智能前沿业态健康发展的新探索:解读《人工智能拟人化互动服务管理暂行办法》

    00:54

    用至强 6高存力搞定MoE卸载!

    17:53

    美国将允许英伟达向中国“经批准的客户”出售H200 GPU

    02:08

    苹果与微信就15%手续费达成一致?腾讯未置可否

    22:00

    技术热点

    PHP 和 Node.js 的10项对比挑战

    Javascript闭包深入解析及实现方法

    windows 7、windows 8.1手动增加右键菜单功能技巧

    MYSQL出错代码大汇总

    windows 7假死机怎么办 windows 7系统假死机的原因以及解决方法

    Ubuntu(Linux)下配置IP地址的方法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。