「アルゴリズム入門」を学ぶ(18) ~動的計画法の行列連鎖乗算(C言語)~


序文

この記事では主に、動的計画法における行列連鎖の乗算問題、つまり行列連鎖が与えられた場合、その最小コスト計算次数を求める問題について説明します。動的プログラミング方式の分析と C 言語の実装について説明します。


1. 行列連鎖乗算

1. 問題の説明

n 個の行列< A 1 、 A 2 、 A 3 、 A 4 、...、 A n > <A_1,A_2,A_3,A_4,...,A_n> のシーケンス (行列のチェーン) があるとします<1234... > 、今度はその積A 1 A 2 A 3 A 4 . . . A n A_1 A_2A_3A_4...A_n を計算したいとします。
1234... A
行列チェーン乗算の場合、括弧を追加することで、どの 2 つの行列を最初に乗算するかを決定できます。掛け算の順序に関係なく、最終的な結果には影響しません。
ただし、異なる次数間の行列乗算の場合、コンピューターはまったく異なる代償を支払わなければなりません。
たとえば、 < A 1 , A 2 , A 3 > 、ここで A 1 は 2 ∗ 3、A 2 は 3 ∗ 4、A 3 は 4 ∗ 1 <A_1,A_2,A_3> 、ここで A_1 は 2*3、A_2は 3*4; A_3 は 4*1<123>ここで、A12人分3 A23人分4 A34人分1
2 つの異なる戦略の削減コストを計算できます。

  • ( ( A 1 A 2 ) A 3 ) = 2 ∗ 3 ∗ 4 + 2 ∗ 4 ∗ 1 = 32 ((A_1A_2)A_3)=2*3*4+2*4*1=32( (12) A3)=234+241=32
  • ( A 1 ( A 2 A 3 ) ) = 3 * 4 * 1 + 2 * 3 * 1 = 18 (A_1(A_2A_3))=3*4*1+2*​​3*1=18( A1( A23))=341+231=18

明らかに、この 2 つのコストはまったく異なります。さて、私たちの問題は行列チェーンが与えられた場合、その最小コスト計算順序を取得します。

2. 問題解決

1. 最適な副問題構造

まず、対応する再帰的解の公式を直接与えます。
w [ i , j ] = { 0 if i = jmin { w [ i , k ] + w [ k + 1 , j ] + pi − 1 pkpj } if i < jw [ i,j]=\begin{cases} 0& \text{ if }i=j \\ min\{ w[i,k]+w[k+1,j]+p_{i-1}p_kp_j\} & \text{ if } i<j \\ \end{cases}w [j ]={ 0min { w [ i ,k ]+w [ k+1 j ]+pi 1ppj} もし 私が=j もし 私が<j

  • w[i,j] は、i 番目の行列から j 番目の行列までのパス上の行列チェーンの最小コストを表します。
  • pi − 1 pkpj p_{i-1}p_kp_jpi 1ppj最適なサブソリューションから最適な親ソリューションまでを構築するのに必要なコストを表します。

2. 動的プログラミング

2 次元配列を使用して部分問題に対する最適な解決策を記録します。たとえば、w[2,4]の最適解は配列w[1,3]に記録されます(配列は0から始まるため)。
同時に、長さ 2 の部分行列チェーンから最適解を見つけ始め、最終的に n 行列チェーンの最適解が得られるまで長さを 1 ずつ増やします。
このプロセスでは 3 層のサイクルが使用されます

  • ループの最初の層は、マトリックス チェーンの長さを小さいものから大きいものまで横断します。
  • 2 番目の層は 1 番目の層のループをループして、長さの下の部分行列チェーン内の最初の行列の位置を決定します。
  • 3 番目の層は、最初の 2 つのループによって決定されたマトリックス チェーンのすべての「切断」スキームをループします。

3. 最適ソリューションの構築

動的計画法の 3 層ループの後、最小コストの特定の値を取得できますが、行列の乗算の具体的な順序は何か、最適な解を構築する必要があります。
最適なソリューションの構築を支援するために、2 次元の数値グループを使用します。

  • s[0,6] を使用して、行列チェーン w[1,7] の最初の括弧で囲まれた位置、つまりその k 値を記録します。
  • すべての場合の行列のチェーンの k の最適値を記録します。
  • その後、アルゴリズムを再帰的に呼び出すことで、括弧内の出力結果を取得できます。詳細については、以下のコードを参照してください。読者は、再帰ツリーを描くことによって、解決策を迅速に決定することもできます。

3、Cコード

1. コード

#include<stdio.h>
#include<stdlib.h>
#include<time.h>



//由于有n个矩阵链乘
//需要n+1个存储单元来存储行列信息
//此处SIZE为存储单元的个数
//因此矩阵链乘的个数是SIZE-1 
#define SIZE 10
//此处可以规定矩阵的行列数为1~LIM的随机数 
#define LIM 100
//注意:
//由于计算机存储数大小的限制
//SIZE和LIM不宜过大
//过大肯定会使得程序崩溃 



int Matrix_chain_order(int *p,int s[][SIZE-1],int w[][SIZE-1],int size)
{
    
    
	int i=0;
	int j=0;
	int k=0;
	int q=0;
	int l=0;
	int r=0;
	for(i=0;i<size-1;i++)
	{
    
    
		w[i][i]=0;
		s[i][i]=0;
	}
	for(i=2;i<=(size-1);i++)
	{
    
    
		for(j=1;j<=(size-i);j++)
		{
    
    
			l=j;
			r=j+i-1;
			w[l-1][r-1]=10000000;
			for(k=l;k<r;k++)
			{
    
    
				q=w[l-1][k-1]+w[k][r-1]+p[l-1]*p[k]*p[r];
				if(q<w[l-1][r-1])
				{
    
    
					w[l-1][r-1]=q;
					s[l-1][r-1]=k;
				}
			}
		}
	}
	return w[0][size-2];
}



void print_priority(int s[][SIZE-1],int l,int r)
{
    
    
	if(l==r)
	{
    
    
		printf("A%d",l+1);
		return;
	}
	printf("(");
	print_priority(s,l,s[l][r]-1);
	print_priority(s,s[l][r],r);
	printf(")");		
}



int main()
{
    
    
	int p[SIZE];
	int s[SIZE-1][SIZE-1];
	int w[SIZE-1][SIZE-1];
	int min_pay;
	int i=0;
	int j=0;
	srand((unsigned)time(NULL));
	for(i=0;i<SIZE;i++)
	{
    
    
		p[i]=rand()%LIM+1;
	}
	for(i=0;i<SIZE;i++)
	{
    
    
		printf("%5d",p[i]);
	}
	printf("\n");
	min_pay=Matrix_chain_order(p,s,w,SIZE);
	printf("%5d\n",min_pay);
	print_priority(s,0,SIZE-2);
	return 0;
} 

2. 結果

ここに画像の説明を挿入


要約する

読者は記事の不適切な部分を修正してください。動的プログラミングに関するその他の質問については、 「アルゴリズム入門」学習 (17)----動的プログラミング鋼帯切断 (C 言語) も
参照してください。

おすすめ

転載: blog.csdn.net/weixin_52042488/article/details/127147710