1. 程式人生 > >[資料結構]稀疏矩陣乘法演算法實現

[資料結構]稀疏矩陣乘法演算法實現

作者zhonglihao
演算法名稀疏矩陣乘法 Sparse Matrix Multiplication
分類資料結構
複雜度O(n^2)
形式與資料結構C++程式碼 一維結構體儲存
特性極簡封裝 不使用連結串列 不需要轉置 計算過程容易理解
具體參考出處《演算法導論》(寫的不想看)
備註

// ConsoleApplication1.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"

//稀疏矩陣儲存結構體 第一個元素為矩陣頭,包含行列長度,元素總個數
typedef struct
{
	int row;
	int col;
	int element;
}sparse_mat;

void SparseMatrixRectPrint(sparse_mat* s_mat);
void SparseMatrixTriPrint(sparse_mat* s_mat);
sparse_mat* SparseMatrixMul(sparse_mat* s_mat_A, sparse_mat* s_mat_B);

int _tmain(int argc, _TCHAR* argv[])
{
	int i, j, k;
	const int mat_A_row = 4;
	const int mat_A_col = 4;
	const int mat_B_row = 4;
	const int mat_B_col = 4;

	//原矩陣
	int mat_A[mat_A_row][mat_A_col] = { 1, 1, 0, 0,
										0, 0, 1, 0, 
										0, 1, 0, 0, 
										0, 0, 1, 0 };

	int mat_B[mat_B_row][mat_B_col] = { 1, 0, 0, 0,
										0, 1, 0, 0, 
										0, 0, 1, 0, 
										0, 0, 0, 1 };

	//計算有效元素數量
	int mat_A_ele_count = 0;
	int mat_B_ele_count = 0;

	for (i = 0; i < mat_A_row; i++)
	{
		for (j = 0; j < mat_A_col; j++)
		{
			if (mat_A[i][j] != 0) mat_A_ele_count++;
		}
	}

	for (i = 0; i < mat_B_row; i++)
	{
		for (j = 0; j < mat_B_col; j++)
		{
			if (mat_B[i][j] != 0) mat_B_ele_count++;
		}
	}

	//動態分配
	sparse_mat* sparse_m_A = (sparse_mat*)malloc((mat_A_ele_count + 1)*sizeof(sparse_mat));
	sparse_mat* sparse_m_B = (sparse_mat*)malloc((mat_B_ele_count + 1)*sizeof(sparse_mat));

	//存入稀疏矩陣資訊
	sparse_m_A[0].row		= mat_A_row;
	sparse_m_A[0].col		= mat_A_col;
	sparse_m_A[0].element	= mat_A_ele_count;
	sparse_m_B[0].row		= mat_B_row;
	sparse_m_B[0].col		= mat_B_col;
	sparse_m_B[0].element	= mat_B_ele_count;

	for (i = 0, mat_A_ele_count = 0; i < mat_A_row; i++)
	{
		for (j = 0; j < mat_A_col; j++)
		{
			if (mat_A[i][j] != 0)
			{
				mat_A_ele_count++;
				sparse_m_A[mat_A_ele_count].element = mat_A[i][j];
				sparse_m_A[mat_A_ele_count].row = i;
				sparse_m_A[mat_A_ele_count].col = j;
			}
		}
	}

	for (i = 0, mat_B_ele_count = 0; i < mat_B_row; i++)
	{
		for (j = 0; j < mat_B_col; j++)
		{
			if (mat_B[i][j] != 0)
			{
				mat_B_ele_count++;
				sparse_m_B[mat_B_ele_count].element = mat_B[i][j];
				sparse_m_B[mat_B_ele_count].row = i;
				sparse_m_B[mat_B_ele_count].col = j;
			}
		}
	}

	//列印原陣列
	SparseMatrixRectPrint(sparse_m_A);
	SparseMatrixRectPrint(sparse_m_B);
	//SparseMatrixTriPrint(sparse_m_A); 
	//SparseMatrixTriPrint(sparse_m_B);

	//計算稀疏矩陣乘法
	sparse_mat* sparse_m_C = (sparse_mat*)SparseMatrixMul(sparse_m_A, sparse_m_B);
	SparseMatrixRectPrint(sparse_m_C);

	system("Pause");
	return 0;
}

//三元組稀疏矩陣乘法函式 極簡封裝 需要花費一點時間計算申請的記憶體 但是肯定比連結串列省空間啦
//Method Written By Zhonglihao
sparse_mat* SparseMatrixMul(sparse_mat* s_mat_A, sparse_mat* s_mat_B)
{
	int i, j, k;
	int s_mat_C_row			= s_mat_A[0].row;
	int s_mat_C_col			= s_mat_B[0].col;
	int s_mat_A_ele_count	= s_mat_A[0].element;
	int s_mat_B_ele_count   = s_mat_B[0].element;

	//判斷是否能夠相乘 或 有一個全為0 那就不用乘啦
	if (s_mat_A[0].col != s_mat_B[0].row) return NULL;
	if (s_mat_A_ele_count == 0 || s_mat_B_ele_count == 0)
	{
		sparse_mat* s_mat_C	= (sparse_mat*)malloc((1)*sizeof(sparse_mat));
		s_mat_C[0].row		= s_mat_C_row;
		s_mat_C[0].col		= s_mat_C_col;
		s_mat_C[0].element	= 0;
		return s_mat_C;
	}

	//申請一個長度為B列寬的快取 兩個用途 計算輸出大小時做列封禁,計算相乘時做和快取
	int* col_buffer = (int*)malloc(s_mat_C_col*sizeof(int));
	//清空快取區
	for (k = 0; k < s_mat_C_col; k++) col_buffer[k] = 0;

	//判斷需要輸出的三元大小申請記憶體
	int malloc_element_count = 0;
	for (i = 1; i <= s_mat_A_ele_count; i++)
	{
		if (i >= 2 && s_mat_A[i].row != s_mat_A[i - 1].row) //換行解禁
		{
			for (k = 0; k < s_mat_C_col; k++) col_buffer[k] = 0;
		}

		for (j = 1; j <= s_mat_B_ele_count; j++)
		{
			if ((s_mat_A[i].col == s_mat_B[j].row) && col_buffer[s_mat_B[j].col] != 1)//沒有列封禁
			{
				col_buffer[s_mat_B[j].col] = 1;//列封禁
				malloc_element_count++;
			}
		}
	}

	sparse_mat* s_mat_C		= (sparse_mat*)malloc((malloc_element_count + 1)*sizeof(sparse_mat));
	s_mat_C[0].row			= s_mat_C_row;
	s_mat_C[0].col			= s_mat_C_col;
	s_mat_C[0].element		= malloc_element_count;
	int s_mat_C_ele_count	= 0;//用於存入元素時做指標

	//開始進行乘法相乘
	for (k = 0; k < s_mat_C_col; k++) col_buffer[k] = 0;//清理列快取
	for (i = 1; i <= s_mat_A_ele_count; i++)
	{
		for (j = 1; j <= s_mat_B_ele_count; j++)
		{
			if (s_mat_A[i].col == s_mat_B[j].row)//有效用 壓入快取區
				col_buffer[s_mat_B[j].col] += s_mat_A[i].element * s_mat_B[j].element;
		}

		//如果要換行或者是最後一行
		if (((i != s_mat_A_ele_count) && (s_mat_A[i].row != s_mat_A[i + 1].row)) || i == s_mat_A_ele_count)
		{
			//掃描快取組
			for (k = 0; k < s_mat_C_col; k++)
			{
				//如果該點不是0 壓入三元組 清零快取
				if (col_buffer[k] != 0)
				{
					s_mat_C_ele_count++;
					s_mat_C[s_mat_C_ele_count].row = s_mat_A[i].row;
					s_mat_C[s_mat_C_ele_count].col = k;
					s_mat_C[s_mat_C_ele_count].element = col_buffer[k];
					col_buffer[k] = 0;
				}
			}
		}
	}

	//釋放快取 返回結果
	free(col_buffer);
	return s_mat_C;
}

//稀疏矩陣列印 按矩形列印 需要確定三元組按Z排列有序
void SparseMatrixRectPrint(sparse_mat* s_mat)
{
	//獲取行列資訊
	int i, j;
	int row = s_mat[0].row;
	int col = s_mat[0].col;

	//列印元素遞增 前提是三元組按照行列順序排好,就只需要遞增下標
	int ele_count = 1;

	//按矩陣掃描列印
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (i == s_mat[ele_count].row && j == s_mat[ele_count].col)
			{
				printf("%d\t", s_mat[ele_count].element);
				ele_count++;
			}
			else
			{
				printf("0\t");
			}
		}//for
		printf("\n");
	}//for
	
	//跳空換行 返回
	printf("\n");
	return;
}

//稀疏矩陣列印 按三元組結構列印
void SparseMatrixTriPrint(sparse_mat* s_mat)
{
	int i, j;
	int ele_count = s_mat[0].element;

	//按順序列印
	for (i = 1; i <= ele_count; i++)
	{
		printf("%d\t%d\t%d\n", s_mat[i].row, s_mat[i].col, s_mat[i].element);
	}

	//跳空換行 返回
	printf("\n");
	return;
}