最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++實(shí)現(xiàn)特殊矩陣的壓縮存儲算法

 更新時間:2022年08月16日 08:38:21   作者:一枚大果殼  
在實(shí)際存儲時,會發(fā)現(xiàn)矩陣中有許多值相同的數(shù)據(jù)或有許多零數(shù)據(jù),且分布呈現(xiàn)出一定的規(guī)律,稱這類型的矩陣為特殊矩陣。本文將利用C++實(shí)現(xiàn)特殊矩陣的壓縮存儲,感興趣的可以了解一下

1. 前言

什么是特殊矩陣?

C++,一般使用二維數(shù)組存儲矩陣數(shù)據(jù)。

在實(shí)際存儲時,會發(fā)現(xiàn)矩陣中有許多值相同的數(shù)據(jù)或有許多零數(shù)據(jù),且分布呈現(xiàn)出一定的規(guī)律,稱這類型的矩陣為特殊矩陣。

為了節(jié)省存儲空間,可以設(shè)計算法,對這類特殊矩陣進(jìn)行壓縮存儲,讓多個相同的非零數(shù)據(jù)只分配一個存儲空間;對零數(shù)據(jù)不分配空間。

本文將講解如何壓縮這類特殊矩陣,以及壓縮后如何保證矩陣的常規(guī)操作不受影響。

2. 壓縮對稱矩陣

什么是對稱矩陣?

在一個n階矩陣A中,若所有數(shù)據(jù)滿足如下述特性,則可稱A為對稱矩陣。

a[i][j]==a[j][i]

i是數(shù)據(jù)在矩陣中的行號。

j是數(shù)據(jù)在矩陣中的列號。

0<<i,j<<n-1

n階對稱矩陣 a[i][j]中,當(dāng)i==j(行號和列號相同)時所有元素所構(gòu)建成的集合稱為主對角線。

如下圖所示:

對稱矩陣以主對角線為分界線,把整個矩陣分成 2 個三角區(qū)域,主對角線之上的稱為上三角,主對角線之下的區(qū)域稱為下三角。

對稱矩陣的上三角和下三角區(qū)域中的元素是相同的,以n行n列的二維數(shù)組存儲時,會浪費(fèi)近一半的空間,可以采壓縮機(jī)制,將 二維數(shù)組中的數(shù)據(jù)壓縮存儲在一個一維數(shù)組中,這個過程也稱為數(shù)據(jù)線性化。

線性過程時,一維數(shù)組的空間需要多大?

n階矩陣,使用二維數(shù)組存儲,理論上所需要的存儲單元應(yīng)該是 n2。

對稱矩陣以主對角線為分界線,上三角和下三角區(qū)域中的數(shù)據(jù)是相同的。注意,主對角線上的元素是需要單獨(dú)存儲的,主對角線上的數(shù)據(jù)個數(shù)為 n。

真正需要的存儲單元應(yīng)該:(理論上所需要的存儲單元-主對角線上的數(shù)據(jù)所需單元) / 2 +主對角線上的數(shù)據(jù)所需單元。

如下表達(dá)式所述:

(n2-n)/2+n=n(n+1)/2

所以,可以把n階矩陣中的數(shù)據(jù)可以全部壓縮在長度為 n(n+1)/2 的一維數(shù)組中,能節(jié)約近一半的存儲空間。并且n階矩陣和一維數(shù)組之間滿足如下的位置對應(yīng)關(guān)系:

i>=j 表示矩陣中的下三角區(qū)域(包含主對角線上數(shù)據(jù))。

i<j表示矩陣中的上三角區(qū)域。

轉(zhuǎn)存實(shí)現(xiàn):

#include <iostream>
using namespace std;
int main(int argc, char** argv) {
	//對稱矩陣
	int nums[4][4]= { {3,5,6,8},{5,4,7,9},{6,7,12,10},{8,9,10,13} };
	//一維數(shù)組,根據(jù)上述公式,一維數(shù)組長度為 4*(4+1)/2=10
	int zipNums[10]= {0};
	for(int i=0; i<4; i++) {
		for(int j=0; j<4; j++) {
			if (i>=j) {
				zipNums[ i*(i+1)/2+j]=nums[i][j];
			} else {
				zipNums[ j*(j+1)/2+i]=nums[i][j];
			}
		}
	}
	for(int i=0; i<10; i++) {
		cout<<zipNums[i]<<"\t";
	}
	return 0;
}

如上是二維數(shù)組壓縮到一維數(shù)組后的結(jié)果。

3. 壓縮稀疏矩陣

什么是稀疏矩陣?

如果矩陣A中的有效數(shù)據(jù)的數(shù)量遠(yuǎn)遠(yuǎn)小于矩陣實(shí)際能描述的數(shù)據(jù)的總數(shù),則稱A為稀疏矩陣。

現(xiàn)假設(shè)有 mn列的矩陣,其中所保存的元素個數(shù)為 c,則稀疏因子為:e=c/(m*n)。當(dāng)用二維數(shù)組存儲稀疏矩陣中數(shù)據(jù)時,僅有少部分空間被利用,可以采用壓縮機(jī)制來進(jìn)行存儲。

稀疏因子越小,表示有效數(shù)據(jù)越少。

稀疏矩陣中的非零數(shù)據(jù)的存儲位置是沒有規(guī)律的,在壓縮存儲時,除了需要記錄非零數(shù)據(jù)本身外還需要記錄其位置信息。所以需要一個三元組對象(i,j,a[i][j])對數(shù)據(jù)進(jìn)行唯一性確定。

3.1 三元組表

為了便于描述,壓縮前的矩陣稱為原稀疏矩陣,壓縮后的稀疏矩陣稱三元組表矩陣。

原稀疏矩陣也好,三元組表矩陣也好。只要頂著矩陣這個概念,就應(yīng)該能進(jìn)行矩陣相應(yīng)的操作。矩陣的內(nèi)置操作有很多,本文選擇矩陣的轉(zhuǎn)置操作來對比壓縮前和壓縮后的算法差異性。

什么是矩陣轉(zhuǎn)置?

如有 mn列的A 矩陣,所謂轉(zhuǎn)置,指把A變成 nm列的 B矩陣。AB滿足 A[i][j]=B[j][i]。即A的行變成B的列。如下圖所示:

A稀疏矩陣轉(zhuǎn)置成B稀疏矩陣的原生實(shí)現(xiàn):

//原矩陣
int aArray[4][5]= {{0,5,0,1,0},{0,0,3,0,0},{0,7,0,0,0},{0,0,9,0,0}};
//轉(zhuǎn)置后矩陣
int bArray[5][4];
//轉(zhuǎn)置算法 
for(int row=0; row<4; row++) {
	for(int col=0; col<5; col++) {
		bArray[col][row]=aArray[row][col];
	}
}

基于原生矩陣上的轉(zhuǎn)置算法,其時間復(fù)雜度為 O(m*n),即O(n2)。

從存儲角度而言,aArray矩陣和其轉(zhuǎn)置后的bArray矩陣都是稀疏矩陣,使用二維數(shù)組存儲會浪費(fèi)大量的空間。有必要對其以三元組表的形式進(jìn)行壓縮存儲。

三元組表是一個一維數(shù)組,因其中的每一個存儲位置需要存儲原稀疏矩陣中非零數(shù)據(jù)的3 個信息(行,列,值)。三元組表名由此而來,也就是說數(shù)組中存儲的是對象。

先來一個圖示,直觀上了解一下A稀疏矩陣壓縮前后的差異性。

壓縮算法實(shí)現(xiàn):

#include <iostream>
using namespace std;
typedef int DataType;
#define maxSize 100
//三元組結(jié)構(gòu)
struct Node {
	//行號
	int row=-1;
	//列號
	int col=-1;
	//非零元素的值
	DataType val=0;
} ;

//維護(hù)三元組表的類
class Matrix {
	private:
		//位置編號
		int idx=0;
		//壓縮前稀疏矩陣的行數(shù)
		int rows;
		//壓縮前稀疏矩陣的列數(shù)
		int cols;
		//原稀疏矩陣中非零數(shù)據(jù)的個數(shù)
		int terms;
		//壓縮存儲的一維數(shù)組,初始化
		Node node;
		Node data[maxSize]= {node};
	public:
		//構(gòu)造函數(shù)
		Matrix(int row,int col) {
			this->rows=row;
			this->cols=col;
             this->terms=0;
		}
		//存儲三元結(jié)點(diǎn)
		void setData(int row ,int col,int val) {
			Node n;
			n.row=row;
			n.col=col;
			n.val=val;
			this->data[idx++]=n;
             //記錄非零數(shù)據(jù)的數(shù)量
             this->terms++;
		}
        //重載上面函數(shù)
    	void setData(int index,int row ,int col,int val) {
			Node n;
			n.row=row;
			n.col=col;
			n.val=val;
			this->data[index]=n;
			this->terms++;
		}
		//顯示三無組表中的數(shù)據(jù)
		void showInfo() {
			for(int i=0; i<maxSize; i++ ) {
				if(data[i].val==0)break;
				cout<<data[i].row<<"\t"<<data[i].col<<"\t"<<data[i].val<<endl;
			}
		}
		//基于三元組表的轉(zhuǎn)置算法
		Matrix transMatrix();
};

int main(int argc, char** argv) {
	//原稀疏矩陣
	int aArray[4][5]= {{0,5,0,1,0},{0,0,3,0,0},{0,7,0,0,0},{0,0,9,0,0}};
	//實(shí)例化
	Matrix matrix(4,5);
	//壓縮矩陣
	for(int row=0; row<4; row++) {
		for(int col=0; col<5; col++) {
			if (aArray[row][col]!=0) {
                 //轉(zhuǎn)存至三元組表中
				matrix.setData(row,col,aArray[row][col]);
			}
		}
	}
	matrix.showInfo();
	return 0;
}

代碼執(zhí)行后的結(jié)果和直觀圖示結(jié)果一致:

壓縮之后,則要思考,如何在A三元組表的基礎(chǔ)上直接實(shí)現(xiàn)矩陣的轉(zhuǎn)置?;蛘哒f ,轉(zhuǎn)置后的矩陣還是使用三元組表方式描述。

先從直觀上了解一下,轉(zhuǎn)置后的B矩稀疏陣的三元組表的結(jié)構(gòu)應(yīng)該是什么樣子。

是否可以通過直接交換A的三元組表中行和列位置中的值?至于可不可以,可以先用演示圖推演一下:

從圖示可知,如果僅是交換A三元組表的行和列位置后得到的新三元組表并不和前面所推演出現(xiàn)的B三元組表一致。

如果仔細(xì)觀察,可發(fā)現(xiàn)得到的新三元組表的是對原B稀疏表以列優(yōu)先遍歷后的結(jié)果。

B稀疏矩陣的三元組表顯然應(yīng)該是以行優(yōu)先遍歷的結(jié)果。

3.2 以列優(yōu)先搜索

經(jīng)過轉(zhuǎn)置后,A稀疏矩陣的行會變成B稀疏矩陣的列,也可以說A的列變成B的行。如果在A中以列優(yōu)先搜索,則相當(dāng)于在B中以行優(yōu)先進(jìn)行搜索。可利用這個簡單而又令人興奮的道理實(shí)現(xiàn)基于三元組表的轉(zhuǎn)置。

Matrix Matrix::transMatrix(){
	//轉(zhuǎn)置后的三元組表對象
	Matrix bMatrix(this->cols,this->rows);
	//對原稀疏矩陣以列優(yōu)先搜索
	for(int c=0;c<this->cols;c++){
		//在對應(yīng)的三元組表上查找此列上是否有非零數(shù)據(jù)
	 	for(int j=0;j<this->terms;j++ ){
	 		if(this->data[j].col==c){
	 			//如果此列上有數(shù)據(jù),則轉(zhuǎn)置并保存
				bMatrix.setData(this->data[j].col,this->data[j].row,this->data[j].val);  
			 }
		 } 
	}
	return  bMatrix;
}

測試代碼:

int main(int argc, char** argv) {
//原稀疏矩陣
int aArray[4][5]= {{0,5,0,1,0},{0,0,3,0,0},{0,7,0,0,0},{0,0,9,0,0}};
//實(shí)例化壓縮矩陣
Matrix matrix(4,5);
//壓縮矩陣
for(int row=0; row<4; row++) {
	for(int col=0; col<5; col++) {
		if (aArray[row][col]!=0) {
			matrix.setData(row,col,aArray[row][col]);
		}
	}
}
cout<<"顯示 A 稀疏矩陣壓縮后的結(jié)果:"<<endl; 
matrix.showInfo();
cout<<"在A的三元組表的基礎(chǔ)上轉(zhuǎn)置后的結(jié)果:"<<endl;
Matrix bMatrix= matrix.transMatrix();
bMatrix.showInfo(); 	 
return 0;
}

輸出結(jié)果:

代碼執(zhí)行后輸出的結(jié)果,和前文推演出來的結(jié)果是一樣的。

前文可知,基于原生稀疏矩陣上的轉(zhuǎn)置時間復(fù)雜度為 O(m*n)?;谌M表的 時間復(fù)雜度=稀疏矩陣的列數(shù)乘以稀疏矩陣中非零數(shù)據(jù)的個數(shù)。當(dāng)稀疏矩陣中的元素個數(shù)為n*m時,則上述的時間復(fù)雜度會變成 O(m*n2)。

3.3 找出存儲位置

上述算法適合于當(dāng)稀疏因子較小時,當(dāng)矩陣中的非零數(shù)據(jù)較多時,時間復(fù)雜度會較高??梢栽谏鲜隽袃?yōu)先搜索的算法基礎(chǔ)上進(jìn)行優(yōu)化。

其核心思路如下所述:

  • 在原A稀疏矩陣中按列優(yōu)先進(jìn)行搜索。
  • 統(tǒng)計每一列中非零數(shù)據(jù)的個數(shù)。
  • 記錄每一列中第一個非零數(shù)據(jù)在B三元組表中的位置。

對A稀疏矩陣按列遍歷時,可以發(fā)現(xiàn),掃描時,數(shù)據(jù)出現(xiàn)的順序和其在B三元組表中的存儲順序是一致的。

如果在遍歷時,能記錄每列非零數(shù)據(jù)在B三元組表中應(yīng)該存儲的位置,則可以實(shí)現(xiàn)A三元組表中的數(shù)據(jù)直接以轉(zhuǎn)置要求存儲在B三元組表中。

重寫上述的轉(zhuǎn)置函數(shù)。

Matrix Matrix::transMatrix() {
	//保存轉(zhuǎn)置后數(shù)據(jù)的壓縮矩陣
	Matrix bMatrix(this->cols,this->rows);
	//初始化數(shù)組,用來保存A稀疏矩陣中第一列中非零數(shù)據(jù)的個數(shù)
	int counts[this->cols]= {0};
	//計算每一列中非零數(shù)據(jù)個數(shù)
	for(int i=0; i<this->terms; i++)
		counts[this->data[i].col]++;
	//初始化數(shù)組,用來保存A稀疏矩陣每列中非零數(shù)據(jù)在B三元組表中的起始位置
	int position[this->cols]= {0};	
	for(int i=1;i<this->cols;i++ ){
        //上一列的起始位置加上上一列非零數(shù)據(jù)的個數(shù)
		position[i]=position[i-1]+counts[i-1];
	}
    //轉(zhuǎn)置A三元組表
    for(int i=0;i<this->terms;i++){ 
    	int col=this->data[i].col;
    	int row=this->data[i].row;
    	int val=this->data[i].val;
    	//找到在B三元組中的起始存儲位置
		int pos=position[col];
		bMatrix.setData(pos,col,row,val);
		position[col]++;	
	} 
	return  bMatrix;
}

測試代碼不需要任何變化:

int main(int argc, char** argv) {
	//原稀疏矩陣
	int aArray[4][5]= {{0,5,0,1,0},{0,0,3,0,0},{0,7,0,0,0},{0,0,9,0,0}};
	//實(shí)例化壓縮矩陣
	Matrix matrix(4,5);
	//壓縮矩陣
	for(int row=0; row<4; row++) {
		for(int col=0; col<5; col++) {
			if (aArray[row][col]!=0) {
				matrix.setData(row,col,aArray[row][col]);
			}
		}
	}
	cout<<"顯示 A 稀疏矩陣壓縮后的結(jié)果:"<<endl;
	matrix.showInfo();
	cout<<"在A的三元組表的基礎(chǔ)上轉(zhuǎn)置后的結(jié)果:"<<endl;
	Matrix bMatrix= matrix.transMatrix();
	bMatrix.showInfo();
	return 0;
}

輸出結(jié)果:

上述 2 種轉(zhuǎn)置算法,其本質(zhì)是一樣的,第一種方案更容易理解,第二種方案在第一種方案的基礎(chǔ)上用空間換取了時間上性能的提升。

4. 總結(jié)

使用二維數(shù)組存儲矩陣中數(shù)據(jù)時,如果矩陣中的有效數(shù)據(jù)較小時,可以采用壓縮的方式對其進(jìn)行存儲。

本文著重講解如何使用三元組表方式壓縮存儲稀疏矩陣。轉(zhuǎn)存過程并不難,難點(diǎn)在于轉(zhuǎn)存為三元組表后,如何在三元組表的基礎(chǔ)上正常進(jìn)行矩陣相關(guān)操作。

以上就是C++實(shí)現(xiàn)特殊矩陣的壓縮存儲算法的詳細(xì)內(nèi)容,更多關(guān)于C++矩陣壓縮存儲的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vs2022環(huán)境下安裝低版本.net framework的實(shí)現(xiàn)步驟

    Vs2022環(huán)境下安裝低版本.net framework的實(shí)現(xiàn)步驟

    本文主要介紹了Vs2022環(huán)境下安裝低版本.net framework的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • C語言sizeof與字符串處理與動態(tài)內(nèi)存分配及main函數(shù)參數(shù)詳解

    C語言sizeof與字符串處理與動態(tài)內(nèi)存分配及main函數(shù)參數(shù)詳解

    這篇文章主要介紹了C語言字符串處理函數(shù)、sizeof、動態(tài)內(nèi)存分配函數(shù)、main函數(shù)參數(shù)問題,static在修飾變量的時候,如果是修飾全局變量,則跟全局變量功能一樣,通過示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • C語言實(shí)現(xiàn)一個簡易通訊錄

    C語言實(shí)現(xiàn)一個簡易通訊錄

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)一個簡易通訊錄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • 如何區(qū)分C++中的inline和#define宏

    如何區(qū)分C++中的inline和#define宏

    這篇文章主要介紹了如何區(qū)分C++中的inline和#define宏,文中講解非常詳細(xì),代碼幫助大家更好的參考和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • 深入C++拷貝構(gòu)造函數(shù)的總結(jié)詳解

    深入C++拷貝構(gòu)造函數(shù)的總結(jié)詳解

    本篇文章是對C++中拷貝構(gòu)造函數(shù)進(jìn)行了總結(jié)與介紹。需要的朋友參考下
    2013-05-05
  • C++學(xué)習(xí)筆記之初始化列表

    C++學(xué)習(xí)筆記之初始化列表

    初始化列表是類中構(gòu)造函數(shù)的一部分,用于實(shí)例化類中變量時賦初值,下面這篇文章主要給大家介紹了關(guān)于C++學(xué)習(xí)筆記之初始化列表的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • c語言實(shí)現(xiàn)http下載器的方法

    c語言實(shí)現(xiàn)http下載器的方法

    這篇文章主要介紹了c語言實(shí)現(xiàn)http下載器的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-07-07
  • 從匯編看c++中引用與指針的使用分析

    從匯編看c++中引用與指針的使用分析

    在c++中,引用和指針具有相同的作用,都可以用來在函數(shù)里面給變函數(shù)外面對象或者變量的值,下面就來看他們的原理
    2013-05-05
  • C++數(shù)據(jù)結(jié)構(gòu)之紅黑樹的實(shí)現(xiàn)

    C++數(shù)據(jù)結(jié)構(gòu)之紅黑樹的實(shí)現(xiàn)

    紅黑樹在表意上就是一棵每個節(jié)點(diǎn)帶有顏色的二叉搜索樹,并通過對節(jié)點(diǎn)顏色的控制,使該二叉搜索樹達(dá)到盡量平衡的狀態(tài)。本文主要為大家介紹了C++中紅黑樹的原理及實(shí)現(xiàn),需要的可以參考一下
    2022-08-08
  • Qt編程實(shí)現(xiàn)小時鐘

    Qt編程實(shí)現(xiàn)小時鐘

    這篇文章主要為大家詳細(xì)介紹了Qt編程實(shí)現(xiàn)小時鐘,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05

最新評論

泰宁县| 绥棱县| 济源市| 溧阳市| 南皮县| 武川县| 南华县| 如东县| 瑞丽市| 雅江县| 尚志市| 富蕴县| 北碚区| 柏乡县| 宁强县| 丹巴县| 辽阳县| 惠安县| 雷州市| 体育| 抚州市| 吉安县| 蓝山县| 尉氏县| 九寨沟县| 周口市| 保定市| 四子王旗| 滦平县| 邢台县| 大同市| 隆林| 桐城市| 茌平县| 利川市| 昌吉市| 任丘市| 江油市| 福清市| 呼玛县| 广西|