算法实验动态规划----矩阵连乘
- 格式:doc
- 大小:65.50 KB
- 文档页数:10
动态规划法解矩阵连乘问题实验内容给定n个矩阵{A1,A2,….An},其中Ai与Ai+1是可乘的,i=1,2,3。
,n-1。
我们要计算这n个矩阵的连乘积。
由于矩阵乘法满足结合性,故计算矩阵连乘积可以有许多不同的计算次序。
这种计算次序可以用加括号的方式确定。
若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则我们可依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。
解题思路将矩阵连乘积A(i)A(i+1)…A(j)简记为A[i:j],这里i <= j 。
考察计算A[i:j]的最优计算次序。
设这个计算次序在矩阵A(k)和A(k+1)之间将矩阵链断开,i <= k < j, 则其相应完全加括号方式为(A(i)A(i+1) …A(k)) * (A(k+1)A(k+2) …A(j))。
特征:计算A[i:j]的最优次序所包含的计算矩阵子链A[i:k]和A[k+1:j]的次序也是最优的。
矩阵连乘计算次序问题的最优解包含着其子问题的最优解。
设计算A[i:j] , 1 <= i <= j <= n ,所需要的最少数乘次数m[i,j],则原问题的最优值为m[1, n]当i = j 时,A[i:j]=Ai ,因此,m[i,i] = 0 , i = 1,2, …,n当i < j 时,m[i,j] = m[i,k] + m[k+1,j] + p(i-1)p(k)p(j) 这里A(i)的维数为p(i-1)*(i)( 注:p(i-1)为矩阵A(i)的行数,p(i)为矩阵A[i]的列数)实验实验代码#in elude <iostream>#in elude <vector>using n amespaee std ;class matrix_cha in{public:matrix_eha in(const vector <int> & c){ cols = c ;count = cols.size (); mc.resize (co unt);s.resize (co unt);for (i nt i = 0; i < count; ++i) { mc[i].resize (co unt); s[i].resize (co unt);}for (i = 0; i < count; ++i) { for (int j = 0; j < count; ++j) { mc[i][j] = 0 ;s[i][j] = 0 ;//记录每次子问题的结果void lookup_cha in () {__lookup_cha in (1, count - 1);min_count = mc[1][co unt - 1];cout << "min _multi_co unt = "<< min_count << endl ;//输出最优计算次序__trackback (1, count - 1);}//使用普通方法进行计算void calculate () {int n = count - 1; //矩阵的个数// r表示每次宽度// i,j表示从从矩阵i到矩阵j// k表示切割位置for (i nt r = 2; r <= n; ++ r) {for (int i = 1; i <= n - r + 1; ++ i) {int j = i + r - 1 ;//从矩阵i到矩阵j连乘,从i的位置切割,前半部分为0mc[i][j] = mc[i+1][j] + cols[i-1] * cols[i] * cols[j];s[i][j] = i ;for (int k = i + 1; k < j; ++ k) {int temp = mc[i][k] + mc[k + 1][j] +cols[i-1] * cols[k] * cols[j];if (temp < mc[i][j]) {mc[i][j] = temp ;s[i][j] = k ;}} // for k} // for i} // for rmin_count = mc[1][ n];cout << "min _multi_co unt = "<< min_count << endl ;//输出最优计算次序__trackback (1, n);private:int __lookup_cha in (int i, i nt j) {//该最优解已求出,直接返回if (mc[i][j] > 0) {return mc[i][j];}if (i == j) {return 0 ; //不需要计算,直接返回}//下面两行计算从i到j按照顺序计算的情况int u = __lookup_cha in (i, i) + __lookup_cha in (i + 1, j)+ cols[i-1] * cols[i] * cols[j];s[i][j] = i ;for (int k = i + 1; k < j; ++ k) {int temp = __lookup_cha in (i, k) + __lookup_cha in(k + 1, j)+ cols[i - 1] * cols[k] * cols[j];if (temp < u) {u = temp ;s[i][j] = k ;}}mc[i][j] = u ;return u ;}void __trackback (int i, i nt j) {if (i == j) {return ;}__trackback (i, s[i][j]);__trackback (s[i][j] + 1, j);cout <<i << "," << s[i][j] << " " << s[i][j] + 1 << "," << j << endl;}private:vector<int> cols ; // 列数int count ; // 矩阵个数+ 1vector<vector<int> > mc; //从第i个矩阵乘到第j个矩阵最小数乘次数vector<vector<int> > s; //最小数乘的切分位置int min_count ; //最小数乘次数};int mai n(){//初始化con st i nt MATRIX_COUNT = 6 ;vectorvi nt> c(MA TRIX_COUNT + 1);c[0] = 30 ;c[1] = 35 ;c[2] = 15 ;c[3] = 5 ;c[4] = 10 ;c[5] = 20 ;c[6] = 25 ;matrix_cha in me (c); // mc.calculate (); mc」o okup_cha in (); return 0 ;}实验结果实验验证从s 可知计算顺序为((A1(A2A3))((A4A5))A6))实验总结在这次实验中懂得了动态规划法运用方法和解题思路的重要性,在这个程序中如何 建立动态规划的过程建立递归过程 保存已解决的子问题答案。
算法设计与分析——矩阵连乘问题(动态规划)⼀、问题描述引出问题之前我们先来复习⼀下矩阵乘积的标准算法。
int ra,ca;//矩阵A的⾏数和列数int rb,cb;//矩阵B的⾏数和列数void matrixMultiply(){for(int i=0;i<ra;i++){for(int j=0;j<cb;j++){int sun=0;for(int k=0;k<=ca;k++){sum+=a[i][k]*b[k][j];}c[i][j]=sum;}}}给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。
如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
例如,给定三个连乘矩阵{A1,A2,A3}的维数分别是10*100,100*5和5*50,采⽤(A1A2)A3,乘法次数为10*100*5+10*5*50=7500次,⽽采⽤A1(A2A3),乘法次数为100*5*50+10*100*50=75000次乘法,显然,最好的次序是(A1A2)A3,乘法次数为7500次。
加括号的⽅式对计算量有很⼤的影响,于是⾃然地提出矩阵连乘的最优计算次序问题,即对于给定的相继n个矩阵,如何确定矩阵连乘的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
⼆、问题分析矩阵连乘也是Catalan数的⼀个常⽤的例⼦,关于时间复杂度的推算需要参考离散数学关于Catalan的内容。
下⾯考虑使⽤动态规划法解矩阵连乘积的最优计算次序问题。
1、分析最优解的结构问题的最优⼦结构性质是该问题可以⽤动态规划求解的显著特征!!!2、建⽴递归关系3、计算最优值public static void matrixChain(int n) {for (int i = 1; i <= n; i++) {m[i][i] = 0;}for (int r = 2; r <= n; r++) {//i与j的差值for (int i = 1; i <= n - r + 1; i++) {int j = i + r - 1;m[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j];s[i][j] = i;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 < m[i][j]) {m[i][j] = t;s[i][j] = k;}}}}}4、构造最优解public static void traceback(int i, int j) {if (i == j) {System.out.printf("A%d", i); // 输出是第⼏个数据return;}System.out.printf("(");traceback(i, s[i][j]);// 递归下⼀个数据System.out.printf(" x ");traceback(s[i][j] + 1, j);System.out.printf(")");}三、总结。
动态规划算法解矩阵连乘问题一、实验目的通过上机实验,要求掌握动态规划算法的问题描述、算法设计思想、程序设计和算法复杂性分析等。
二、实验环境VC6.0 C++,vs2005三、实验内容1 用动态规划算法解矩阵连乘问题(1)问题的描述给定n个矩阵{A1,A2,…,A n},其中A i与A i+1是可乘的,i=1,2,…,n-1。
要算出这n个矩阵的连乘积A1A2…A n。
由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。
这种计算次序可以用加括号的方式来确定。
若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。
完全加括号的矩阵连乘积可递归地定义为:(1)单个矩阵是完全加括号的(当然实际上可以不加);(2)矩阵连乘积A是完全加括号的,则A可表示为2个完全加括号的矩阵连乘积B和C的乘积并加括号,即A=(BC)。
例如,矩阵连乘积A1A2A3A4有5种不同的完全加括号的方式:(A1(A2(A3A4))),(A1((A2A3)A4)),((A1A2)(A3A4)),((A1(A2A3))A4),(((A1A2)A3)A4)。
每一种完全加括号的方式对应于一个矩阵连乘积的计算次序,这决定着作乘积所需要的计算量。
若A是一个p×q矩阵,B 是一个q×r矩阵,则计算其乘积C=AB的标准算法中,需要进行pqr次数乘。
(3)为了说明在计算矩阵连乘积时,加括号方式对整个计算量的影响,先考察3个矩阵{A1,A2,A3}连乘的情况。
设这三个矩阵的维数分别为10×100,100×5,5×50。
加括号的方式只有两种:((A1A2)A3),(A1(A2A3)),第一种方式需要的数乘次数为10×100×5+10×5×50=7500,第二种方式需要的数乘次数为100×5×50+10×100×50=75000。
一、实验目的通过本次实验,加深对动态规划算法的理解和应用,掌握解决矩阵连乘问题的方法,提高算法分析和设计能力。
二、实验原理矩阵连乘问题是指给定n个矩阵,每个矩阵都与它的前一个矩阵可乘,求计算这些矩阵连乘积的最优计算次序,以使计算过程中所需的数乘次数最少。
由于矩阵乘法满足结合律,因此可以通过加括号的方式确定不同的计算次序。
三、实验步骤1. 问题描述:给定n个矩阵A1, A2, ..., An,其中Ai与Ai-1是可乘的。
求计算矩阵连乘积A1A2...An的最优计算次序,使得计算过程中所需的数乘次数最少。
2. 输入数据:矩阵个数n,每个矩阵的规模。
3. 输出结果:计算矩阵连乘积的最优计算次序和最少数乘次数。
4. 算法设计:- 定义一个二维数组m[i][j],其中m[i][j]表示计算矩阵AiAi-1...Aj的最少数乘次数。
- 初始化m[i][i] = 0,因为单个矩阵无需计算。
- 对于每个子问题A[i:j],计算m[i][j]的最小值:- 遍历k从i到j-1,将问题分解为A[i:k]和Ak+1:j,计算m[i][k]和m[k+1][j]的和,并加上k个矩阵的维度乘积。
- 取上述和的最小值作为m[i][j]的值。
5. 递归关系:- 当i = j时,m[i][j] = 0。
- 当i < j时,m[i][j] = min(m[i][k] + m[k+1][j] + p[i-1]p[k]p[j]),其中k从i到j-1,p[i-1]表示矩阵Ai-1的行数,p[j]表示矩阵Aj的列数。
6. 自底向上计算:- 从m[1][1]开始,按照递归关系计算m[1][2],m[1][3],...,m[1][n]。
- 然后计算m[2][3],m[2][4],...,m[2][n],以此类推,直到计算m[1][n]。
7. 输出最优计算次序:- 从m[1][n]开始,根据递归关系和子问题的最优解,逐步确定每个子问题的最优计算次序,直到得到整个问题的最优计算次序。
一、实验背景与目的矩阵连乘问题是一个经典的算法问题,它涉及给定一系列矩阵,确定这些矩阵的最佳乘积顺序,以最小化乘法操作的次数。
本实验旨在通过动态规划算法解决矩阵连乘问题,加深对动态规划方法的理解,并提高算法分析与设计的能力。
二、实验内容与步骤1. 问题描述与理解:- 给定n个矩阵A1, A2, ..., An,其中任意两个相邻矩阵都是可乘的。
- 目标是确定计算这些矩阵连乘积的最佳顺序,以最小化所需的乘法次数。
2. 算法分析:- 使用动态规划方法,通过将问题分解为子问题并存储子问题的解来求解。
- 设定m[i, j]表示矩阵Ai到Aj的最佳乘积顺序的乘法次数。
3. 动态规划过程:- 初始化m[i, i] = 0,因为单个矩阵不需要乘法。
- 对于长度为k的矩阵序列,通过遍历所有可能的分割点,计算m[i, j]的最小值。
- 具体步骤包括:- 对于每个可能的k(1 ≤ k ≤ n-1),- 对于每个起始矩阵i(1 ≤ i ≤ n-k),- 计算m[i, i+k-1]和m[i+k, j],- 更新m[i, j]为m[i, i+k-1] + m[i+k, j] + p[i-1] p[i] p[i+k]。
4. 代码实现:- 使用C或Java等编程语言实现动态规划算法。
- 编写辅助函数来计算矩阵的乘法次数。
三、实验结果与分析1. 实验结果:- 通过实验,成功实现了矩阵连乘问题的动态规划算法。
- 得到了计算给定矩阵序列连乘积所需的最小乘法次数。
2. 结果分析:- 动态规划方法有效地解决了矩阵连乘问题,避免了穷举法的指数级时间复杂度。
- 通过分析子问题的解,我们可以找到整个问题的最优解。
四、实验总结与反思1. 实验收获:- 加深了对动态规划方法的理解,特别是如何通过子问题的解来构建整个问题的解。
- 学会了如何将实际问题转化为动态规划问题,并使用代码实现算法。
2. 反思与展望:- 实验过程中遇到了一些挑战,如理解子问题的定义和计算最优子结构的策略。
矩阵链乘法(动态规划)
⼀题意描述:
给定由n个要相乘的矩阵构成的序列(链)<A1,A2,A3,····A n>。
由于矩阵满⾜结合律(加括号⽅式表⽰结合⽅式),不同的计算⽅式导致的求出最终计算结果的代价相异,有的花的时间很少,有的⽅式所花时间很多,那么下⾯的任务就是求出算出结果所需要的最少时间及⼀个最优解。
⼆思路分析:
设p(n)表⽰⼀串n个矩阵可能的加全部括号⽅案数。
当n=1时,只有⼀个矩阵,此时p(1)=1。
当n>=2时,⼀个加全部括号的矩阵乘积等于两个加全部括号的⼦矩阵乘积的乘积,⽽且这两个⼦乘积之间的分裂可能发⽣在第k个和第k+1个矩阵之间,其中k=1,2,····,n-1;因此可以求得递归式:
1.找局部最优解:把问题:转化成两个最优⼦问题:及
2.构造递归解:
⾸先定义m[i,j]为解决⼦问题A[i....j]的最⼩计算次数,那么解决整个问题A[1,n]所花的最⼩时间为m[1,n]。
那么递归⽅程可以写成如下形式:
为了跟踪如何构造⼀个最优解我们可以定义s[i,j]为这样的⼀个k值,在该处分裂乘积后可得⼀个最优解。
3.构造函数进⾏求解
输出最优路径的函数⾃⼰编写,经过调⽤数组s[i][j]即可。
动态规划之矩阵连乘【问题描述】给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。
如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
例如,给定三个连乘矩阵{A1,A2,A3}的维数分别是10*100,100*5和5*50,采⽤(A1A2)A3,乘法次数为10*100*5+10*5*50=7500次,⽽采⽤A1(A2A3),乘法次数为100*5*50+10*100*50=75000次乘法,显然,最好的次序是(A1A2)A3,乘法次数为7500次。
分析:矩阵链乘法问题描述:给定由n个矩阵构成的序列{A1,A2,...,An},对乘积A1A2...An,找到最⼩化乘法次数的加括号⽅法。
1)寻找最优⼦结构此问题最难的地⽅在于找到最优⼦结构。
对乘积A1A2...An的任意加括号⽅法都会将序列在某个地⽅分成两部分,也就是最后⼀次乘法计算的地⽅,我们将这个位置记为k,也就是说⾸先计算A1...Ak和Ak+1...An,然后再将这两部分的结果相乘。
最优⼦结构如下:假设A1A2...An的⼀个最优加括号把乘积在Ak和Ak+1间分开,则前缀⼦链A1...Ak的加括号⽅式必定为A1...Ak的⼀个最优加括号,后缀⼦链同理。
⼀开始并不知道k的确切位置,需要遍历所有位置以保证找到合适的k来分割乘积。
2)构造递归解设m[i,j]为矩阵链Ai...Aj的最优解的代价,则3)构建辅助表,解决重叠⼦问题从第⼆步的递归式可以发现解的过程中会有很多重叠⼦问题,可以⽤⼀个nXn维的辅助表m[n][n] s[n][n]分别表⽰最优乘积代价及其分割位置k 。
辅助表s[n][n]可以由2种⽅法构造,⼀种是⾃底向上填表构建,该⽅法要求按照递增的⽅式逐步填写⼦问题的解,也就是先计算长度为2的所有矩阵链的解,然后计算长度3的矩阵链,直到长度n;另⼀种是⾃顶向下填表的备忘录法,该⽅法将表的每个元素初始化为某特殊值(本问题中可以将最优乘积代价设置为⼀极⼤值),以表⽰待计算,在递归的过程中逐个填⼊遇到的⼦问题的解。
矩阵连乘问题(动态规划)一、实验目的与要求1、明确矩阵连乘的概念。
2、利用动态规划解决矩阵连乘问题。
二、实验题:问题描述:给定n个矩阵{A1,A2,...,An},其中Ai与Ai+1是可乘的,i=1,2...,n-1。
确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少数乘次数。
三、实验代码#include<iostream>using namespace std;const int MAX = 100;//p用来记录矩阵的行列,main函数中有说明//m[i][j]用来记录第i个矩阵至第j个矩阵的最优解//s[][]用来记录从哪里断开的才可得到该最优解int p[MAX+1],m[MAX][MAX],s[MAX][MAX];int n;//矩阵个数int matrixChain(){for(int i=0;i<=n;i++)m[i][i]=0;for(int r=2;r<=n;r++)//对角线循环for(int i=0;i<=n-r;i++){//行循环int j = r+i-1;//列的控制//找m[i][j]的最小值,先初始化一下,令k=im[i][j]=m[i+1][j]+p[i+1]*p[i]*p[j +1];s[i][j]=i;//k从i+1到j-1循环找m[i][j]的最小值for(int k = i+1;k<j;k++){int temp=m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];if(temp<m[i][j]){m[i][j]=temp;//s[][]用来记录在子序列i-j段中,在k位置处//断开能得到最优解s[i][j]=k;}}}return m[0][n-1];//最终结果}//根据s[][]记录的各个子段的最优解,将其输出void traceback(int i,int j){if(i==j){cout<<'A'<<i;return ;}if(i<s[i][j])cout<<'(';traceback(i,s[i][j]);if(i<s[i][j])cout<<')';if(s[i][j]+1<j)cout<<'(';traceback(s[i][j]+1,j);if(s[i][j]+1<j)cout<<')';}void traceback(){cout<<'(';traceback(0,n-1);cout<<')';cout<<endl;}int main(){cout<<"请输入矩阵的个数:"<<endl;cin>>n;cout<<"输入矩阵(形如a*b,中间用空格隔开):"<<endl;for(int i=0;i<=n;i++)cin>>p[i];//测试数据可以设为六个矩阵分别为//A1[30*35],A2[35*15],A3[15*5],A4[5*10],A5[10*20],A6[20*25] //则p[0-6]={30,35,15,5,10,20,25}cout<<"输出结果如下:"<<endl;matrixChain();traceback(0,n-1);//最终解值为m[0][n-1];cout<<endl;return 0;}四、实验结果。
实验3 矩阵连乘一、 实验目的1. 理解动态规划的OS1和OS2原则OS1: Overlapping Subproblems; OS2: Optimal Substructure.2. 进一步理解先做证明、再做设计、最后写程序的算法设计过程对于矩阵连乘的效率提高问题,要求实验者按步骤给出从最初的思路到最终的程序,这一算法设计与实现过程。
3. 矩阵连乘动态规划算法的优化在格子设计、观察程序输出等的基础上,探讨提高矩阵连乘动态规划算法效率的技术。
二、 实验环境C/C++编程环境 或 任何编程语言环境三、 实验内容1. 矩阵连乘问题的描述输入:n 个矩阵12n A ,A ,...,A输出:×××12n A A ...A 所需要的最少乘法次数OptimalMC (按蛮力法计算矩阵m nA ×与n pB ×相乘所需标量乘法次数=m n p ××),并给出得到这个OptimalMC 的12n A ,A ,...,A 乘法次序。
若以二叉树形式表示乘法次序,对于234×××1A A A A ,一种乘法次序,如图1所示:图1 矩阵连乘的乘法次序示例上图表示()()234×××1A A A A2. 矩阵连乘问题的算法设计请实验者按下述步骤设计矩阵连乘问题的动态规划算法:2.1 写出求解OptimalMC 的递推式注意:应指出递推式中的哪些部分表示状态变量、哪些部分表示动作、哪些部分表示最优解。
并给出递推式的初始条件。
2.2 证明2.1中给出的递推式的正确性证明方法,可采用Copy & Paste 方法。
若递推式成立,则称递推式满足OS2: Optimal Substructure 原则。
2.3 格子设计依据递推式:1)画出二维内存单元格的计算规则若子问题(由单元格表示)之间存在重叠(即按计算规则,单元格中的数据存在依赖关系),则称本问题满足OS1: Overlapping Subproblems 。
动态规划实现矩阵链乘法问题矩阵链乘法问题( matrix-chain multiplication problem ) (1)问题描述 给定n个矩阵的链<A 1 ,A 2 ,…,A n >,其中i=1,2,…,n,矩阵A i的维数为p i-1 ×p i。
求⼀个完全“括号化⽅案”,使得计算乘积A 1 A 2 …A n 所需的标量乘法次数最⼩ (2)最优括号化⽅案的结构特征 ⽤记号 A i,j表⽰ A i A i+1 …A j通过加括号后得到的⼀个最优计算模式,且恰好在A k与A k+1之间分开。
则“前缀”⼦链A i A i+1 …A k必是⼀个最优的括号化⼦⽅案,记为A i,k;同理“后缀”⼦链A k+1 A k+2 …A j也必是⼀个最优的括号化⼦⽅案,记为A k+1,j。
(3)⼀个递归求解的⽅案 对于矩阵链乘法问题,我们将所有对于1≤i≤j≤n确定A i A i+1 …A j的最⼩代价括号⽅案作为⼦问题。
令m[i,j]表⽰计算矩阵A i,j所需要的标量乘法的次数最⼩值,则最优解就是计算A i...n所需的最低代价就是m[1,n] 递归定义m[i,j]。
①对于i=j的情况下,显然有m=0,不需要做任何标量乘法运算。
所以,对于所有的i=1、2......n,m[i,i] = 0. ②当i < j的情况,就按照最优括号化⽅案的结构特征进⾏计算m[i,j]。
假设最优括号化⽅案的分割点在矩阵A k和A k+1之间,那么m的值就是A i...k和A k+1...j的代价加上两者量程的代价的最⼩值。
即。
该公式的假设是最优分割点是已知的,但是实际上不知道。
然⽽,k只有j-i中情况取值。
由于最优分割点k必定在i~j内取得,只需要检查所有可能的情况,找到最优解即可。
可以得出⼀个递归公式 m只是给出了⼦问题最优解的代价,但是并未给出构造最优解的⾜够信息(即分割点的位置信息)。
所以,在此基础之上,我们使⽤⼀个⼆维数组s[i,j]来保存 A i A i+1 …A j 的分割点位置k。
算法分析与设计实验报告实验题目:动态规划算法的设计与实现1、实验目的通过本实验,掌握动态规划算法的设计的基本思想,进一步提高学生的编程能力。
2、实验内容:给定n个矩阵{A1,A2,…,A n},其中A i与A i+1是可乘的,i=1,2…,n-1。
如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
3、源程序if (t<u) //返回t,k中较小的值,并记录断点处k{ u=t; s[i][j]=k;} }return u; }int Look(int i,int j) //备忘录计算最优值{ if (m[i][j]>0){ return m[i][j]; }if (i == j) return 0;int u=Look(i, i)+Look(i+1,j)+p[i-1]*p[i]*p[j]; s[i][j]=i;for (int k=i+1; k<j;k++){ int t=Look(i,k)+Look(k+1,j)+p[i-1]*p[k]*p[j]; //递归if (t<u){ u=t; //从k处断开,分别求得每次的数乘次数s[i][j]=k; //返回t,k中较小的值,并记录断点处k} } m[i][j]=u;return u; }void Traceback(int i,int j) { //输出矩阵结合方式,加括号输出if(i == j) //只有一个矩阵,直接输出{ cout<<"A"<<i; }else if(i+1 == j) //两个矩阵,加括号输出{ cout<<"(A"<<i<<"A"<<j<<")"; }else{ cout<<"("; Traceback(i,s[i][j]); //递归,从最得到最优解的地方s[i][j]处断开Traceback(s[i][j]+1,j);cout<<")"; } }void main(){ cout<<"输入矩阵个数:n=";cin>>n; cout<<"输入第一个矩阵行数和第一个到第n个矩阵的列数:"; for(int i=0;i<=n;i++){ cin>>p[i]; } cout<<endl; cout<<"请选择解决矩阵连乘问题的方法:"<<endl; cout<<"1.动态规划算法"<<endl; cout<<"2.直接递归算法"<<endl; cout<<"3.备忘录算法"<<endl;cout<<"0.退出..."<<endl;cout<<endl;cout<<"请选择算法:";cin>>q; cout<<endl;while(q!=0){ switch(q){case 1: matrixChain(); cout<<"动态规划算法解决矩阵连乘问题:"<<endl; cout<<"最优计算次序为:";Traceback(1,n); cout<<endl; cout<<"矩阵连乘的最优数乘次数为:"<<m[1][n]<<endl; //最终解值为m[1][n]break;case 2: Recur(0,n); cout<<"直接递归算法解决矩阵连乘问题:"<<endl;5、结论动态规划算法设计通常有四个步骤:1.找出最优解的性质,并刻画其结构特征。
动态规划程序设计实验目的:掌握并实现动态规划算法。
实验内容:对维数为序列(5,10,3,12,5,50,6)的各矩阵。
找出其矩阵链乘的一个最优加全括号。
实验要求:利用动态规划思想写出算法的伪代码和C程序代码(一)算法思想穷举所有的计算次序,且对每一计算次序确定其乘法次数。
由此可找出n个矩阵进行连乘积A1A2…An的最小乘法次数。
将矩阵链乘积简记为A[i:j] ,这里i≤j考察计算A[i:j]的最优计算次序。
设这个计算次序在矩阵Ak和Ak+1之间将矩阵链断开,i≤k<j,则其相应完全加括号方式为计算量:A[i:k]的计算量加上A[k+1:j]的计算量,再加上A[i:k]和A[k+1:j]相乘的计算量设计算A[i:j],1≤i≤j≤n,所需要的最少数乘次数m[i,j],则原问题的最优值为m[1,n]当i=j时,A[i:j]=Ai,因此,m[i,i]=0,i=1,2,…,n当i<j时,可以递归地定义m[i,j]为:k位置只有j-i种可能(二)程序代码//动态规划import java.io.*;public class Testsuanfa {public final int len = this.GetN()+1;public int[] A = new int[len];public double[][] M = new double[len][len];public double[][] S = new double[len][len];//取得用户需要规划的矩阵连乘的个数。
public int GetN(){int dvalue = 0;String value;System.out.println("请输入连乘矩阵个数:");BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));try {value = bfr.readLine();dvalue =Integer.parseInt(value);//捕捉输入异常} catch (IOException e) {System.out.println("输入出错了,请重新输入:");System.exit(0);}catch (NumberFormatException e2) {System.out.println("请输入正确的数字!!");System.exit(0);}return dvalue;}//输入矩阵的序列public int GetA(){int dvalue = 0;String value;System.out.println("请输入分别矩阵维数序列:");BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));try {value = bfr.readLine();dvalue =Integer.parseInt(value);//捕捉输入异常} catch (IOException e) {System.out.println("输入出错了,请重新输入:");System.exit(0);}catch (NumberFormatException e2) {System.out.println("请输入正确的数字!!");System.exit(0);}return dvalue;}public void f(){//调用GetA方法,拿到每个序列值for(int i=0;i<len;i++){A[i] = this.GetA();}for(int i=0;i<len;i++){M[i][i] = 0;}//依次从长度为2到len,求解每一个长度的最有加全括号。
实现矩阵连乘的动态规划算法1.计算连个矩阵乘积的标准算法://标准算法void MatrixMultiply(int a[][MAXN], int b[][MAXN], int p, int q, int r){int sum[MAXN][MAXN];memset(sum, 0, sizeof(sum));int i, j, k;//遍历矩阵a的⾏for (k = 0; k < p; k++){//遍历矩阵b的列for (j = 0; j < r; j++){//对应位置相乘for (i = 0; i < q; i++){sum[k][j] += a[k][i] * b[i][j];}}}}所以A、B两个矩阵相乘的计算量为p*q*r。
2. 计算连个矩阵乘积的动态规划算法:#include<stdio.h>#include<stdlib.h>#include<Windows.h>#define MAX 100int matrix_chain(int *p, int n, int **m, int **s){//a[][]最⼩乘次数//s[][]最⼩乘数时的断开点int i,j,r,k;for (i = 0; i < n; i++) //单⼀矩阵的最⼩乘次都置为0{m[i][i] = 0;}for (r = 2; r <= n; r++) //r为连乘矩阵的个数{for (i = 0; i <= n-r; i++) //i表⽰连乘矩阵中的第⼀个{j = i + r -1; //j表⽰连乘矩阵中的最后⼀个m[i][j] = 99999;for (k = i; k <= j-1; k++) //在第⼀个与最后⼀个之间寻找最合适的断开点,注意,这是从i开始,即要先计算两个单独矩阵相乘的乘次{int tmp = m[i][k] + m[k+1][j] + p[i]*p[k+1]*p[j+1];if (tmp < m[i][j]){m[i][j] = tmp;s[i][j] = k;}}}}return m[0][n-1];}void print_chain(int i, int j, char **a,int **s){ //递归的⽅式来把最⼩乘数的表达式输出if (i == j){printf("%s",a[i]);}else{printf("(");print_chain(i,s[i][j],a,s);print_chain(s[i][j]+1,j,a,s);printf(")");}}int main(){//min_part[i][j]存储的是i+1到j+1的最⼩乘次,因为是从0开始//min_point[i][j]存储的是i+1到j+1之间最⼩乘次时的分割点int *p, **min_part, **min_point;char **a;int n = 6,i;int ret;p = (int *)malloc((n+1)*sizeof(int));a = (char **)malloc(n*sizeof(char*));min_part = (int **)malloc(n*sizeof(int *)); min_point = (int **)malloc(n*sizeof(int *));for (i = 0; i < n; i++){min_part[i] = (int *)malloc(n*sizeof(int)); min_point[i] = (int *)malloc(n*sizeof(int));a[i] = (char *)malloc(n*sizeof(char));}p[0] = 30; //第⼀个矩阵的⾏数p[1] = 35; //第⼆个矩阵的⾏数p[2] = 15; //……p[3] = 5; //……p[4] = 10; //……p[5] = 20; //第六个矩阵的⾏数p[6] = 25; //第六个矩阵的列数a[0] = "A1";a[1] = "A2";a[2] = "A3";a[3] = "A4";a[4] = "A5";a[5] = "A6";ret = matrix_chain(p,n,min_part,min_point); printf("Minest times:%d.\n",ret);print_chain(0,n-1,a,min_point);printf("\n");free(p);free(min_part);free(min_point);free(a);system("pause");return 0;}3.递归加括号的过程的运算量://加括号的过程是递归的。
首先看两个图。
矩阵相乘。
然后下面是矩阵相乘的代码。
/***只是矩阵的相乘。
*@author Administrator**/public class MatrixMultiply {public static void main(String args[]) {int a[][] = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };int b[][] = { { 1, 5, 9 }, { 2, 6, 10 }, { 3, 7, 11 }, { 4,8, 12 } };int c[][] = new int[3][3];matrixMultiply(a, b, c, 3, 4, 4, 3);for (int i = 0; i <= 2; i++)for (int j = 0; j <= 2; j++)System.out.println(c[i][j]);}public static void matrixMultiply(int[][] a, int[][] b, int[][] c, int ra,int ca, int rb, int cb) {if (ca != rb) // 不能乘,a行不等于b列throw new IllegalArgumentException("矩阵不可乘");for (int i = 0; i < ra; i++)for (int j = 0; j < cb; j++) {int sum = a[i][0] * b[0][j];for (int k = 1; k < ca; k++)sum += a[i][k] * b[k][j];c[i][j] = sum;}}}动态规划的思想其实是把所有可能都记录起来,然后根据之前的去推后面的。
下面的代码中,用P来表示矩阵连乘的输入如P是{30,35,15,5,10,20} P有6个元素,却只有5个矩阵就代表30*35 35*15 15*5 5*10 10*20 5个矩阵数组M[i][j]代表从i乘到j这样需要的最少数乘数。
实验三:动态规划法
【实验目的】
深入理解动态规划算法的算法思想,应用动态规划算法解决实际的算法问题。
【实验性质】
验证性实验。
【实验要求】
对于下列所描述的问题,给出相应的算法描述,并完成程序实现与时间复杂度的分析。
该问题描述为:一般地,考虑矩阵A1,A2,…,An的连乘积,它们的维数分别为d0,d1,…,dn,即Ai的维数为di-1×di (1≤i≤n)。
确定这n个矩阵的乘积结合次序,使所需的总乘法次数最少。
对应于乘法次数最少的乘积结合次序为这n个矩阵的最优连乘积次序。
按给定的一组测试数据对根据算法设计的程序进行调试:6个矩阵连乘积A=A1×A2×A3×A4×A5×A6,各矩阵的维数分别为:A1:10×20,A2:20×25,A3:25×15,A4:15×5,A5:5×10,A6:10×25。
完成测试。
【算法思想及处理过程】
【程序代码】
printf ("\n\n矩阵连乘次数的最优值为:\n");
printf ("-----------------------------------------------\n");
print2 (0, 6-1, s);
printf ("\n-----------------------------------------------\n\n");
return 0;
}
void MatrixChain (int p[], int m[][6], int s[][6], int n)
{
int i, j, k, z, t;
for (i=0; i<n; i++)
{
m[i][i] = 0;
s[i][i] = 0;
}
for (z=2; z<=n; z++)
for (i=0; i<=n-z; i++)
{
j = i + z - 1;
m[i][j] = m[i+1][j] + p[i] * p[i+1] * p[j+1];
s[i][j] = i;
for (k = i+1; k<j; k++)
{
t = m[i][k] + m[k+1][j] + p[i] * p[k+1] * p[j+1];
if (t < m[i][j])
{
m[i][j] = t;
s[i][j] = k;
}
}
}
}
void print1 (int m[][6], int s[][6],int p[])
{
int i, j;
printf ("\n\n程序所给矩阵如下:\n");
printf ("-----------------------------------------------\n");
for (i=0; i<6; i++)
printf ("A%d 矩阵: %2d X %-2d \n",i+1,p[i], p[i+1]);
printf ("\n\n-----------------------------------------------\n"); printf("矩阵的最少计算次数为:%d\n", m[0][5]);
printf ("-----------------------------------------------\n");
printf ("\n\n数乘次数: \n");
printf ("-----------------------------------------------\n");
for (i=0; i<6; i++)
{
for (j=0; j<i; j++)
printf (" ");
for (j=i; j<6; j++)
printf ("%-7d", m[i][j]);
printf ("\n");
}
printf ("-----------------------------------------------\n");
printf ("\n\n中间断点: \n");
printf ("-----------------------------------------------\n");
for (i=0; i<6; i++)
{
for (j=0; j<i; j++)
printf (" ");
for (j=i; j<6; j++)
printf ("%-7d", s[i][j]);
printf ("\n");
}
printf ("-----------------------------------------------\n"); }
void print2(int i, int n, int s[][6])
{
if (i == n)
【运行结果】
【算法分析】
函数MatrixChain( )包含三重循环,循环体内的计算量为O(1) , 所以算法的时间复杂度为O(n3) ,算法的空间时间复杂度为O(n3) .
【实验总结】。