매트릭스-C++

선형 테이블을 2차원으로 표현한 행렬입니다. 2차원 배열을 사용하여 행렬을 나타낼 수 있으며 희소 행렬 및 준대각선 행렬과 같이 널리 사용되는 특수한 형태의 행렬을 압축하여 저장할 수 있습니다.

대칭 및 삼각 행렬

행과 열의 수가 같은 행렬을 정사각형이라고 합니다. 정사각형 행렬에는 대칭 및 삼각형의 두 가지 특별한 유형이 있습니다. 대칭 행렬에는 반복되는 요소가 많기 때문에 대칭 행렬의 반복되지 않는 부분을 예약하여 공간을 절약할 수 있습니다. 행렬의 요소 정보 중 일부만 유지하면서 원래의 행렬을 복원할 수 있는 이런 종류의 표현 방법을 행렬의 압축 저장이라고 합니다.

대칭 행렬의 압축 저장 방법은 행렬의 하부 삼각 요소만 유지하는 것입니다. 삼각 행렬의 압축 감소는 대칭 행렬의 압축 감소와 유사합니다.

//矩阵的类型定义
constexpr auto M = 100; //矩阵行数的上限
constexpr auto N = 100; //矩阵列数的上限
typedef int datatype;
struct matrix {
	datatype a[M][N];
	int row; //矩阵的行数
	int col; //矩阵的列数
	matrix(int r = 0, int c = 0) :row(r), col(c) {
		memset(a, 0, sizeof(a));
	}
	matrix(datatype b[M][N], int r, int c) :row(r), col(c) { //用数组b初始化当前矩阵
		for (int i = 0; i < r; i++)
			for (int j = 0; j < c; j++)
				a[i][j] = b[i][j];
	}
};

//两个矩阵相乘,mat1*mat2,相乘结果作为返回值
matrix mat(matrix mat1, matrix mat2) {
	if (mat1.col != mat2.col) {
		cout << "矩阵不能相乘!" << endl;
		return NULL;
	}
	matrix mat(mat1.row, mat2.col); //mat的行数和列数分别mat1的行数和mat2的列数
	for (int i = 0; i < mat1.row; i++) {
		for (int j = 0; j < mat1.col; j++) {
			mat.a[i][j] = 0;
			for (int k = 0; k < mat1.col; k++) {
				mat.a[i][j] += mat1.a[i][k] * mat2.a[k][j];
			}
		}
	}
	return mat;
}

//特殊矩阵的表示与操作
//对称矩阵的压缩和还原

//将对称矩阵mat压缩为一维向量,结果作为返回值
vector<int> convert(matrix mat) {
	vector<int>ret;
	for (int i = 0; i < mat.row; i++) {
		for (int j = 0; j <= i; j++) {
			ret.push_back(mat.a[i][j]);
		}
	}
	return ret;
}

//将压缩的对称矩阵ve还原成二维表示方法,结果作为返回值
matrix convert(vector<int>ve) {
	matrix mat;
	int i = 0,j = 0;
	for (int k = 0; k < ve.size(); k++) {
		j = k - i * (i + 1) / 2; //数学计算,判断列数
		mat.a[i][j] = mat.a[j][i] = ve[k];
		if (i * (i + 2) / 2 < k) i++; //判断是否进入下一行
	}
	mat.row = mat.col = i;
	return mat;
}

희소 행렬

행렬의 전체 요소 수에 대한 행렬의 0이 아닌 요소 수의 비율을 행렬의 밀도라고 합니다. 즉, m*n 행렬에 0이 아닌 요소가 t개 있는 경우 행렬의 밀도는 행렬은 y=t/(mn)입니다. y가 특정 임계값(예: 0.05)보다 작고 0이 아닌 요소의 분포가 불규칙한 경우 행렬을 희소 행렬이라고 하고 그렇지 않은 경우 밀집 행렬 이라고 합니다 .

저장 공간을 절약하기 위해 희소 행렬을 저장할 때 0이 아닌 요소만 저장하는 압축 저장 방법을 사용할 수 있습니다. 압축되어 저장된 행렬을 원래의 행렬로 복원하기 위해서는 0이 아닌 요소를 저장할 때 요소의 값뿐만 아니라 그 위치, 즉 0이 아닌 각 요소의 저장 정보도 함께 저장되어야 한다. 3중 항(i, j, aij)이어야 하며, i 및 j는 각각 요소 aij의 행 및 열 위치입니다.

압축된 희소 행렬의 원소의 개수는 보통 원본 행렬의 원소 개수보다 훨씬 적기 때문에 압축된 계수 행렬의 연산 효율은 원본 행렬보다 높다. 다음은 압축된 희소 행렬에 대한 전치 연산을 예로 들어 설명합니다.

행렬의 전치 연산은 실제로 행렬의 모든 요소의 행 번호와 열 번호를 호출하는 것이므로 압축된 희소 행렬 sm의 경우 sm에서 각 요소의 행 번호와 열 번호만 교환하면 됩니다. nz, 하지만 상호 교환 sm.nz의 요소 정렬은 행 우선 및 교체 후 열의 요구 사항을 충족하지 않습니다. 이를 처리하는 가장 쉬운 방법은 sm.nz를 정렬하는 것입니다. smNode 구조에서 STL Just Sort의 알고리즘을 호출하면 됩니다.

//矩阵的类型定义
constexpr auto M = 100; //矩阵行数的上限
constexpr auto N = 100; //矩阵列数的上限
typedef int datatype;
struct matrix {
	datatype a[M][N];
	int row; //矩阵的行数
	int col; //矩阵的列数
	matrix(int r = 0, int c = 0) :row(r), col(c) {
		memset(a, 0, sizeof(a));
	}
	matrix(datatype b[M][N], int r, int c) :row(r), col(c) { //用数组b初始化当前矩阵
		for (int i = 0; i < r; i++)
			for (int j = 0; j < c; j++)
				a[i][j] = b[i][j];
	}
};

//两个矩阵相乘,mat1*mat2,相乘结果作为返回值
matrix mat(matrix mat1, matrix mat2) {
	if (mat1.col != mat2.col) {
		cout << "矩阵不能相乘!" << endl;
		return NULL;
	}
	matrix mat(mat1.row, mat2.col); //mat的行数和列数分别mat1的行数和mat2的列数
	for (int i = 0; i < mat1.row; i++) {
		for (int j = 0; j < mat1.col; j++) {
			mat.a[i][j] = 0;
			for (int k = 0; k < mat1.col; k++) {
				mat.a[i][j] += mat1.a[i][k] * mat2.a[k][j];
			}
		}
	}
	return mat;
}


//稀疏矩阵压缩存储的类型定义
struct smNode
{
	int r, c;  //非零元素所在的行号和列号
	datatype data; //非零元素的值
	bool operator<(smNode sm)const { //排序规则:行先列后,从小到大依次排列。
		if (r == sm.r) return c < sm.c;
		return r < sm.r;
	}
};

struct sMatrix {
	smNode nz[N]; //存放所有非零元素
	int n, row, col; //n:非零元素的数量;row:原矩阵的行数;col:原矩阵的列数
	sMatrix() :n(0), row(0), col(0) {
	}
};

//稀疏矩阵的压缩与还原
//将稀疏矩阵mat压缩存储,结果作为返回值
sMatrix sm_convert(matrix mat) {
	sMatrix sm;
	sm.row = mat.row = mat.col;
	for (int i = 0; i < mat.row; i++) {
		for (int j = 0; j < mat.col; j++) {
			if (mat.a[i][j] != 0) { //非零元素加入结果
				int& idx = sm.n; //idx为引用类型
				sm.nz[idx++] = { i,j,mat.a[i][j] };
			}
		}
	}
}

//将压缩存储的稀疏矩阵sm还原为原矩阵
matrix sm_convert(sMatrix sm) {
	matrix mat(sm.row, sm.col);
	for (int i = 0; i < sm.n; i++) {
		smNode tmp = sm.nz[i]; //第i个非零元素
		mat.a[tmp.r][tmp.c] = tmp.data;
	}
	return mat;
}

//压缩稀疏矩阵的转置
//求压缩稀疏矩阵的转置,结果也为压缩稀疏矩阵并作为返回值
sMatrix sm_tr(sMatrix sm) {
	sMatrix ret;
	ret.row = sm.col, ret.col = sm.row, ret.n = sm.n;//转置后的函数和列数与原矩阵交换,非零元素的数量不变
	for (int i = 0; i < sm.n; i++) { //考虑每一个非零元素
		smNode tmp = sm.nz[i];
		ret.nz[i] = { tmp.c,tmp.r,tmp.data }; //颠换行列
	}
	sort(ret.nz, ret.nz + ret.n); //将元素按指定的顺序排列
	return ret;
}

Guess you like

Origin blog.csdn.net/qq_62687015/article/details/128588675