关闭 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]]

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

    上一篇返回首页 下一篇

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

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    如何删除自带的不常用应用为windows 7减负

    MySQL中多表删除方法

    改进的二值图像像素标记算法及程序实现

    windows 7 32位系统下手动修改磁盘属性例如M盘修改为F盘

    windows 7中怎么样在家庭组互传文件

    Linux应用集成MySQL数据库访问技巧

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

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