[C++] 計算行列式的若干種方法

測試樣例

10
1 3 4 0 2 0 4 -5 9 3
6 7 8 5 2 7 8 4 6 2
8 7 6 9 8 7 6 8 45 2
4 3 2 5 4 3 2 8 6 4
1 2 0 4 1 2 3 9 87 6
5 6 7 8 5 6 8 6 8 1
9 8 7 6 9 8 7 -9 2 3
5 6 3 5 4 1 8 7 9 2
-9 8 6 2 4 56 6 3 8 9
5 3 6 8 6 6 1 8 2 3
答案:7583304

按行(列)展開法

det(Ann)=j=1naijAij,(i=1,2,...,n)det(A_{n*n}) = \sum\limits_{j=1}^na_{ij}A_{ij},(i=1,2,...,n)

det(Ann)=i=1naijAij,(j=1,2,...,n)det(A_{n*n}) = \sum\limits_{i=1}^na_{ij}A_{ij},(j=1,2,...,n)

#include <iostream>
#include <fstream>
using namespace std;
//構造第一行第line個元素的餘子式
void StructMinor(double* matrix, int dimension, int line, double* minor){
	int ptr=0;
	for(int cnt1=1; cnt1<dimension; cnt1++) for(int cnt2=0; cnt2<dimension; cnt2++)
		if(line!=cnt2) minor[ptr++] = matrix[cnt1*dimension+cnt2];
	return;
}
double Determinant(double* matrix, int dimension){
	//遞歸基,當矩陣中只有一個元素的時候,行列式的值即爲實數值
	if(dimension <= 1) return *matrix;
	double minor[(dimension-1)*(dimension-1)];
	double determinant = 0;
	int sign = 1;
	for(int cnt=0; cnt<dimension; cnt++){
		//構造matrix[0][cnt]的餘子式
		StructMinor(matrix, dimension, cnt, minor);
		//determinant=ΣELEMENT0j*A0j
		determinant += sign*matrix[cnt]*Determinant(minor, dimension-1);
		sign *= -1;
	}
	return determinant;
}
int main(){
	int dimension;
	int scale;
	double matrix[100];
	ifstream input("matrix.txt");
	input >> dimension;
	scale = dimension*dimension;
	for(int cnt=0; cnt<scale; cnt++) input >> matrix[cnt];
	cout << Determinant(matrix, dimension);
	return 0;
}

計算結果

在這裏插入圖片描述

高斯消元轉化爲上三角陣

det(Ann)=i=1npivot elementidet(A_{n*n}) = \prod\limits_{i=1}^npivot\ element_i

#include <iostream>
#include <fstream>
using namespace std;
//通過高斯消元法化矩陣爲upper triangle matrix
void GaussElimination2UTM(double* matrix, int dimension){
	//注意cnt3一定要從矩陣最右側運算至左側,否則主元列對應元素歸零,運算就無法正常進行
	for(int cnt1=0; cnt1<dimension; cnt1++) for(int cnt2=cnt1+1; cnt2<dimension; cnt2++) for(int cnt3=dimension-1; cnt3>=cnt1; cnt3--)
		matrix[cnt2*dimension+cnt3] += -1*matrix[cnt1*dimension+cnt3]*matrix[cnt2*dimension+cnt1]/matrix[cnt1*dimension+cnt1];
	return;
}
double Determinant(double* matrix, int dimension){
	double determinant=1;
	GaussElimination2UTM(matrix, dimension);
	//上三角陣對角線乘積即爲行列式
	for(int cnt=0; cnt<dimension; cnt++) determinant *= matrix[cnt*dimension+cnt];
	return determinant;
}
int main(){
	int dimension;
	int scale;
	double matrix[100];
	ifstream input("matrix.txt");
	input >> dimension;
	scale = dimension*dimension;
	for(int cnt=0; cnt<scale; cnt++) input >> matrix[cnt];
	cout << Determinant(matrix, dimension) << '\n';
	return 0;
}

計算結果

在這裏插入圖片描述
計算結果出現明顯錯誤,猜測原因是這個矩陣在進行行變換的時候出現了第四行第四列,極小的主元,所以原因在於double的精度對於這種算法依然不夠

對於測試樣例
10
1 3 67 0 2 0 4 -5 9 3
6 7 2 5 2 7 8 4 6 2
8 7 54 9 8 7 6 8 45 2
4 3 89 5 4 3 2 8 6 4
1 2 43 4 1 2 3 9 87 6
5 6 6 8 5 6 8 6 8 1
9 8 1 6 9 8 7 -9 2 3
5 6 2 5 4 1 8 7 9 2
-9 8 76 2 4 56 6 3 8 9
5 3 6 8 6 6 1 8 2 3
在這裏插入圖片描述
可以得出正確結果
上述數據改變了某一列,使得矩陣沒有出現極小主元
可知該算法面對出現了極小主元的矩陣表現不佳,但是其時間複雜度是O(n3)O(n^3),相對於使用遞歸的算法很有效率

嘗試引入permutation operation

如下是MIT在2000年錄製的線性代數課程
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <fstream>
#include <cmath>
using namespace std;
double GaussElimination2UTM(double* matrix, int dimension){
	double sign=1;
	double temp;
	for(int cnt1=0; cnt1<dimension; cnt1++){
		//在此引入置換操作,如果主元很小(小於0.001)那麼進行行交換,此操作在行列式中引入了負號,消元函數返回行列式由於置換操作需要加入的符號
		for(int cnt2=cnt1+1; cnt2<dimension; cnt2++){
			if(abs(matrix[cnt1*dimension+cnt1])>1e-3) break;
			for(int cnt3=0; cnt3<dimension; cnt3++){temp=matrix[cnt1*dimension+cnt3]; matrix[cnt1*dimension+cnt3]=matrix[cnt2*dimension+cnt3]; matrix[cnt2*dimension+cnt3]=temp;}
			sign*=-1;
		}
		for(int cnt2=cnt1+1; cnt2<dimension; cnt2++) for(int cnt3=dimension-1; cnt3>=cnt1; cnt3--)
			matrix[cnt2*dimension+cnt3] += -1*matrix[cnt1*dimension+cnt3]*matrix[cnt2*dimension+cnt1]/matrix[cnt1*dimension+cnt1];
	} 
	return sign;
}
double Determinant(double* matrix, int dimension){
	double determinant=1;
	determinant=GaussElimination2UTM(matrix, dimension);
	for(int cnt=0; cnt<dimension; cnt++) determinant *= matrix[cnt*dimension+cnt];
	return determinant;
}
int main(){
	int dimension;
	int scale;
	double matrix[100];
	ifstream input("matrix.txt");
	input >> dimension;
	scale = dimension*dimension;
	for(int cnt=0; cnt<scale; cnt++) input >> matrix[cnt];
	cout << Determinant(matrix, dimension) << '\n';
	return 0;
}

在這裏插入圖片描述
問題解決

嘗試使用分數運算

爲了解決精度問題,考慮到測試樣例只輸入有理數,所以嘗試了使用分數表示每個數字,但是由於long long類型長度有限,表達極小大數依然有困難,所以依然算不了上面的10階的測試樣例,不過也算個有趣的嘗試。同時這種計算方法可以避免在結果在計算過程中丟失精度

#include <iostream>
#include <fstream>
#include <cmath>
class rationalnumber{
	public:
		long long numerator;
		long long denominator;
};
using namespace std;
rationalnumber ReductionOfFraction(rationalnumber A){
	bool sign;
	long long gcd, mod, temp;
	rationalnumber result;
	if(A.numerator == 0){
		result.numerator = 0;
		result.denominator = 1;
	}
	else{
		if(sign = (A.numerator < 0)) A.numerator = -A.numerator;
		if(A.numerator%A.denominator == 0){result.numerator = A.numerator/A.denominator; result.denominator = 1;}
		else{
			temp = A.numerator>A.denominator?A.numerator:A.denominator;
			gcd = A.numerator<A.denominator?A.numerator:A.denominator;
			mod = temp%gcd;
			while(mod!=0){
				temp = gcd;
				gcd = mod;
				mod = temp%gcd;
			}
			result.numerator = A.numerator/gcd;
			result.denominator = A.denominator/gcd;
		}
		if(sign) result.numerator = -result.numerator;
	}
	return result;
}
rationalnumber NegativeOfFraction(rationalnumber A){
	rationalnumber result;
	result.numerator = -A.numerator;
	result.denominator = A.denominator;
	return result;
}
rationalnumber AdditionOfFraction(rationalnumber A, rationalnumber B){
	rationalnumber result;
	result.numerator = A.numerator*B.denominator + B.numerator*A.denominator;
	result.denominator = A.denominator * B.denominator;
	result=ReductionOfFraction(result);
	return result;
}
rationalnumber MultiplicationOfFraction(rationalnumber A, rationalnumber B){
	rationalnumber result;
	result.numerator = A.numerator * B.numerator;
	result.denominator = A.denominator * B.denominator;
	result=ReductionOfFraction(result);
	return result;
}
rationalnumber DivisionOfFraction(rationalnumber A, rationalnumber B){
	rationalnumber result;
	result.numerator = A.numerator * B.denominator;
	result.denominator = A.denominator * B.numerator;
	result=ReductionOfFraction(result);
	return result;
}
//通過高斯消元法化矩陣爲upper triangle matrix
void GaussElimination2UTM(rationalnumber* matrix, int dimension){
	//注意cnt3一定要從矩陣最右側運算至左側,否則主元列對應元素歸零,運算就無法正常進行
	for(int cnt1=0; cnt1<dimension; cnt1++) for(int cnt2=cnt1+1; cnt2<dimension; cnt2++) for(int cnt3=dimension-1; cnt3>=cnt1; cnt3--)
		matrix[cnt2*dimension+cnt3] =
			AdditionOfFraction(
				matrix[cnt2*dimension+cnt3],
				NegativeOfFraction(
					DivisionOfFraction(
						MultiplicationOfFraction(matrix[cnt1*dimension+cnt3], matrix[cnt2*dimension+cnt1]),
						matrix[cnt1*dimension+cnt1]
					)
				)						
			);
	return;
}
rationalnumber Determinant(rationalnumber* matrix, int dimension){
	rationalnumber determinant;
	determinant.denominator = 1;
	determinant.numerator = 1;
	GaussElimination2UTM(matrix, dimension);
	//上三角陣對角線乘積即爲行列式
	for(int cnt=0; cnt<dimension; cnt++) determinant = MultiplicationOfFraction(determinant, matrix[cnt*dimension+cnt]);
	return determinant;
}
int main(){
	int dimension;
	int scale;
	double fraction;
	rationalnumber determinant;
	rationalnumber matrix[100];
	ifstream input("matrix.txt");
	input >> dimension;
	scale = dimension*dimension;
	for(int cnt=0; cnt<scale; cnt++){
		input >> fraction;
		for(matrix[cnt].denominator = 1; abs(fraction-(int)fraction)>10e-17;  matrix[cnt].denominator*=10, fraction*=10);
		matrix[cnt].numerator=(int)fraction;
	}
	determinant = Determinant(matrix, dimension);
	cout << (double)determinant.numerator/(double)determinant.denominator << '\n';
	return 0;
}

定義法

使用行列式的定義式進行計算
det(Ann)=all of permutations(1)τ(p1p2...pn)a1p1a2p2...anpndet(A_{n*n})=\sum\limits_{all\ of\ permutations}(-1)^{\tau(p_1p_2...p_n)}a_{1p_1}a_{2p_2}...a_{np_n}

τ(p1p2...pn) is the inversion number of permutation p1p2...pn.{\tau(p_1p_2...p_n)}\ is\ the\ inversion\ number\ of\ permutation\ p_1p_2...p_n.

代碼

#include <iostream>
#include <fstream>
using namespace std;
int dimension;
char permutation[10];
double matrix[100];
double Determinant(int depth, double sign){
	double result;
	//遞歸基,在遞歸樹的葉子返回定義的求和式的其中一項
	if(depth>=dimension){
		result = sign;
		for(int cnt=0; cnt<dimension; cnt++) result *= matrix[cnt*dimension+permutation[cnt]];
	}
	else{
		int temp;
		//不交換的情況,對逆序數無貢獻
		result = Determinant(depth+1, sign);
		//將後續幾個元素分別於第一個元素進行交換,逆序數加一
		for(int cnt=depth+1; cnt<dimension; cnt++){
			//交換
			temp = permutation[depth];
			permutation[depth] = permutation[cnt];
			permutation[cnt] = temp;
			//計算此情況下所有值之和
			result += Determinant(depth+1, -1*sign);
			//換回
			temp = permutation[depth];
			permutation[depth] = permutation[cnt];
			permutation[cnt] = temp;
		}
	}
	return result;
}
int main(){
	int scale;
	ifstream input("matrix.txt");
	input >> dimension;
	for(int cnt=0; cnt<dimension; cnt++) permutation[cnt] = cnt;
	scale = dimension*dimension;
	for(int cnt=0; cnt<scale; cnt++) input >> matrix[cnt];
	cout << Determinant(0,1) << '\n';
	return 0;
}

測試結果

在這裏插入圖片描述

有問題歡迎提出

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