算法分析 动态规划解决矩阵连乘问题

  • Post author:
  • Post category:其他




矩阵连乘问题

问题:

给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1 是可乘的,i=1,2…,n-1。

如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。

1、按设计动态规划算法的步骤解题。

(1)找出最优解的性质,并刻划其结构特征。
(2)递归地定义最优值。
(3)以自底向上的方式计算出最优值。
(4)根据计算最优值时得到的信息,构造最优解(由子结构的最优解得到原先大问题的最优解)。

2、求算法的时间复杂性,和空间复杂性

3、体会动态规划和穷举法在解决该问题中的本质差异。



问题辅助分析:

(1)问题的描述

给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2,…,n-1。要算出这n个矩阵的连乘积A1A2…An。由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用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次数乘。

(2)算法设计思想

动态规划算法的基本思想是将待求解问题分成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,动态规划法经分解得到的子问题往往不是相互独立的,前一子问题的解为后一子问题的解提供有用的信息,可以用一个表来记录所有已解决的子问题的答案,不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

代码:

#include<iostream>
#include<iomanip>
#include<memory.h>

using namespace std;


//求出矩阵A[i:j]的最少数乘次数m[i][j],和记录矩阵A[i:j]此时的断开位置s[i][j]
//p为输入的矩阵的行数或列数。n为矩阵个数。m[i][j]为矩阵A[i:j]最少乘法次数。 s[i][j]为当最少乘法次数时,记录此时的断开位置(即加括号的位置)

void MatrixChain(int *p,int n,int m[][100],int s[][100])
{
	for (int i = 1; i <= n; i++) m[i][i] = 0;  //初始化单个矩阵的乘法次数都为0次。使用矩阵的下标从1,1开始
	for (int r = 2; r <= n; r++)   //r为矩阵连乘的长度,即矩阵个数
		for (int i = 1; i <= n - r+1; i++) {      //i为矩阵连乘的起点
			int j=i+r-1;                                    //j为矩阵连乘的终点
			m[i][j] = m[i][i]+ 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;}
			}
		}
}

//利用断开位置s[i][j]输出矩阵A[i:j]的最优加括号方式
void print_optimal(int s[100][100], int i ,int j, int a[])
{
	if(i==j)	cout<<" A["<<a[i-1]<<","<<a[i]<<"]";
	else{
		cout<<" ( ";
		print_optimal(s,i,s[i][j],a);
		print_optimal(s,s[i][j]+1,j,a);
		cout<<" ) ";
	}
}

void print_matrix(int p[][100],int n)
{
	for(int i=1;i<n+1;i++)
	{
		for(int j=1;j<n+1;j++)
		{
			cout<<setw(8)<<p[i][j];
		}
		cout<<endl;
	}
}

int main()
{
	int n,k;
	int array[100],m[100][100],s[100][100];
	memset(array,0,sizeof(array));
	memset(m,0,sizeof(m));
	memset(s,0,sizeof(s));

	while(true)
	{
		cout<<endl<<"请输入矩阵的个数:(输入小于0结束程序)"<<endl;
		cin>>n;
		if (n<0)
			break;
		cout<<"请输入 "<<n+1<<" 个整数,分别是各个矩阵的行列数"<<endl;
		for(k=0;k<n+1;k++)
		{
			cin>>array[k];
		}
		cout<<endl;
		MatrixChain(array,n,m,s);
		cout<<"m矩阵"<<endl;
		print_matrix(m,n);
		cout<<"s矩阵"<<endl;
		print_matrix(s,n);
		cout<<"最优的运算方式的乘法次数为:"<<m[1][n]<<endl;
		cout<<"加括号的方式为"<<endl;
		print_optimal(s,1,n,array);
		cout<<endl;
	}
}

运行截图

在这里插入图片描述



版权声明:本文为qq_40054352原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。