文章目录
- 一、实验目的、内容
- 二、实验程序设计及结构
- 1.需求分析
- 变量
- 函数
- 2.设计结构或流程图
- 三、设计过程
- 四、测试分析
- 第一组
- 第二组
- 实验中出现的bug及解决方案
- 五、设计的特点和结果
一、实验目的、内容
输入是一个 n n n( n < 256 n<256 n<256)阶方阵 A A A,输出是它的逆矩阵,再将得到的逆矩阵与原来的矩阵相乘,验证其结果是单位矩阵。
二、实验程序设计及结构
1.需求分析
变量
二级指针a
(double**
)存储原矩阵的首地址,b
(double**
)存储逆矩阵的首地址,c
(double**
)存储乘积的首地址;数n
(unsigned char
)存储阶数;循环变量i
(unsigned char
),j
(unsigned char
)。
函数
求逆函数double** inv(const double** a, const unsigned char n)
,求积函数double** pro(const double** a, const double** b, const unsigned char n)
,主函数int main()
。
2.设计结构或流程图
- 输入原矩阵及矩阵的阶数。
- 求逆矩阵并输出。(调用求逆函数)
- 在堆区开辟两个二维数组,其中一个初始化为原矩阵,另一个初始化为单位矩阵。
- 从首行开始逐行进行初等行变换,将原矩阵化为阶梯型,对单位矩阵进行同等操作。
- 从最后一行开始逐行消元素,直到将原矩阵化为单位矩阵为止。
- 分别对原矩阵和逆矩阵左乘和右乘进行验证并输出。(调用求积函数)
- 释放堆区数据并退出。
三、设计过程
#include <iostream>
#define abs(a) (a > 0 ? a : -a)
using namespace std;
// 求逆函数
double **inv(double **a, unsigned char n)
{unsigned char i, j, k = -1;double **t = new double *[n], **b = new double *[n]; // t作为逆矩阵,b作为原矩阵的副本// 拷贝a并把t初始化为单位矩阵for (i = 0; i < n; ++i){t[i] = new double[n];b[i] = new double[n];for (j = 0; j < n; ++j){b[i][j] = a[i][j];if (i == j)t[i][j] = 1.;elset[i][j] = 0.;}}double x;double *w;while (++k < n) // 化原矩阵为阶梯型{// 第k行第k列为零,在第k列中寻找不为零的元素if (abs(b[k][k]) < 1e-5){i = k;// 由于浮点数为二进制指数表达,在计算过程中可能出现偏差// 所以将1e-5到-1e-5之间的数据近似地视为0doif (++i == n)return nullptr;while (abs(b[i][k]) < 1e-5); // 如果遍历完了整行,证明行列式为0,逆矩阵不存在w = b[i];b[i] = b[k];b[k] = w; // 交换找到的行w = t[i];t[i] = t[k];t[k] = w; // 对逆矩阵做同样的初等行变换}for (i = k + 1; i < n; ++i){x = b[i][k] / b[k][k]; // 记录比例系数for (j = k + 1; j < n; ++j)b[i][j] -= x * b[k][j]; // 对原矩阵做初等行变换for (j = 0; j <= k; ++j)t[i][j] -= x * t[k][j]; // 对逆矩阵做同样的初等行变换}x = b[k][k];// 将b[k][k]变为1,由于b[k][k]在接下来的程序里无用,故i从k+1开始for (i = k + 1; i < n; ++i)b[k][i] /= x;for (i = 0; i <= k; ++i)t[k][i] /= x; // 对逆矩阵做同样的初等行变换}do // 化为单位矩阵{i = --k;do{x = b[--i][k];for (j = 0; j < n; ++j)t[i][j] -= x * t[k][j];} while (i > 0);delete[] b[k]; // 防止内存泄漏} while (k > 1);delete[] *b;delete[] b;return t;
}
double **pro(double **a, double **b, unsigned char n) // 方阵乘法
{double **p = new double *[n], *q;for (unsigned char i = 0; i < n; ++i){q = p[i] = new double[n];for (unsigned char j = 0; j < n; ++j){*q = *a[i] * b[0][j];for (unsigned char k = 1; k < n; ++k)*q += a[i][k] * b[k][j];++q;}}return p;
}
int main()
{unsigned short n; // 为了输入方便,故采用unsigned shortcout << "请输入方阵的阶数:\n";cin >> n;cout << "请输入方阵:\n";double **a = new double *[n];// 由于a是指针类型而非数组类型,故不能用范围for循环for (unsigned char i = 0; i < n; ++i){a[i] = new double[n];for (unsigned char j = 0; j < n; ++j)cin >> a[i][j];}double **b = inv(a, n);if (!b){cout << "逆矩阵不存在!\n";system("pause");return 0;}cout << "逆矩阵为:\n";for (unsigned char i = 0; i < n; ++i){for (unsigned char j = 0; j < n; ++j)cout << b[i][j] << '\t';cout << endl;}cout << "原矩阵右乘逆矩阵为:\n";double **c = pro(a, b, n);for (unsigned char i = 0; i < n; ++i){for (unsigned char j = 0; j < n; ++j)cout << c[i][j] << '\t';delete[] c[i]; // 防止内存泄漏cout << endl;}delete[] c;cout << "原矩阵左乘逆矩阵为:\n";c = pro(b, a, n);for (unsigned char i = 0; i < n; ++i){for (unsigned char j = 0; j < n; ++j)cout << c[i][j] << '\t';delete[] c[i]; // 防止内存泄漏delete[] a[i];delete[] b[i];cout << endl;}delete[] c;delete[] a;delete[] b;system("pause");return 0;
}
四、测试分析
第一组
第二组
实验中出现的bug及解决方案
bug | 解决方案 |
---|---|
未考虑行列式为0的情况 | 返回空指针 |
由于浮点数计算的不精确,容易出现大数 | 将-1e-5 至1e-5 之间的数据近似视为0 |
五、设计的特点和结果
采用初等变换法求逆矩阵,较伴随矩阵法较复杂,但避免了递归,效率更高。
结果:求得逆矩阵。但由于浮点数运算的偏差,容易出现极小数,往后可以通过编写一个有理数类加以解决。