目录
一.前言
最近在学习C++,数据结构以及线性代数,我突发奇想,为什么不把这几个东西放在一起学习呢?于是我想把一些可以用C++语言描述的线性代数上的操作写出来,这样一来也可以加深我的记忆,写了一些之后果然行列式的性质我都记住了。接下来我将分享我的小成果。
二.行列式运算操作集
1.概览
我将这个操作集命名为了“linear_algebra.h”,也就是线性代数,我用百度直接翻译的。因为我的目标不只局限于行列式,我希望能写出一个包括大部分线性代数操作的库,因此我给它起名为线性代数,但是目前里边只有行列式的操作。
2.行列式的定义
既然是对行列式进行操作那么必须有一个合适的结构来存储行列式,经过认真的学习行列式的构造,我定义了如下结构的行列式:
typedef struct{
double data[MaxSize][MaxSize];//二维数组,存储行列式用的数据主体
int dimension;//维度,也就是行列式的阶数
double multiple;//用于存储行列式的倍数
}det;//行列式的基本定义
首先是一个方形二维数组存放行列式,我命名为了data,dimension是阶数,multiple是系数,我用英文“乘”来表示,因为我当时忘了系数用英语怎么说。我们知道行列式的一个性质就是可以提出一行或一列的公因数放到外边作为整个行列式的系数,我们也可以将行列式的系数乘到里边的一行或一列中去。因此我需要一个变量来存储这个值。
3.行列式的输出与输入
1.行列式的输入
void input_det(det &inc_det){//以引用的方式传入行列式,因为我们需要对行列式进行改变。
int i, j;
std::cout << "请输入行列式维度:";
std::cin >> inc_det.dimension;
std::cout << "请输入行列式构型:" << std::endl;
for (i = 0; i < inc_det.dimension; i++){
for (j = 0; j < inc_det.dimension; j++){
std::cin >> inc_det.data[i][j];
}
}
inc_det.multiple = 1;
}//此函数用于输入一个行列式
2.行列式的输出
void print_det(det inc_det){//打印一个行列式1,因为仅访问即可,所以不用使用引用类型
int i, j;
std::cout << "这个行列式的维度为:" << inc_det.dimension << std::endl;
std::cout << "其具体构型为:" << std::endl;
for (i = 0; i < inc_det.dimension; i++){
std::cout << "|";
for (j = 0; j < inc_det.dimension-1; j++){
std::cout << inc_det.data[i][j] << " ";
}
if(i == inc_det.dimension/2)
std::cout << inc_det.data[i][j] << "|" << "*"<< inc_det.multiple << std::endl;
else
std::cout << inc_det.data[i][j] << "|" << std::endl;
}
}//此函数用于输出一个行列式
4.行列式行与行,列与列的相加
void det_add(det &inc_det, int i, int j, int n, int flag){//此函数用于将行列式的第i行/列的n倍加到第j行/列。i是加行;j是被加行;n是加行的倍数;flag是标志符号,若为0,则代表进行行操作,若为1则代表列操作,其他非法。
i -= 1;
j -= 1;
if(flag==0){//标志为0进行行操作
for (int t = 0; t < inc_det.dimension;t++){
inc_det.data[j][t] = inc_det.data[j][t] + n * inc_det.data[i][t];
}
}else if(flag==1){
for (int t = 0; t < inc_det.dimension;t++){
inc_det.data[t][j] = inc_det.data[t][j] + n * inc_det.data[t][i];
}
}else{
std::cout << "非法的标志符号:flag。flag只允许为1/0。" << std::endl;
}
}//将一个行列式中的第i行的n倍加在其第j行上
就是简单的二维数组的行列操作。
5.行列式的行交换与列交换
void det_change(det &inc_det, int i, int j, int flag){//行/列互换。交换第i行/列与第j行/列的值。flag是标志符号,0代表行操作,1代表列操作,其他非法。
int t, h;
i -= 1;
j -= 1;
if(flag==0){
for (t = 0; t < inc_det.dimension; t++){
h = inc_det.data[j][t];
inc_det.data[j][t] = inc_det.data[i][t];
inc_det.data[i][t] = h;
}
inc_det.multiple *= -1;//符号变化
}else if(flag==1){
for (t = 0; t < inc_det.dimension; t++){
h = inc_det.data[t][j];
inc_det.data[t][j] = inc_det.data[t][i];
inc_det.data[t][i] = h;
}
inc_det.multiple *= -1;//符号变化
}else{
std::cout << "非法的标志符号:flag。flag只允许为1/0。" << std::endl;
}
}//互换行列式的第i行和第j行
同样是简单的二维数组行列操作,但是注意系数要乘一个“-1”。
6.行列式的行提取公因数与列提取公因数
void det_extract(det &inc_det, int i, int n,int flag){//行/列公因数的提取。flag是标志位,0代表行操作,1代表列操作。
int t;
i -= 1;
if(i<0){
std::cout << "非法的参数值:i。行/列数最小为1。" << std::endl;
return;
}
if(flag==0){
for (t = 0; t < inc_det.dimension;t++){
inc_det.data[i][t] = inc_det.data[i][t]/n;
}
inc_det.multiple *= n;
}else if(flag==1){
for (t = 0; t < inc_det.dimension;t++){
inc_det.data[t][i] = inc_det.data[t][i]/n;
}
inc_det.multiple *= n;
}else{
std::cout << "非法的标志符号:flag。flag只允许为1/0。" << std::endl;
}
}
7.行列式系数清零恢复
void det_recover(det &inc_det, int i,int flag){//将系数乘到第i行/列上
int t;
i -= 1;
if(i<0){
std::cout << "非法的参数值:i。行/列数最小为1。" << std::endl;
return;
}
if(flag==0){
for (t = 0; t < inc_det.dimension;t++){
inc_det.data[i][t] = inc_det.data[i][t]*inc_det.multiple;
}
inc_det.multiple = 1;
}else if(flag==1){
for (t = 0; t < inc_det.dimension;t++){
inc_det.data[t][i] = inc_det.data[t][i]*inc_det.multiple;
}
inc_det.multiple = 1;
}else{
std::cout << "非法的标志符号:flag。flag只允许为1/0。" << std::endl;
}
}
将系数乘入指定的行/列中,系数变为1。
8.判断一个行列式是否是三角阵
bool trangle_det(det &inc_det){//通过逐行检测的方式判断一个行列式是不是三角阵
int i, j,ji = 0;
bool down_flag = true;
bool up_flag = true;
for (i = 1,j = 1; i < inc_det.dimension;i++,j++){
for (ji = 0; ji < j;ji++){
if(inc_det.data[i][ji]!=0){
down_flag = false;
}
}
}
for (i = 0, j = 1; i < inc_det.dimension; i++, j++){
for (ji = j; ji < inc_det.dimension;ji++){
if(inc_det.data[i][ji]!=0){
up_flag = false;
}
}
}
if(up_flag||down_flag){
return true;
}else{
return false;
}
}//判断某一个行列式是否是三角行列式,包括上三角和下三角
通过暴力扫描法判断一个行列式是否是三角阵,三角阵分为上三角和下三角和对角阵,但是我们只需判断出它为上三角或下三角就能确定其为三角阵了。
9.求余子式和代数余子式
det remainder_form(det inc_det, int i, int j){//求余子式,结果会返回一个新的矩阵类型。
det m;
int inc_r, inc_l;
int m_r = 0, m_l = 0;
m.dimension = inc_det.dimension - 1;
m.multiple = inc_det.multiple;
for (inc_r = 0; inc_r < inc_det.dimension; inc_r++){
for (inc_l = 0; inc_l < inc_det.dimension; inc_l++){
if (inc_r != i && inc_l != j){
m.data[m_r][m_l] = inc_det.data[inc_r][inc_l];
m_l++;
if (m_l > m.dimension-1){
m_r++;
m_l = 0;
}
}
}
}
return m;
}
这个函数只能求出余子式的结构。也就是会得到一个新的矩阵。代数余子式经过带入公式就可以得到,具体的行列式求值需要函数的相互配合使用。但是这个函数只负责求出余子式的结构。同时我们注意在求余子式时不能让外边有系数,这是我设计的一个缺陷,如果外边有系数必须先乘进去,否则会导致行列式出现数值错误。
10.求行列式的值
1.递归函数
int solve_normal_det(det inc_det){//求普通和行列式
double sum = 0;
if(inc_det.dimension>2){
for (int i = 0; i < inc_det.dimension; i++){
sum = sum + pow(-1,i)*inc_det.data[i][0]*solve_normal_det(remainder_form(inc_det,i,0));
}
return sum;
}else{
return inc_det.data[0][0] * inc_det.data[1][1] - inc_det.data[0][1] * inc_det.data[1][0];
}
}
递归的配合余子式函数算出行列式的值,可以发现这里和书上的定义法是一样的,也就是第一行/列的值乘以他们的代数余子式再相加。这里可以发现代数余子式直接带入公式就可以配合余子式函数计算出来。
2.总函数
double solve_det(det &inc_det){
det_recover(inc_det, 1, 0);//在求一个矩阵之前我们最好将他恢复成初始阵
if(trangle_det(inc_det)){
int result = 1;
for (int i = 0; i < inc_det.dimension;i++){
result = result * inc_det.data[i][i];
}
return result * inc_det.multiple;;
}else{
return solve_normal_det(inc_det);
}
}//用于求解行列式
这个函数有点特殊,它是用于求所有的行列式的,在这个函数里首先会判断一下是否为三角阵,如果是的话就直接对角相乘;不是的话就使用上面的行列式求解函数。因为这里涉及计算以及余子式的划分,所以我在一开头就把系数乘到了行列式里边。
11.行列式的转置
void det_transposition(det &inc_det){//转置就是行列互换
int t[MaxSize][MaxSize];
for (int i = 0; i < inc_det.dimension;i++){
for (int j = 0; j < inc_det.dimension;j++){
t[i][j] = inc_det.data[i][j];
}
}//先复制一份样品。
for (int i = 0; i < inc_det.dimension;i++){
for (int j = 0; j < inc_det.dimension;j++){
inc_det.data[j][i] = t[i][j];
}
}
}
暴力的备份转置法。
12.所有代码
#include <iostream>
#include <math.h>
#define MaxSize 100
/*-------------------------------------------维度在100以内的行列式计算----------------------------------------------*/
typedef struct{
double data[MaxSize][MaxSize];//二维数组,存储行列式用的数据主体
int dimension;//维度,也就是行列式的阶数
double multiple;//用于存储行列式的倍数
}det;//行列式的基本定义
void input_det(det &inc_det){//以引用的方式传入行列式,因为我们需要对行列式进行改变。
int i, j;
std::cout << "请输入行列式维度:";
std::cin >> inc_det.dimension;
std::cout << "请输入行列式构型:" << std::endl;
for (i = 0; i < inc_det.dimension; i++){
for (j = 0; j < inc_det.dimension; j++){
std::cin >> inc_det.data[i][j];
}
}
inc_det.multiple = 1;
}//此函数用于输入一个行列式
void print_det(det inc_det){//打印一个行列式1,因为仅访问即可,所以不用使用引用类型
int i, j;
std::cout << "这个行列式的维度为:" << inc_det.dimension << std::endl;
std::cout << "其具体构型为:" << std::endl;
for (i = 0; i < inc_det.dimension; i++){
std::cout << "|";
for (j = 0; j < inc_det.dimension-1; j++){
std::cout << inc_det.data[i][j] << " ";
}
if(i == inc_det.dimension/2)
std::cout << inc_det.data[i][j] << "|" << "*"<< inc_det.multiple << std::endl;
else
std::cout << inc_det.data[i][j] << "|" << std::endl;
}
}//此函数用于输出一个行列式
void det_add(det &inc_det, int i, int j, int n, int flag){//此函数用于将行列式的第i行/列的n倍加到第j行/列。i是加行;j是被加行;n是加行的倍数;flag是标志符号,若为0,则代表进行行操作,若为1则代表列操作,其他非法。
i -= 1;
j -= 1;
if(flag==0){//标志为0进行行操作
for (int t = 0; t < inc_det.dimension;t++){
inc_det.data[j][t] = inc_det.data[j][t] + n * inc_det.data[i][t];
}
}else if(flag==1){
for (int t = 0; t < inc_det.dimension;t++){
inc_det.data[t][j] = inc_det.data[t][j] + n * inc_det.data[t][i];
}
}else{
std::cout << "非法的标志符号:flag。flag只允许为1/0。" << std::endl;
}
}//将一个行列式中的第i行的n倍加在其第j行上
void det_change(det &inc_det, int i, int j, int flag){//行/列互换。交换第i行/列与第j行/列的值。flag是标志符号,0代表行操作,1代表列操作,其他非法。
int t, h;
i -= 1;
j -= 1;
if(flag==0){
for (t = 0; t < inc_det.dimension; t++){
h = inc_det.data[j][t];
inc_det.data[j][t] = inc_det.data[i][t];
inc_det.data[i][t] = h;
}
inc_det.multiple *= -1;//符号变化
}else if(flag==1){
for (t = 0; t < inc_det.dimension; t++){
h = inc_det.data[t][j];
inc_det.data[t][j] = inc_det.data[t][i];
inc_det.data[t][i] = h;
}
inc_det.multiple *= -1;//符号变化
}else{
std::cout << "非法的标志符号:flag。flag只允许为1/0。" << std::endl;
}
}//互换行列式的第i行和第j行
void det_extract(det &inc_det, int i, int n,int flag){//行/列公因数的提取。flag是标志位,0代表行操作,1代表列操作。
int t;
i -= 1;
if(i<0){
std::cout << "非法的参数值:i。行/列数最小为1。" << std::endl;
return;
}
if(flag==0){
for (t = 0; t < inc_det.dimension;t++){
inc_det.data[i][t] = inc_det.data[i][t]/n;
}
inc_det.multiple *= n;
}else if(flag==1){
for (t = 0; t < inc_det.dimension;t++){
inc_det.data[t][i] = inc_det.data[t][i]/n;
}
inc_det.multiple *= n;
}else{
std::cout << "非法的标志符号:flag。flag只允许为1/0。" << std::endl;
}
}
void det_recover(det &inc_det, int i,int flag){//将系数乘到第i行/列上
int t;
i -= 1;
if(i<0){
std::cout << "非法的参数值:i。行/列数最小为1。" << std::endl;
return;
}
if(flag==0){
for (t = 0; t < inc_det.dimension;t++){
inc_det.data[i][t] = inc_det.data[i][t]*inc_det.multiple;
}
inc_det.multiple = 1;
}else if(flag==1){
for (t = 0; t < inc_det.dimension;t++){
inc_det.data[t][i] = inc_det.data[t][i]*inc_det.multiple;
}
inc_det.multiple = 1;
}else{
std::cout << "非法的标志符号:flag。flag只允许为1/0。" << std::endl;
}
}
bool trangle_det(det &inc_det){//通过逐行检测的方式判断一个行列式是不是三角阵
int i, j,ji = 0;
bool down_flag = true;
bool up_flag = true;
for (i = 1,j = 1; i < inc_det.dimension;i++,j++){
for (ji = 0; ji < j;ji++){
if(inc_det.data[i][ji]!=0){
down_flag = false;
}
}
}
for (i = 0, j = 1; i < inc_det.dimension; i++, j++){
for (ji = j; ji < inc_det.dimension;ji++){
if(inc_det.data[i][ji]!=0){
up_flag = false;
}
}
}
if(up_flag||down_flag){
return true;
}else{
return false;
}
}//判断某一个行列式是否是三角行列式,包括上三角和下三角
det remainder_form(det inc_det, int i, int j){//求余子式,结果会返回一个新的矩阵类型。我认为在求余子式之前我们应该先把矩阵系数清空
det m;
int inc_r, inc_l;
int m_r = 0, m_l = 0;
m.dimension = inc_det.dimension - 1;
m.multiple = inc_det.multiple;
for (inc_r = 0; inc_r < inc_det.dimension; inc_r++){
for (inc_l = 0; inc_l < inc_det.dimension; inc_l++){
if (inc_r != i && inc_l != j){
m.data[m_r][m_l] = inc_det.data[inc_r][inc_l];
m_l++;
if (m_l > m.dimension-1){
m_r++;
m_l = 0;
}
}
}
}
return m;
}
int solve_normal_det(det inc_det){//求普通和行列式
double sum = 0;
if(inc_det.dimension>2){
for (int i = 0; i < inc_det.dimension; i++){
sum = sum + pow(-1,i)*inc_det.data[i][0]*solve_normal_det(remainder_form(inc_det,i,0));
}
return sum;
}else{
return inc_det.data[0][0] * inc_det.data[1][1] - inc_det.data[0][1] * inc_det.data[1][0];
}
}
double solve_det(det &inc_det){
det_recover(inc_det, 1, 0);//在求一个矩阵之前我们最好将他恢复成初始阵
if(trangle_det(inc_det)){
int result = 1;
for (int i = 0; i < inc_det.dimension;i++){
result = result * inc_det.data[i][i];
}
return result * inc_det.multiple;;
}else{
return solve_normal_det(inc_det);
}
}//用于求解行列式
void det_transposition(det &inc_det){//转置就是行列互换
int t[MaxSize][MaxSize];
for (int i = 0; i < inc_det.dimension;i++){
for (int j = 0; j < inc_det.dimension;j++){
t[i][j] = inc_det.data[i][j];
}
}//先复制一份样品。
for (int i = 0; i < inc_det.dimension;i++){
for (int j = 0; j < inc_det.dimension;j++){
inc_det.data[j][i] = t[i][j];
}
}
}
三.总结
我主要是为了复习线性代数写了这个操作集。通过写这个操作集我比较深入的复习了行列式的性质,对于行列式的计算以及行和列运算的共同点有了深入的了解。同时还对行列式的运算有了更深的认识。接下来复习全本书应该会舒服点吧?