1. 程式人生 > >5.3矩陣的壓縮儲存(稀疏矩陣轉置和快速轉置)

5.3矩陣的壓縮儲存(稀疏矩陣轉置和快速轉置)

在矩陣中有許多值相同的元素或者是零元素。有時為了節省儲存空間,可以對這類矩陣進行壓縮儲存。所謂的壓縮儲存是指:為多個值相同的元值分配一個儲存空間;對零元不分配空間。

5.32稀疏矩陣

在m*n的矩陣中,有t個元素不為零。零α=t/m*n,稱 α為矩陣的稀疏因子。通常認為α<=0.05時稱為稀疏矩陣。

對於稀疏矩陣的非零元我們有下面這個表示:

如:(,(1,2,12),(1,3,9),(3,1,-3),(3,6,14),(4,3,24),(5,2,18),(6,1,15),(6,4,-7))

如下圖所示:


下面我們來看下三元組的結構體:

下面是書中程式碼(嚴蔚敏版的資料結構)

#define  MAXSIZE 12500	//非零元素個數的最大值為12500
typedef struct{
	int i, j;	//該非零元的行下標和列下標
	ElemType e;
}Triple;

typedef struct{
	Triple data[MAXSIZE + 1];	//非零元三元組表,data[0]未用
	int mu, nu, tu;	//矩陣的行數、列數和非零元個數
}TSMatrix;
分析下:

這裡的思路和我們在連結串列上看到的有相似之處,結點變成了非零元素,線性表程式設計了非零元三元組的表。但多出了矩陣的行數,列數和非零元個數

學過線性代數的都知道,轉置運算是一種最簡單的矩陣運算。

對應一個m*n的矩陣M它的轉置矩陣是T,如下圖所示:


P·S:所謂的置換就是行和列進行交換,也就是關於主對角線對稱(矩陣左上角到右下角的線稱為主對角線)

而在程式裡面,他將會是下面這張圖這樣:


這裡的i對應行,j對應列,v表示值。

從分析a和b之間的差異可見只要做到:

1.將矩陣的行列值相互轉換。

2.將每個三元組中的i和j交換。

3.重排三元組之間的次序便可實現矩陣的轉置。

下面是書中給我們提供的虛擬碼:

Status TransposeSMatrix(TSMatrix M, TSMatrix &T) 
{ 
	// 採用三元組順序表儲存表示,求稀疏矩陣M的轉置矩陣T
	int p, q, col;
	T.mu = M.nu;  T.nu = M.mu;  T.tu = M.tu;
	if (T.tu) 
	{
		q = 1;
		for (col = 1; col <= M.nu; ++col)
		for (p = 1; p <= M.tu; ++p)
		if (M.data[p].j == col)
		{
			T.data[q].i = M.data[p].j; 
			T.data[q].j = M.data[p].i;
			T.data[q].e = M.data[p].e;  
			++q;
		}
	}
	return Ok;
} // TransposeSMatrix
下面來分析下:

col表示列,從M矩陣的第一列開始。比如col=1時,他先檢索M的第一列,把非零元中第一列的換成T中的第一行,就這個思路。

下面是矩陣的快速轉置方法:

原理是:如果能預先確定矩陣M中每一列(即T中每一行)的第一個非零元在b.data中(上面那圖是b.data)恰當位置。那麼在對a.data中的三元組一次做轉置時,便可直接放到b.data中恰當的位置上去。

設兩個向量:num和cpot

num[col]表示矩陣M中第col列中的非零元素個數。

cpot[col]指M中第col列的第一個非零元在b.data中的恰當位置。

有下面兩個公式:

cpot[1]=1;

cpot[col]=copt[col-1]+num[col-1] 2<=col<=a.nu

圖如下:


下面來分析下這個表:

cpot[1]=1.

cpot[2]=num[1]+cpot[1]=1+2=3

cpot[3]=num[2]+cpot[2]=2+3=5

cpot[4]=num[3]+cpot[3]=2+5=7

cpot[5]=num[4]+cpot[4]=1+7=8

cpot[6]=num[5]+cpot[5]=0+8=8

cpot[7]=num[6]+cpot[6]=1+8=9

這個表就是這麼填的,但是在程式碼裡面就不一樣了。我這裡先提一下,程式碼裡面有覆蓋和範圍這種概念,這是什麼意思,意思就是,大家看cpot[5]和cpot[6]都是8,那麼在最後,他只會保留cpot[6],而cpot[7]他這裡是9,但本身就只有8個元素,哪來第九個呢?所以這個cpot[9]在程式裡面是沒有用的。

下面是書中程式碼:
Status  FastTransposeSMatrix(TSMatrix M, TSMatrix &T) 
{ 
	// 採用三元組順序表儲存表示,求稀疏矩陣M的轉置矩陣T
	int col, t, p, q;
	int num[20], cpot[20];
	T.mu = M.nu;  T.nu = M.mu;  T.tu = M.tu;
	if (T.tu) {
		for (col = 1; col <= M.nu; ++col)	//對列數進行初始化
			num[col] = 0;
		for (t = 1; t <= M.tu; ++t) // 求 M 中每一列所含非零元的個數
			++num[M.data[t].j];
		cpot[1] = 1;
		// 求 M 中每一列的第一個非零元在 b.data 中的序號
		for (col = 2; col <= M.nu; ++col) 
			cpot[col] = cpot[col - 1] + num[col - 1];
		for (p = 1; p <= M.tu; ++p) 
		{
			col = M.data[p].j;   
			q = cpot[col];
			T.data[q].i = M.data[p].j;  
			T.data[q].j = M.data[p].i;
			T.data[q].e = M.data[p].e;  
			++cpot[col];
		} // for
	} // if
	return OK;
} // FastTransposeSMatrix
分析如下:

這個程式的關鍵就是他只用了一個for迴圈,而上面那個程式用了兩個for迴圈,這使得時間複雜度降低了。這個for(p=1;p<M.tu;++p).這個就是我剛剛在上表說的那個意思。這裡有個++cpot[col]這是個關鍵

現在來解釋下++cpot[col]:

我們可以看到上表中cpot[col]只有1,3,5,7,8而2,4,5沒有,所以用了這個++cpot[col]後他就把每一列的第一個元素移到了第二個。

如果還有同學不懂,下面我給出全部的程式碼。

不懂的同學單步除錯下。

#include <stdio.h>
#include <windows.h>
#define MAXSIZE 1250  

#define    OK      1  
#define    ERROR   0  
#define    TRUE    1  
#define    FLASE   0  

typedef    int     Status;
typedef    int     ElemType;

typedef struct{
	int   i, j;       //該非零元的行下標和列下標  
	ElemType e;       //非零元對應的值  
}Triple;

typedef struct{
	Triple   data[MAXSIZE + 1];       //非零元三元組表,data[0]未用  
	int      mu, nu, tu;            //矩陣的行數,列數,非零元個數  
}TSMatrix;

Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T)              //快速轉置  
{                                                      //採用三元組順序表儲存表示,求稀疏矩陣M的轉置矩陣T  
	T.mu = M.nu;
	T.nu = M.mu;
	T.tu = M.tu;
	if (T.tu)
	{
		int col;
		int num[100], cpot[100];
		for (col = 1; col <= M.nu; ++col)
			num[col] = 0;                 //num陣列的初始化  
		for (int t = 1; t <= M.tu; ++t)
			++num[M.data[t].j];         //求M中每一列含有的非零元個數  
		cpot[1] = 1;
		for (col = 2; col <= M.nu; ++col)
			cpot[col] = cpot[col - 1] + num[col - 1];         //求cpot向量  
		int q;
		for (int p = 1; p <= M.tu; ++p)
		{
			col = M.data[p].j;
			q = cpot[col];
			T.data[q].i = M.data[p].j;
			T.data[q].j = M.data[p].i;
			T.data[q].e = M.data[p].e;
			++cpot[col];
		}//for  
	}//if  
	return OK;
}//FastTransposeSMatrix  

Status main()
{
	TSMatrix M;
	TSMatrix T;
	printf("請輸入原矩陣:\n");
	printf("行數、列數: ");
	scanf_s("%d%d", &M.mu, &M.nu);
	printf("元素總數: ");
	scanf_s("%d", &M.tu);
	printf("輸入各個對應壓縮值:\n");
	for (int i = 1; i <= M.tu; ++i)
		scanf_s("%d%d%d", &M.data[i].i, &M.data[i].j, &M.data[i].e);

	FastTransposeSMatrix(M, T);

	printf("轉置後行數、列數、元素總數非別為:\n%d     %d     %d\n\n", T.mu, T.nu, T.tu);
	printf("值為:\n");
	for (int t = 1; t <= T.tu; ++t)
		printf("%d     %d     %d\n", T.data[t].i, T.data[t].j, T.data[t].e);
	system("pause");
	return OK;
}

執行結果如下:


和下面這圖是不是一模一樣