(基于Java)算法之动态规划——矩阵连乘问题
动态规划(Dynamic Programming):与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适用于动态规划法求解的问题,经分解得到的子问题往往不是互相独立的。
使用动态规划法求解的问题需要符合一些条件:
(1):所求解问题必须要符合最优子结构;(最优子结构即:原问题的最优解中包含了子问题的最优解)
(2):原问题分解出来的子问题相互之间存在联系,即递归时会重复解决之前已解决过的子问题。
先说明一些前提:
(1):矩阵相乘的条件是:前一个矩阵的行数=后一个矩阵的列数;
(2):可以用数组p[0:n]来存放n个连乘矩阵的行数和列数(p[i-1]表示Ai的行数,p[i]表示Ai的列数);
(3):用A[i:j]表示Ai连乘到Aj,假设最优的加括号方式(最外层)是:(Ai*……*Ak)(Ak+1*……*Aj) 。
下面我们先从动态规划解决矩阵连乘问题的最初始的方法入手,代码如下:
private static int recurMatrixChain(int i,int j,int[] p) //最初始的矩阵连乘问题算法
{
if(i == j) return 0; //i == j,即只有一个矩阵,计算次数当然为零
int min = recurMatrixChain(i,i,p) + recurMatrixChain(i+1,j,p) + p[i-1] * p[i] * p[j];
for(int k = i + 1; k < j; k++){
int t = recurMatrixChain(i,k,p) + recurMatrixChain(k+1,j,p) + p[i-1] * p[k] * p[j];
if(t < min) min = t; //从k处断开,如果t比min更小,则说明存在更优的解决方法,把t赋值给min
}
return min;
}
递归掌握得好的话,结合注释,理解起来并不会觉得困难。但这个方法存在一个严重的弊端,虽然能计算出最优解,但是在计算过程中,其实调用了指数级别次的方法,而这么多次调用其实都在解决重复子问题而已。
下面介绍一种能解决上边提到的问题,动态规划解决矩阵连乘问题的第二种方法——备忘录方法,代码如下:
private static int lookupChain(int i,int j,int[][] m,int[] p)
{
if(m[i][j] > 0) return m[i][j]; //如果m[i][j]非零,则说明该子问题被计算过,只需取出这个数,无需进行计算
if(i == j) return 0;
int min = lookupChain(i,i,m,p) + lookupChain(i+1,j,m,p) + p[i-1] * p[i] * p[j];
for(int k = i + 1; k < j; k++){
int t = lookupChain(i,k,m,p) + lookupChain(k+1,j,m,p) + p[i-1] * p[k] * p[j];
if(t < min) min = t;
}
m[i][j] = min; //对于未记录的子问题,通过计算把该子问题的最优解求出后,存放在数组中
return min;
} private static int memoizedMatrixChain(int n,int[][] m,int[] p) //这个就是解决矩阵连乘问题的备忘录方法
{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
m[i][j] = 0; //对数组进行初始化
return lookupChain(1,n,m,p);
}
我们可以看出,lookupChain方法跟recurMatrixChain方法其实差不多,只是加插了两行代码而已,而这就是两种算法之间的不同之处,就是最重要的地方。第一次调用时跟第一种方法一样,同时记录了子问题的最优解,当第二次调用时,便可以直接从备忘录中提取子问题的最优解,大大地减少了方法的调用次数。
下面介绍另一种动态规划方法——填表法,我的老师将其称为真·动态规划,代码如下:
private static void matrixChain(int n,int[][] m,int[] p){ //填表法,我的老师也叫这方法为真·动态规划方法
for(int i = n-1; i >= 1; i--)
for(int j = i+1; j <= n; j++){
int min = m[i][j] + m[i+1][j] + p[i-1] * p[i] * p[j];
for(int k = i+1; k < j; k++){
int t = m[i][k] + m[k+1][j] + p[i-1] * p[k] * p[j];
if(t < min) min = t;
}
m[i][j] = min;
}
}
我们会发现,该算法其实跟第一种方法几乎相同,但是这个算法是直接用数组来存放子问题的最优解,跟备忘录方法稍有不同,而且这个算法并没有使用递归。而且这个填表法其实需要个人有比较强的能力,我们可以先举个简单点的例子,然后自己画一个二维数组,分析第一个数是填入到哪里,第二个数是填入到哪里,如此重复,找出规律后就可以开始编写自己的填表法了,在这里的是自下而上的填表法。
到这里结束了,如果有不对的地方或者对这个算法有更好的建议,欢迎指出!
(基于Java)算法之动态规划——矩阵连乘问题的更多相关文章
- Java算法——动态规划
基本思想: 动态规划算法通常用于求解具有某种最优性质的问题(作用就是求最优解).在这类问题中,可能会有许多可行解.每一个解都对应于一个值,我们希望找到具有最优值的解.动态规划算法与分治法类似,其基本思 ...
- 排序算法总结(基于Java实现)
前言 下面会讲到一些简单的排序算法(均基于java实现),并给出实现和效率分析. 使用的基类如下: 注意:抽象函数应为public的,我就不改代码了 public abstract class Sor ...
- 一个基于RSA算法的Java数字签名例子
原文地址:一个基于RSA算法的Java数字签名例子 一.前言: 网络数据安全包括数据的本身的安全性.数据的完整性(防止篡改).数据来源的不可否认性等要素.对数据采用加密算法加密可以保证数据本身的安全性 ...
- 基于Java实现的冒泡排序算法
冒泡排序是一种简单基础的排序算法,相信在大学课堂里老师已经讲过了,现在我基于Java来实现一遍. 简述 冒泡排序正如其关键词一样,杂乱的气泡经过浮动,最后大的气泡飘到了上面而小的气泡在下面,无序的元素 ...
- 算法(Java实现)—— 动态规划算法
动态规划算法 应用场景-0-1背包问题 背包问题:有一个背包,容量为4磅,现有物品如下 物品 重量 价格 吉他(G) 1 1500 音响(S) 4 3000 电脑(L) 3 2000 要求: 达到目标 ...
- 基于java平台的常用资源整理
这里整理了基于java平台的常用资源 翻译 from :akullpp | awesome-java 大家一起学习,共同进步. 如果大家觉得有用,就mark一下,赞一下,或评论一下,让更多的人知道.t ...
- 这里整理了基于java平台的常用资源
这里整理了基于java平台的常用资源 翻译 from :akullpp | awesome-java 大家一起学习,共同进步. 如果大家觉得有用,就mark一下,赞一下,或评论一下,让更多的人知道.t ...
- 基于java的设计模式入门(1)——为什么要学习设计模式
大年初一,楼主在这里给大家拜年,祝大家码上升职加薪,码上有对象结婚,码上有车有房,幸福安康. 过完年,回学校注册报道之后,大概就要回深圳到公司开始实习了.提高自己,无非就有两种方式,一是看书学习,二是 ...
- 动态规划&矩阵连乘
动态规划&矩阵连乘 动态规划的概念 • 与分治方法类似 分-治-合 • 与分治方法不同 子问题之间并非相互独立 • 基本思想 用一个表记录 ...
随机推荐
- Haproxy的负载均衡和高可用配置
一.Haproxy的理解 Haproxy是一个使用c语言编写的自由开发源代码软件,它提供高可用性.负载均衡.以及基于http和tcp的应用程序代理. Haproxy特别使用于那些负载特别大 ...
- 干掉某个用户的所有进程 ---slay和kill
要杀掉指定进程,你可以: 1.sudo slay <name> 知道进程名字即可 2.kill[参数][进程号] 知道进程ID即可 用‘slay’干掉某个用户的所有进程 slay 是Ch ...
- Linux升级nodejs及多版本管理
最近要用到开发要用到nodejs,于是跑到开发机运行了下node,已经安装了,深感欣慰,是啥版本呢?再次运行了下node -v,原来是0.6.x的.估计是早先什么时候谁弄的.那么来升级下node吧. ...
- 在C#中调用Java生成的jar包文件的方法
C#工程调用Java已生成的jar包步骤如下: 一.使用IKVM.NET组件 首先到IKVM官网(http://www.ikvm.net)下载组件,下载地址:https://sourceforge.n ...
- [Luogu4899][IOI2018] werewolf 狼人
luogu sol \(\mbox{IOI2018}\)的出题人有没有看过\(\mbox{NOI2018}\)的题目呀... \(\mbox{Kruskal}\)重构树+二维数点. 题目相当于是问你从 ...
- USB相关的sysfs文件
主要来自driver/usb/core/sysfs.c: 1.bConfigurationValue RW,W时调用了usb_set_configuration()实时设置配置.根据USB规范(例如第 ...
- MSMQ向远程服务器发送消息----错误总结
一:路径错误(Path)错误 如果向远程服务器发送消息,请使用格式名的形式,如: FormatName:Direct=TCP:121.0.0.1\\private$\\queueFormatName: ...
- ruby里面的毒瘤
转自https://ruby-china.org/topics/26120 由于ruby的灵活性,如何很好的使用,其实是比较有技巧的,其实总结起来,可以大概归类如下 常规业务,不到万不得已,不使用元编 ...
- bzoj1811 mea
Description 考虑一个非递减的整数序列 S1,....Sn+1(Si<=Si+1 1<=i<=n). 序列M1...Mn是定义在序列S的基础上,关系式为 Mi=( Si ...
- 马士兵Spring-声明式事务管理-annotation
1.事务加在DAO层还是service层? service中可能多涉及多种DAO的操作,比如存了一个User之后,需要保存一条日志信息:如果在DAO中分别设置事务的话,一个DAO下面方法抛出异常了,但 ...