矩陣-----對稱矩陣及其壓縮存儲&&稀疏矩陣

什麼是對稱矩陣(SymmetricMatrix)?

對稱對稱-------看

設一個N*N的方陣A,A中任意元素Aij,當且僅當Aij == Aji(0 <= i <= N-1 && 0 <= j <= N-1),則矩陣A是對稱矩陣。以矩陣的對角線爲分隔,分爲上三角和下三角。


壓縮存就是矩陣存儲時只需要存儲上三角/下三角的數據,所以最多存儲n(n+1)/2個數據。

對稱矩陣和壓縮存儲的對應關係:下三角存儲i>=j,  SymmetricMatrix[i][j] == Array[i*(i+1)/2+j]

wKiom1cUrRbTJ4_iAAAIZYb5ioo408.png

那麼,我們該如何存儲呢?

按照一元數組的方法,存儲下三角的元素即可。

template<class T>
class SymmetricMatrix
{
public:
	SymmetricMatrix(T* a, size_t size, size_t n)
		:_data(new T[n*(n + 1) / 2])    //開闢好數組一半大小的空間
		, _size(size)
		, _n(n)
	{
		size_t index = 0;
		for (size_t i = 0; i < _n; i++)
		{
			for (size_t j = 0; j < _n; j++)
			{
				if (i >= j)                    //下三角元素
				{
					_data[index++] = a[i*n + j];
				}
				else
				{
					break;
				}
			}
		}
	}
public:
	void Display()
	{
		size_t index = 0;
		for (size_t i = 0; i < _n; i++)
		{
			for (size_t j = 0; j < _n; j++)
			{
				if (i >= j)
				{
					cout << _data[i*(i + 1) / 2 + j]<<" ";
				}
				else   //上三角位置
				{
					cout << _data[j*(j + 1) / 2 + i]<<" ";  //交換行列座標
				}
			}
			cout << endl;
		}
		cout << endl;
	}
	//獲取某行某列元素
	T& Access(size_t i, size_t j)
	{
		if (i < j)
		{
			swap(i, j);
		}
		return _data[i*(i + 1) / 2 + j];
	}
protected:
	T* _data;
	size_t _size;
	size_t _n;
};


什麼又是稀疏矩陣呢?

壓縮存儲值存儲極少數的有效數據。使用{row,col,value}三元組存儲每一個有效數據,三元組按原矩陣中的位置,以行優先級先後順序依次存放。

首先構建三元組(這裏的每一個三元組就是矩陣中的一個元素)

template<class T>
struct Triple
{
	T _value;
	size_t _col;
	size_t _row;
	Triple(const T& value = T(), size_t row = 0, size_t col = 0)
		:_value(value)
		, _row(row)
		,_col(col)
	{}
};

再存儲有效值。

創建一個類,在構造函數中我們實現有效值的存儲

SparseMatrix(T* a, size_t m, size_t n, const T& invalid)
		:_rowSize(m)
		, _colSize(n)
		, _invalid(invalid)
	{
		for (size_t i = 0; i < _rowSize; i++)
		{
			for (size_t j = 0; j < _colSize; j++)
			{
				if (a[i*n + j] != _invalid)
				{
					_a.push_back(Triple<T>(a[i*n + j], i, j));
				}
			}
		}
	}
	SparseMatrix()
		:_rowSize(0)
		, _colSize(0)
		, _invalid(0)
	{}


這裏還有一個矩陣轉置。何爲矩陣轉置呢?

*矩陣轉置

將原矩陣的行、列對換,也就是將[i][j]和[j][i]位置上的數據對換。

wKioL1cUwMWR2SnoAAASNfIM1gM678.png

SparseMatrix<T> Transport()
	{
		SparseMatrix<T> tmp;
		tmp._colSize = _rowSize;  //交換行列大小
		tmp._rowSize = _colSize;
		tmp._invalid = _invalid;
		
		
		for (size_t i = 0; i < _colSize; i++)    
		{
			size_t index = 0;
			while (index < _a.size())
			{
				if (_a[index]._col == i)    //按照列在存儲的三元組中依次尋找.
				{                           //找到列爲0,壓入新的順序表中,繼續找.....
					Triple<T> t;
					t._col = _a[index]._row;
					t._row = _a[index]._col;
					t._value = _a[index]._value;
					tmp._a.push_back(t);
				}
				index++;
			}

		}
		return tmp;
	}


你們有沒有發現普通轉置的效率太低,時間複雜度太高?它的時間複雜度爲O(列數*有效數據的行數),那我接下來就給大家介紹快速轉置。

快速轉置,只需要遍歷一次存儲的有效數據。這個怎麼做到呢?

wKioL1cUwwixKk2WAAAj0qaFFxI469.png

我們需要得出轉置後每一行有效值的個數和每一行第一個有效值在壓縮矩陣中的起始位置。

RowCounts = { 2 , 0 , 2 , 0 , 2 } ; 

RowStart = { 0 , 2 , 2 , 4 , 4 } ;

我們可以看出 RowStrat[0] 總是恆爲 0,那很容易就可以發現

 RowStart[i] = RowStart[i - 1] + RowCounts[i - 1];

再看代碼

SparseMatrix<T> FastTransport()
	{
		SparseMatrix<T> tmp;
		tmp._colSize = _rowSize;
		tmp._rowSize = _colSize;
		tmp._invalid = _invalid;
		tmp._a.resize(_a.size());

		int *RowCounts = new int[_colSize];
		int *RowStart = new int[_colSize];
		memset(RowCounts, 0, sizeof(int)*_colSize);
		memset(RowStart, 0, sizeof(int)*_colSize);

		//統計個數
		size_t index = 0;
		while (index < _a.size())
		{
			RowCounts[_a[index]._col]++;
			index++;
		}
		RowStart[0] = 0;
		for (size_t i = 1; i < _colSize; i++)
		{
			RowStart[i] = RowStart[i - 1] + RowCounts[i - 1];
		}

		//定位位置
		index = 0;
		while (index < _a.size())
		{
			int rowindex = _a[index]._col;
			int& start = RowStart[rowindex];
			Triple<T> t;
			t._col = _a[index]._row;
			t._row = _a[index]._col;
			t._value = _a[index]._value;
			tmp._a[start] = t;
			start++;
			index++;
		}
		delete[] RowCounts;
		delete[] RowStart;

		return tmp;
	}


接下來我們繼續使用行優先的原則將壓縮矩陣打印出來

void Display()
	{
		size_t index = 0;
		for (size_t i = 0; i < _rowSize; i++)
		{
			for (size_t j = 0; j < _colSize; j++)
			{
				if (index < _a.size()&&_a[index]._row == i&&_a[index]._col == j)
				{
					cout << _a[index]._value << " ";
					index++;
				}
				else
				{
					cout << _invalid << " ";
				}
			}
			cout << endl;
		}
		cout << endl;
	}

最後再補上我們類的成員變量

protected:
	vector<Triple<T>> _a;
	size_t _rowSize;
	size_t _colSize;
	T _invalid;

就是我們的快速轉置了。小夥伴們好好看哦。我會繼續努力噠~j_0007.gif

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