c语言备忘录算法矩阵链乘,《算法导论》读书笔记之第15章 动态规划—矩阵链乘法...

1 #include 2 using namespacestd;3 #define A_ROWS 3

4 #define A_COLUMNS 2

5 #define B_ROWS 2

6 #define B_COLUMNS 3

7 void matrix_multiply(int A[A_ROWS][A_COLUMNS],int B[B_ROWS][B_COLUMNS],intC[A_ROWS][B_COLUMNS]);8 intmain()9 {10 int A[A_ROWS][A_COLUMNS] = {1,0,11 1,2,12 1,1};13 int B[B_ROWS][B_COLUMNS] = {1,1,2,14 2,1,2};15 int C[A_ROWS][B_COLUMNS] = {0};16 matrix_multiply(A,B,C);17 for(int i=0;i)

18 {19 for(int j=0;j)

20 cout

30 {31 inti,j,k;32 for(i=0;i)

33 for(j=0;j)

34 {35 C[i][j] = 0;36 for(k=0;k)

37 C[i][j] += A[i][k] * B[k][j]; //将A的每一行的每一列与B的每一列的每一行的乘积求和

38 }39 }40 }

程序测试结果如下所示:

816222d320a3fb8c4bf6b21a701819b2.png

2、矩阵链乘问题描述

注意:在矩阵链乘问题中,实际上并没有把矩阵相乘,目的是确定一个具有最小代价的矩阵相乘顺序。找出这样一个结合顺序使得相乘的代价最低。

3、动态规划分析过程

1)最优加全部括号的结构

动态规划第一步是寻找一个最优的子结构。假设现在要计算AiAi+1....Aj的值,计算Ai...j过程当中肯定会存在某个k值(i<=ki...j分成两部分,使得Ai...j的计算量最小。分成两个子问题Ai...k和Ak+1...j,需要继续递归寻找这两个子问题的最优解。

有分析可以到最优子结构为:假设AiAi+1....Aj的一个最优加全括号把乘积在Ak和Ak+1之间分开,则Ai..k和Ak+1..j也都是最优加全括号的。

2)一个递归解

设m[i,j]为计算机矩阵Ai...j所需的标量乘法运算次数的最小值,对此计算A1..n的最小代价就是m[1,n]。现在需要来递归定义m[i,j],分两种情况进行讨论如下:

当i==j时:m[i,j] = 0,(此时只包含一个矩阵)

当ii-1pkpj} (i≤k<j)。

3)计算最优代价

虽然给出了递归解的过程,但是在实现的时候不采用递归实现,而是借助辅助空间,使用自底向上的表格进行实现。设矩阵Ai的维数为pi-1pi,i=1,2.....n。输入序列为:p=,length[p] = n+1。使用m[n][n]保存m[i,j]的代价,s[n][n]保存计算m[i,j]时取得最优代价处k的值,最后可以用s中的记录构造一个最优解。书中给出了计算过程的伪代码,摘录如下:

1 MAXTRIX_CHAIN_ORDER(p)2 n = length[p]-1;3 for i=1to n4 do m[i][i] = 0;5 for t = 2 to n //t is the chain length

6 do for i=1 to n-t+1

7 j=i+t-1;8 m[i][j] =MAXLIMIT;9 for k=i to j-1

10 q = m[i][k] + m[k+1][i] + qi-1qkqj;11 if q

MATRIX_CHAIN_ORDER具有循环嵌套,深度为3层,运行时间为O(n3)。如果采用递归进行实现,则需要指数级时间Ω(2n),因为中间有些重复计算。递归是完全按照第二步得到的递归公式进行计算,递归实现如下所示:

1 int recursive_matrix_chain(int *p,int i,int j,int m[N+1][N+1],int s[N+1][N+1])2 {3 if(i==j)4 m[i][j] = 0;5 else

6 {7 intk;8 m[i][j] =MAXVALUE;9 for(k=i;k)

10 {11 int temp = recursive_matrix_chain(p,i,k,m,s) +recursive_matrix_chain(p,k+1,j,m,s) + p[i-1]*p[k]*p[j];12 if(temp

对递归算计的改进,可以引入备忘录,采用自顶向下的策略,维护一个记录了子问题的表,控制结构像递归算法。完整程序如下所示:

1 int memoized_matrix_chain(int *p,int m[N+1][N+1],int s[N+1][N+1])2 {3 inti,j;4 for(i=1;i<=N;++i)5 for(j=1;j<=N;++j)6 {7 m[i][j] =MAXVALUE;8 }9 return lookup_chain(p,1,N,m,s);10 }11

12 int lookup_chain(int *p,int i,int j,int m[N+1][N+1],int s[N+1][N+1])13 {14 if(m[i][j]

19 {20 intk;21 for(k=i;kk)

22 {23 int temp = lookup_chain(p,i,k,m,s)+lookup_chain(p,k+1,j,m,s) + p[i-1]*p[k]*p[j]; //通过递归的形式计算,只计算一次,第二次查表得到24 if(temp

4)构造一个最优解

第三步中已经计算出来最小代价,并保存了相关的记录信息。因此只需对s表格进行递归调用展开既可以得到一个最优解。书中给出了伪代码,摘录如下:

1 PRINT_OPTIMAL_PARENS(s,i,j)2 if i==j3 then print "Ai"

4 else

5 print "(";6 PRINT_OPTIMAL_PARENS(s,i,s[i][j]);7 PRINT_OPTIMAL_PARENS(s,s[i][j]+1,j);8 print")";

4、编程实现

采用C++语言实现这个过程,现有矩阵A1(30×35)、A2(35×15)、A3(15×5)、A4(5×10)、A5(10×20)、A6(20×25),得到p=<30,35,15,5,10,20,25>。实现过程定义两个二维数组m和s,为了方便计算其第一行和第一列都忽略,行标和列标都是1开始。完整的程序如下所示:

1 #include 2 using namespacestd;3

4 #define N 6

5 #define MAXVALUE 1000000

6

7 void matrix_chain_order(int *p,int len,int m[N+1][N+1],int s[N+1][N+1]);8 void print_optimal_parents(int s[N+1][N+1],int i,intj);9

10 intmain()11 {12 int p[N+1] = {30,35,15,5,10,20,25};13 int m[N+1][N+1]={0};14 int s[N+1][N+1]={0};15 inti,j;16 matrix_chain_order(p,N+1,m,s);17 cout<

36 void matrix_chain_order(int *p,int len,int m[N+1][N+1],int s[N+1][N+1])37 {38 inti,j,k,t;39 for(i=0;i<=N;++i)40 m[i][i] = 0;41 for(t=2;t<=N;t++) //当前链乘矩阵的长度

42 {43 for(i=1;i<=N-t+1;i++) //从第一矩阵开始算起,计算长度为t的最少代价

44 {45 j=i+t-1;//长度为t时候的最后一个元素

46 m[i][j] = MAXVALUE; //初始化为最大代价

47 for(k=i;k<=j-1;k++) //寻找最优的k值,使得分成两部分k在i与j-1之间

48 {49 int temp = m[i][k]+m[k+1][j] + p[i-1]*p[k]*p[j];50 if(temp

53 s[i][j] = k; //记录当前的括号位置,即矩阵的编号

54 }55 }56 }57 }58 }59

60 //s中存放着括号当前的位置

61 void print_optimal_parents(int s[N+1][N+1],int i,intj)62 {63 if( i ==j)64 cout<

66 {67 cout<

73 }

程序测试结果如下所示:

6a126b7348eac5fef984b055720f3713.png

5、总结

动态规划解决问题关键是分析过程,难度在于如何发现其子问题的结构及子问题的递归解。这个需要多多思考,不是短时间内能明白。在实现过程中遇到问题就是数组,数组的下标问题是个比较麻烦的事情,如何能够过合理的去处理,需要一定的技巧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值