如何用C语言求N阶矩阵的逆?高阶行列式计算异常求助
问题分析与解决方案
你遇到的核心问题并非行列式计算模块本身,而是余子矩阵的存储逻辑错误导致逆矩阵计算异常,同时pow函数的浮点数精度可能带来微小误差。以下是具体分析和修复方案:
1. 核心错误:余子矩阵存储位置颠倒
在cofactor函数中,你计算的是第q行第p列元素的余子式,但错误地将其赋值给了fac[p][q](行列颠倒)。后续的转置操作会进一步放大这个错误,最终得到的不是伴随矩阵,而是余子矩阵本身,导致逆矩阵计算完全错误。
修复方式:
将fac[p][q] = x;改为fac[q][p] = x;,确保余子式值存储到正确的行列位置。
2. 次要问题:pow函数的精度隐患
使用pow(-1, q+p)计算符号时,浮点数运算可能产生微小的精度误差(例如-1.0变成-0.9999999999),这会影响最终的行列式和逆矩阵结果。建议改用整数奇偶性判断来替代:
修复方式:
将x = pow(-1, q + p) * determinant( tam - 1,b);改为:
int sign = ((q + p) % 2 == 0) ? 1 : -1; x = sign * determinant(tam - 1, b);
3. 额外bug:main函数中flag未初始化
main函数里的flag变量没有初始值,导致输入合法矩阵后循环可能无法终止。需要初始化flag为0,并在处理完合法矩阵后将其设为1。
修复方式:
在main函数开头添加flag = 0;,并在create_Matrix(line);后添加flag = 1;。
修正后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include<math.h> float determinant(int tam, float[][tam]); void cofactor(int tam, float[][tam]); void transpose(int tam, float[][tam], float[][tam]); int validate() { int val; char *buf = (char *)malloc(10); memset(buf, 0, 10); while (fgets(buf, 10, stdin) != NULL) { if (buf[0] != '\n') { val = atoi(buf); break; } } free(buf); return val; } float determinant(int tam, float matrix[][tam]) { float s = 1, det = 0, b[tam][tam]; int i, j, m, n, c; if (tam == 1) { return (matrix[0][0]); } else { det = 0; for (c = 0; c < tam; c++) { m = 0; n = 0; // 初始化子矩阵为0 for (i = 0; i < tam; i++) { for (j = 0; j < tam; j++) { b[i][j] = 0; } } for (i = 0; i < tam; i++) { for (j = 0; j < tam; j++) { if (i != 0 && j != c) { b[m][n] = matrix[i][j]; if (n < (tam - 2)) { n++; } else { n = 0; m++; } } } } det = det + s * (matrix[0][c] * determinant(tam - 1, b)); s = -1 * s; } } return (det); } void cofactor(int tam, float num[][tam]) { float b[tam][tam], fac[tam][tam]; int p, q, m, n, i, j; float x = 0; for (q = 0; q < tam; q++) { for (p = 0; p < tam; p++) { m = 0; n = 0; // 初始化子矩阵为0 for (i = 0; i < tam; i++) { for (j = 0; j < tam; j++) { b[i][j] = 0; } } for (i = 0; i < tam; i++) { for (j = 0; j < tam; j++) { if (i != q && j != p) { b[m][n] = num[i][j]; if (n < (tam - 2)) { n++; } else { n = 0; m++; } } } } // 用整数判断替代pow,避免精度问题 int sign = ((q + p) % 2 == 0) ? 1 : -1; x = sign * determinant(tam - 1, b); // 修复余子式存储位置 fac[q][p] = x; } } transpose(tam, num, fac); } void transpose(int tam, float num[][tam], float fac[][tam]) { int i, j; float b[tam][tam], inverse[tam][tam], d; // 转置余子矩阵得到伴随矩阵 for (i = 0; i < tam; i++) { for (j = 0; j < tam; j++) { b[i][j] = fac[j][i]; } } d = determinant(tam, num); for (i = 0; i < tam; i++) { for (j = 0; j < tam; j++) { inverse[i][j] = b[i][j] / d; } } printf("\n\n\nThe inverse of matrix is : \n"); for (i = 0; i < tam; i++) { for (j = 0; j < tam; j++) { printf("\t%f", inverse[i][j]); } printf("\n"); } } int verify_Size(int line) { if ((line > 0) && (line <= 6)) { return 1; } return 0; } int create_Matrix(int LINE) { int matrix[LINE][LINE]; float aux[LINE][LINE]; printf("\n\n\nPlease enter %d elements:\n", (LINE * LINE)); printf("--------------------------------\n"); for (int i = 0; i < LINE; i++) { for (int j = 0; j < LINE; j++) { printf("Value[%d][%d]: ", i, j); matrix[i][j] = validate(); } } printf("\n\nYour Bidimensional Matrix is:"); printf("\n--------------------------------\n"); for (int i = 0; i < LINE; i++) { for (int j = 0; j < LINE; j++) { printf("\t%2d", matrix[i][j]); aux[i][j] = (float)matrix[i][j]; } printf("\n"); } float d = determinant(LINE, aux); printf("\n\nDeterminant: %f \n", d); if (fabs(d) < 1e-6) { // 用浮点比较替代==0,避免精度问题 printf("\nInverse of Entered Matrix is not possible\n"); } else { cofactor(LINE, aux); } return 0; } int main() { int flag = 0, line; // 初始化flag do { printf("Enter the order of the Matrix:\n"); printf("-------------------------------\n"); printf("Lines: "); line = validate(); if (verify_Size(line) == 1) { create_Matrix(line); flag = 1; // 设置flag为1,终止循环 } else { printf("\nMatrix must be up to 6 X 6!\n"); flag = 0; } } while (flag != 1); return 0; }
额外优化点
- 在
create_Matrix中,判断行列式是否为0时,用fabs(d) < 1e-6替代d == 0,避免浮点数精度误差导致的误判。 - 将子矩阵的初始化逻辑移到填充之前,确保所有元素初始为0,避免垃圾值干扰。
内容的提问来源于stack exchange,提问作者Rafael Rossales




