測試樣例
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
按行(列)展開法
或
#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;
}
計算結果
高斯消元轉化爲上三角陣
#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
可以得出正確結果
上述數據改變了某一列,使得矩陣沒有出現極小主元
可知該算法面對出現了極小主元的矩陣表現不佳,但是其時間複雜度是,相對於使用遞歸的算法很有效率
嘗試引入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;
}
定義法
使用行列式的定義式進行計算
代碼
#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;
}