方陣行列式並行化計算(OpenMP,MPI),並計算加速比

以下內容爲本人並行計算課程的期末作業,有不足的地方,請多多指教!

實驗目的

本實驗的目的主要有以下三點:

1 實現方陣行列式的計算。

2 實現方陣行列式的並行計算,分別基於 OpenMP MPI

3 比較以上三種算法的運行時間,算加速比。

實驗設計

2.生成方陣

爲方便,本實驗的方陣不採取手動輸入的方式,而是使用隨機數來生成矩陣元素。

定義了一個全局方陣變——int p[100][100]在創建方陣時,方陣的階數N(N<100)外部輸入。然後用兩層for循環來給方陣 p左上角 N×N個位置賦值。具體實現如下

/*
 * 定義矩陣階數N
 */

int N;

/*
 * 定義一個全局矩陣
 */

int p[100][100];

/*
 * 用隨機數生成矩陣
 */

void create(){
	int i,j;
	for(i=0;i<N;i++)
	{
		for(j=0;j<N;j++)
	
	     {
           int a=rand()%15;//產生隨機數,並賦給數組
	       p[i][j]=a;
		 }
	}
}


2.打印矩陣

將生成的矩陣輸出,以便驗算其計算行列式的正確性。具體實現如下:

/*
 * 輸出矩陣
 */

void print()
{
	int i,j;
	for(i=0;i<N;i++)
    {
		for(j=0;j<N;j++)
			printf("%d ",p[i][j]);
		printf("\n");
	}
}

2.計算矩陣行列式

計算矩陣行列式的方法有很多,本實驗選擇的方法是:行列式按行展開法。行列式等於它任一行的各元素與其對應的代數餘子式乘積之和。代數餘子式:A(ij)=-1)^(i+j)M(ij).  (ij)爲下標。某個元素的餘子式等於原行列式劃去該元素所在的行和列。本實驗採取按第一行展開的方法。即:將高階的行列式按第一行展開,一直重複展開行爲,直到階數爲 1。上述過程可用遞歸完成。

2.3.遞歸實現代碼

根據上面的理論,我們容易得出如下的實現方法:

/*
 * 計算行列式的函數
 */

long long mydet(int  p [100][100],int n){
	if(n==1)  //n=1返回矩陣的唯一數,停止遞歸
		return p[0][0];
	else{
		long long sum=0;
		for(int i=0;i<n;i++)
		{
			    int pp[100][100];//用於存放少一維的矩陣,爲方便直接定義爲100×100.
			for(int j=1,j1=0;j<n;j++)//去掉第一行
			{
				for(int k=0,k1=0;k<n;k++)
				{
					if(k==i)
						;//去掉對應的列
					else
					{

				     pp[j1][k1]=p[j][k];//pp爲餘子式	
					 k1++;
					}
				}
			    j1++;
			}
			if(i%2)
                sum+=(-1)*p[0][i]*mydet(pp,n-1);
			else
				sum+=p[0][i]*mydet(pp,n-1);
		}
		return sum;
	}
}

2.4  實現串行\OpenMP\MPI計算

我這裏的並行主要是放在第一次的按行展開那,具體實現看代碼吧。

2.4.1  串行代碼

/*************************************************************************
    > File Name: matrix_det.c
    > Author: surecheun
    > Mail: [email protected]
    > Created Time: 2017年12月06日 星期三 17時28分00秒
 ************************************************************************/
#include<stdlib.h>
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<time.h>
/*
 * 定義矩陣階數N
 */

int N;

/*
 * 定義一個全局矩陣
 */

int p[100][100];

/*
 * 用隨機數生成矩陣
 */

void create(){
	int i,j;
	for(i=0;i<N;i++)
	{
		for(j=0;j<N;j++)
	
	     {
           int a=rand()%15;//產生隨機數,並賦給數組
	       p[i][j]=a;
		 }
	}
}
      
/*
 * 輸出矩陣
 */

void print()
{
	int i,j;
	for(i=0;i<N;i++)
    {
		for(j=0;j<N;j++)
			printf("%d ",p[i][j]);
		printf("\n");
	}
}

/*
 * 計算行列式的函數
 */

long long mydet(int  p [100][100],int n){
	if(n==1)  //n=1返回矩陣的唯一數,停止遞歸
		return p[0][0];
	else{
		long long sum=0;
		for(int i=0;i<n;i++)
		{
			    int pp[100][100];//用於存放少一維的矩陣,爲方便直接定義爲100×100.
			for(int j=1,j1=0;j<n;j++)//去掉第一行
			{
				for(int k=0,k1=0;k<n;k++)
				{
					if(k==i)
						;//去掉對應的列
					else
					{

				     pp[j1][k1]=p[j][k];//pp爲餘子式	
					 k1++;
					}
				}
			    j1++;
			}
			if(i%2)
                sum+=(-1)*p[0][i]*mydet(pp,n-1);
			else
				sum+=p[0][i]*mydet(pp,n-1);
		}
		return sum;
	}
}

int main(){
	printf("N= ");
	scanf("%d",&N);
    while(N){       //如果輸入N就可以繼續算下去,這個設計主要爲了方便獲取時間數據來計算平均用時
		create();
		print();
		clock_t start_t=clock();  //開始計時
		printf("the sum of 串行 is %lld .\n",mydet(p,N));
	         clock_t  end_t=clock();  //結束計時
		double   runing_t =(double)(end_t-start_t)/CLOCKS_PER_SEC;
		printf("the runing time of 串行 is %f s.",runing_t);  //輸出時間
		printf("\n");
		printf("N= ");
		scanf("%d",&N);
	}
 
	return 0;
}

2.4.2 OpenMP代碼

/*************************************************************************
    > File Name: matrix_det_omp.c
    > Author: surecheun
    > Mail: [email protected]
    > Created Time: 2017年12月07日 星期四 17時23分51秒
 ************************************************************************/

#include<stdlib.h>
#include<stdio.h>
#include<math.h>
#include<vector>
#include<time.h>
#include<omp.h>

/*
 * 定義線程數
 */
#define n_threads 2

 /*
 *定義矩陣的階數爲全局變量
 */

int N;

/*
 * 定義一個全局矩陣
 */

int p[100][100];

/*
 * 用隨機數生成矩陣
 */

void create(){
	int i,j;
	for(i=0;i<N;i++)
	{
		for(j=0;j<N;j++)
	
	     {
           int a=rand()%15;//產生隨機數,並賦給數組
	       p[i][j]=a;
	     }
	}
}
      
/*
 * 輸出矩陣
 */

void print()
{
	int i,j;
	for(i=0;i<N;i++)
    {
		for(j=0;j<N;j++)
			printf("%d ",p[i][j]);
		printf("\n");
	}
}

/*
 * 計算行列式的函數
 */

long long mydet(int  p [100][100],int n){
	if(n==1)  //n=1返回矩陣的唯一數,停止遞歸
		return p[0][0];
	else{
		long long sum=0;
		for(int i=0;i<n;i++)
		{
		    int pp[100][100];
			for(int j=1,j1=0;j<n;j++)//去掉第一行
			{
				for(int k=0,k1=0;k<n;k++)
				{
					if(k==i)//去掉和改數相同的列
						;
					else
					{

				     pp[j1][k1]=p[j][k];	//pp爲代數餘子式
					 k1++;
					}
				}
			    j1++;
			}
			if(i%2)
                                          sum+=(-1)*p[0][i]*mydet(pp,n-1);
			else
				sum+=p[0][i]*mydet(pp,n-1);
		}
		return sum;
	}
}

 
int main(){
	printf("N= ");
	scanf("%d",&N);
    while(N){       //如果輸入的N>0,則繼續計算
		create();  //創建矩陣
		print();   //打印創建的矩陣
		  double start1,finish1;
                    start1=omp_get_wtime();  //開始計算時間
                    long long sum=0;
             omp_set_num_threads(n_threads);//設置線程數
           #pragma omp parallel for reduction(+:sum)//並行化
       for(int i=0;i<N;i++)
		{
		    int pp[100][100];
			for(int j=1,j1=0;j<N;j++)//去掉第一行
			{
				for(int k=0,k1=0;k<N;k++)
				{
					if(k==i)//去掉和i相同的列
						;
					else
					{

				     pp[j1][k1]=p[j][k];	//pp爲餘子式
					 k1++;
					}
				}
			    j1++;
			}
			if(i%2)
				sum+=(-1)*p[0][i]*mydet(pp,N-1);
			else
				sum+=p[0][i]*mydet(pp,N-1);
		}
        printf("the sum of omp is %lld .\n",sum);//輸出結果
	    finish1=omp_get_wtime();   //結束計算時間
		double   runing_t =finish1-start1;
		printf("the runing time of opm is %f s.",runing_t);//輸出時間
		printf("\n");
		printf("N= ");
		scanf("%d",&N);
	}
	return 0;
	}

2.4.3 MPI實現代碼

/*************************************************************************
    > File Name: matrix_det_mpi.c
    > Author: surecheun
    > Mail: [email protected]
    > Created Time: 2017年12月07日 星期四 16時24分03秒
 ************************************************************************/

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

/*
 *定義矩陣的階數爲全局變量
 */

int N;

/*
 * 定義一個全局矩陣
 */

int p[100][100];

/*
 * 用隨機數生成矩陣
 */

void create(){
	int i,j;
	for(i=0;i<N;i++)
	{
		for(j=0;j<N;j++)
	
	     {
           int a=rand()%15;//產生隨機數,並賦給數組
	       p[i][j]=a;
	     }
	}
}
      
/*
 * 輸出矩陣
 */

void print()
{
	int i,j;
	for(i=0;i<N;i++)
    {
		for(j=0;j<N;j++)
			printf("%d ",p[i][j]);
		printf("\n");
	}
}

/*
 * 計算行列式的函數
 */

long long mydet(int  p [100][100],int n){
	if(n==1)  //n=1返回矩陣的唯一數,停止遞歸
		return p[0][0];
	else{
		long long sum=0;
		for(int i=0;i<n;i++)
		{
		    int pp[100][100];
			for(int j=1,j1=0;j<n;j++)//去掉第一行
			{
				for(int k=0,k1=0;k<n;k++)
				{
					if(k==i)
						;
					else
					{

				     pp[j1][k1]=p[j][k];	
					 k1++;
					}
				}
			    j1++;
			}
			if(i%2)
                sum+=(-1)*p[0][i]*mydet(pp,n-1);
			else
				sum+=p[0][i]*mydet(pp,n-1);
		}
		return sum;
	}
}



int main(int argc,char *argv[]){  
	  scanf("%d",&N);
      int num_procs,my_rank;    
      double start = 0.0, stop = 0.0;  //記錄時間的變量
      long long  per_procs = 0.0;  //記錄每個進程算的和
      long long  result = 0.0;  //矩陣行列式結果
      MPI_Init(&argc, &argv);  
      MPI_Comm_size(MPI_COMM_WORLD, &num_procs);  
      MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);  
      if (my_rank == 0)
	  {//0號線程創建矩陣
        create(); 
        print(); //打印創建的矩陣
       }  
     start = MPI_Wtime();   //開始計算時間
 
      MPI_Bcast(&N, 1, MPI_INT, 0, MPI_COMM_WORLD);    //將矩陣大小廣播給所有進程  
      for (int i = 0; i <N; i++)
	  {  
      MPI_Bcast(p[i],N, MPI_INT, 0, MPI_COMM_WORLD);  
      }           //將矩陣廣播給所有進程  
   
      for (int i = my_rank; i <N; i += num_procs){ //每個線程處理不同的行和列 
            long long sum_i=0;
		    int pp[100][100];
			for(int j=1,j1=0;j<N;j++)//去掉第一行
			{
		    	for(int k=0,k1=0;k<N;k++)
				{
					if(k==i)
						;
					else
					{
				     pp[j1][k1]=p[j][k];	
					 k1++;
					}
				}
			    j1++;
			}
       if(i%2)
                sum_i=(-1)*p[0][i]*mydet(pp,N-1);
      else
		sum_i=p[0][i]*mydet(pp,N-1);
        per_procs += sum_i;  //記錄每個進程的和
    }  
    MPI_Reduce(&per_procs, &result, 1, MPI_LONG_LONG_INT, MPI_SUM, 0, MPI_COMM_WORLD);//在0號進程求總和  
    if (my_rank == 0){  
        printf("the sum of mpi is %lld .\n",result) ; 
        stop = MPI_Wtime();  //結束計算時間
        printf("the time of mpi is %f s\n", stop - start);  
        fflush(stdout);  
    }  
    MPI_Finalize();
    return 0;  
}  

4 實驗結果

4.1 正確性

4.1.1串行

結果分析,以 n=4爲例,輸出的矩陣爲:

13

1

12

10

8

10

1

12

9

1

2

7

5

4

8

1




輸出結果爲:3875

和matlab計算結果一致!

4.1.2 OpenMP

結果分析,以 n=4爲例,輸出的矩陣爲:

5

0

8

1

1

5

11

3

2

5

1

1

0

0

14

12







輸出結果爲:-2710

和matlab計算結果一致!

4.1.3 MPI

結果分析,以n=4爲例,輸出矩陣爲:

9

1

2

7

5

4

8

1

0

6

7

1

11

8

12

9







輸出結果爲:-202

和matlab計算結果一致!

4.2 加速比

通過多次求平均,得到三種計算實現方法的計算時間(保留 3 位有效數字)如下:

 

N(數)

串行

 

OpenMP

 

MPI

 

9

 

0.0239s

 

0.0117s

 

0.0117s

 

10

 

0.195s

 

0.105s

 

0.100s







柱狀圖如下:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章