[C++] 计算行列式的若干种方法

计算行列式的三种方法

  • 测试样例
  • 按行(列)展开法
    • 计算结果
  • 高斯消元转化为上三角阵
    • 计算结果
    • 尝试引入permutation operation
    • 尝试使用分数运算
  • 定义法
    • 代码
    • 测试结果
  • 有问题欢迎提出

测试样例

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

按行(列)展开法

d e t ( A n ∗ n ) = ∑ j = 1 n a i j A i j , ( i = 1 , 2 , . . . , n ) det(A_{n*n}) = \sum\limits_{j=1}^na_{ij}A_{ij},(i=1,2,...,n) det(Ann)=j=1naijAij,(i=1,2,...,n)

d e t ( A n ∗ n ) = ∑ i = 1 n a i j A i j , ( j = 1 , 2 , . . . , n ) det(A_{n*n}) = \sum\limits_{i=1}^na_{ij}A_{ij},(j=1,2,...,n) det(Ann)=i=1naijAij,(j=1,2,...,n)

#include 
#include 
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;
}

计算结果

[C++] 计算行列式的若干种方法_第1张图片

高斯消元转化为上三角阵

d e t ( A n ∗ n ) = ∏ i = 1 n p i v o t   e l e m e n t i det(A_{n*n}) = \prod\limits_{i=1}^npivot\ element_i det(Ann)=i=1npivot elementi

#include 
#include 
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;
}

计算结果

[C++] 计算行列式的若干种方法_第2张图片
计算结果出现明显错误,猜测原因是这个矩阵在进行行变换的时候出现了第四行第四列,极小的主元,所以原因在于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
[C++] 计算行列式的若干种方法_第3张图片
可以得出正确结果
上述数据改变了某一列,使得矩阵没有出现极小主元
可知该算法面对出现了极小主元的矩阵表现不佳,但是其时间复杂度是 O ( n 3 ) O(n^3) O(n3),相对于使用递归的算法很有效率

尝试引入permutation operation

如下是MIT在2000年录制的线性代数课程
[C++] 计算行列式的若干种方法_第4张图片
[C++] 计算行列式的若干种方法_第5张图片
[C++] 计算行列式的若干种方法_第6张图片[C++] 计算行列式的若干种方法_第7张图片
[C++] 计算行列式的若干种方法_第8张图片
[C++] 计算行列式的若干种方法_第9张图片
[C++] 计算行列式的若干种方法_第10张图片
[C++] 计算行列式的若干种方法_第11张图片
[C++] 计算行列式的若干种方法_第12张图片
[C++] 计算行列式的若干种方法_第13张图片

#include 
#include 
#include 
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;
}

[C++] 计算行列式的若干种方法_第14张图片
问题解决

尝试使用分数运算

为了解决精度问题,考虑到测试样例只输入有理数,所以尝试了使用分数表示每个数字,但是由于long long类型长度有限,表达极小大数依然有困难,所以依然算不了上面的10阶的测试样例,不过也算个有趣的尝试。同时这种计算方法可以避免在结果在计算过程中丢失精度

#include 
#include 
#include 
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;
}

定义法

使用行列式的定义式进行计算
d e t ( A n ∗ n ) = ∑ a l l   o f   p e r m u t a t i o n s ( − 1 ) τ ( p 1 p 2 . . . p n ) a 1 p 1 a 2 p 2 . . . a n p n det(A_{n*n})=\sum\limits_{all\ of\ permutations}(-1)^{\tau(p_1p_2...p_n)}a_{1p_1}a_{2p_2}...a_{np_n} det(Ann)=all of permutations(1)τ(p1p2...pn)a1p1a2p2...anpn

τ ( p 1 p 2 . . . p n )   i s   t h e   i n v e r s i o n   n u m b e r   o f   p e r m u t a t i o n   p 1 p 2 . . . p n . {\tau(p_1p_2...p_n)}\ is\ the\ inversion\ number\ of\ permutation\ p_1p_2...p_n. τ(p1p2...pn) is the inversion number of permutation p1p2...pn.

代码

#include 
#include 
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;
}

测试结果

[C++] 计算行列式的若干种方法_第15张图片

有问题欢迎提出

你可能感兴趣的:(线性代数,c++,递归法)