• 高斯消元法


    题目描述

    这次,alice梦回大一上的线代课堂。她梦到线代老师在考试中出了这道压轴题:

    (12分)已知一个n阶方阵,以及一个列向量A,要求编程求解:

    (1)满足的列向量x(如果不存在唯一解,输出"No Solution!").(3分)

     

    (2)矩阵的行列式det(A).(5分)

    (3)矩阵的秩r(A),以及的零空间的维度dim(N(A)).(4分)

    对于第(1)问,alice很清楚这就是高斯消元,因此她很快就在网上查到了解决这个问题的伪代码:

     const double eps = 1e-8;
     int guass_elimination(double A[maxn][maxn], int n) // 高斯消元
     {
         int i, j, k, r;
         for(i = 0; i < n; i++) { // 消元
             r = i;
             for(j = i + 1; j < n; j++) if(fabs(A[j][i]) > fabs(A[r][i])) r = j;
             if(fabs(A[r][i]) < eps) return 0;  // 无解
             if(r != i) for(j = 0; j <= n; j++) swap(A[i][j], A[r][j]);
             for(k = i + 1; k < n; k++) {
                 double f = A[k][i] / A[i][i];
                 for(j = i; j <= n; j++) A[k][j] -= f * A[i][j];
             }
         }
         for(i = n - 1; i >= 0; i--) {  // 回代
             for(j = i + 1; j < n; j++) {
                 A[i][n] -= A[j][n] * A[i][j];
             }
             A[i][n] /= A[i][i];
         }
         return 1; 
     }

    其中,fabs(a)表示浮点数a的绝对值,swap(a,b)的作用是交换两个数a,b。函数传递的两个参数分别是增广矩阵(A|b)以及行数。这个增广矩阵在函数作用之后,假设只存在唯一解,则矩阵的第(n+1)列就是符合题意的列向量x。

    然而对于第(2)(3)问,则是一筹莫展。

    眼看就要交卷了,这时rawslee通过心灵感应给了她指点:

    "密切关注题目的第一问。"

    在这个提示下,alice重新审视了上面的伪代码,只进行了一些微小的调整以及增加一些语句,便马上写出了第(2)(3)问,愉快的在梦中取得了满分。你知道她是怎么做出来的吗?

    输入格式

    第一行一个正整数T,表示数据组数。

    对于每组数据:

    第一行一个正整数n,表示矩阵A的大小为n行n列。

    之后n行每行n个实数(最多有2位小数),表示矩阵A,第i行第j列表示矩阵的元素Aij。

    输出格式

    对于每组数据,输出三个数,分别代表det(A),(保留2位小数),r(A),dim(N(A)).

    每组输出以换行符结尾。

    数据范围

    .

     

    样例输入1

    1 3 1 3 4 1 4 7 9 3 2

    样例输出1

    38.00 3 0

    样例输入2

    1

    3

    1.50 3.50 4.50

    1.50 3.50 4.50

    9.20 3.20 2.20

    样例输出2

    0.00 2 1

    友情提醒

    为保持精度,建议使用double类型进行计算。

    1. # include<stdio.h>
    2. # include<math.h>
    3. # include<stdlib.h>
    4. /*
    5. (12分)已知一个阶方阵,以及一个列向量,要求编程求解:
    6. (1)满足的列向量(如果不存在唯一解,输出"No Solution!").(3分)
    7. (2)矩阵的行列式.(5分)
    8. (3)矩阵的秩,以及的零空间的维度.(4分)
    9. 对于第(1)问,很清楚这就是高斯消元,因此她很快就在网上查到了解决这个问题的伪代码:
    10. const double eps = 1e-8;
    11. int guass_elimination(double A[maxn][maxn], int n) // 高斯消元
    12. {
    13. int i, j, k, r;
    14. for(i = 0; i < n; i++) { // 消元
    15. r = i;
    16. for(j = i + 1; j < n; j++) if(fabs(A[j][i]) > fabs(A[r][i])) r = j;
    17. if(fabs(A[r][i]) < eps) return 0; // 无解
    18. if(r != i) for(j = 0; j <= n; j++) swap(A[i][j], A[r][j]);
    19. for(k = i + 1; k < n; k++) {
    20. double f = A[k][i] / A[i][i];
    21. for(j = i; j <= n; j++) A[k][j] -= f * A[i][j];
    22. }
    23. }
    24. for(i = n - 1; i >= 0; i--) { // 回代,求方程右边的向量
    25. for(j = i + 1; j < n; j++) {
    26. A[i][n] -= A[j][n] * A[i][j];
    27. }
    28. A[i][n] /= A[i][i];
    29. }
    30. return 1;
    31. }
    32. 其中,fabs(a)表示浮点数a的绝对值,swap(a,b)的作用是交换两个数a,b。
    33. 函数传递的两个参数分别是增广矩阵以及行数。这个增广矩阵在函数作用之后,
    34. 假设只存在唯一解,则矩阵的第列就是符合题意的列向量。
    35. 然而对于第(2)(3)问,则是一筹莫展。
    36. 眼看就要交卷了,这时通过心灵感应给了她指点:
    37. "密切关注题目的第一问。"
    38. 在这个提示下,重新审视了上面的伪代码,
    39. 只进行了一些微小的调整以及增加一些语句,便马上写出了第(2)(3)问
    40. 输入格式
    41. 第一行一个正整数,表示数据组数。
    42. 对于每组数据:
    43. 第一行一个正整数,表示矩阵的大小为行列。
    44. 之后行每行个实数(最多有2位小数),表示矩阵,第行第列表示矩阵的元素。
    45. 输出格式
    46. 对于每组数据,输出三个数,分别代表(保留2位小数),,.
    47. 每组输出以换行符结尾。
    48. 样例输入1
    49. 1 3 1 3 4 1 4 7 9 3 2
    50. 样例输出1
    51. 38.00 3
    52. 样例输入2
    53. 1
    54. 3
    55. 1.50 3.50 4.50
    56. 1.50 3.50 4.50
    57. 9.20 3.20 2.20
    58. 样例输出2
    59. 0.00 2 1
    60. 友情提醒
    61. 为保持精度,建议使用double类型进行计算。
    62. */
    63. int guass_elimination(double *A,int n, double * det, int *dim) // 高斯消元
    64. {//提示中的代码是对增广矩阵操作,而这里只要对方阵操作就行,所以
    65. //循环上限有所修改,对增广部分的代码也删了
    66. int i, j, k, r, flag = 1, time = 0;//time交换行的次数
    67. double temp; //交换行,行列式取相反数
    68. for(i = 0; i < n; i++)
    69. { // 消元
    70. r = i;
    71. for(j = i + 1; j < n; j++)//按列从左向右遍历
    72. if(fabs(A[j*n+i]) > fabs(A[r*n+i]))//第i列中从对角线往下找最大的那个
    73. r = j; //其所在行为r
    74. if(fabs(A[r*n+i]) < 1e-8) //若最大的都是0
    75. flag = 0; // 无数解,降秩
    76. if(r != i)
    77. {
    78. for(j = 0; j < n; j++)
    79. { //将第r行与第i行交换
    80. temp = A[i*n+j];
    81. A[i*n+j] = A[r*n+j];
    82. A[r*n+j] = temp;
    83. }
    84. time ++;
    85. }
    86. for(k = i + 1; k < n; k++) //将第i行以下的减f倍第i行(把最左边的
    87. { //即第i列)全消为0
    88. if(fabs(A[i*n+i]) > 1e-8)
    89. {
    90. double f = A[k*n+i] / A[i*n+i];//f存第i列要减的倍数
    91. for(j = i; j < n; j++)
    92. A[k*n+j] -= f * A[i*n+j];
    93. }
    94. }
    95. }//从而化为阶梯型 (注意是方阵
    96. *det = pow(-1, time);
    97. if(flag == 1)
    98. {
    99. for(int m = 0; m < n; m ++)
    100. {
    101. *det *= A[m*n+m];
    102. }
    103. }
    104. else
    105. {
    106. for(int m = 0; m < n; m ++)
    107. {
    108. if(fabs(A[m*n+m]) > 1e-8)
    109. {
    110. *dim = *dim + 1;//在对角线上数维数
    111. }
    112. }
    113. }
    114. return flag;
    115. }
    116. int main()
    117. {
    118. int num;
    119. scanf("%d", &num);//数据组数
    120. int flag[num], dim[num], n[num];//flag是否满秩,dim为秩,n为总维数
    121. double det[num];//行列式
    122. for(int i = 0; i<num;i++)
    123. {
    124. det[i] = 1;
    125. dim[i]=0;
    126. }
    127. for(int q = 0; q < num; q ++)
    128. {
    129. scanf("%d", &n[q]);
    130. double *a= (double*) malloc (n[q]*n[q] * sizeof(double));
    131. //用malloc而不用可变二维数组
    132. //因为可变二维数组传入函数需要在函数原型中确定列数,但列数非
    133. //常数,又函数原型在主函数获得列数前(此时列数未定义)
    134. //故此时无法用二维数组的模式定位,不如用一维
    135. for(int p = 0; p < n[q]*n[q]; p ++)
    136. {
    137. scanf("%lf", (a + p));//a为地址
    138. }
    139. flag[q] = guass_elimination(a, n[q], &det[q], &dim[q]);
    140. //函数出口最多一个返回值
    141. }
    142. for(int q = 0;q < num; q ++)
    143. {
    144. if(flag[q] != 0)//满秩
    145. {
    146. printf("%.2lf %d %d\n", det[q], n[q], 0);
    147. }
    148. else
    149. {
    150. printf("0.00 %d %d\n", dim[q], n[q] - dim[q]);
    151. }
    152. }
    153. return 0;
    154. }

  • 相关阅读:
    切换nvcc 的CUDA 版本
    提前预体验阿里大模型“通义千问”的方法来了!
    K线形态识别_跳空下跌三颗星
    公共安全数据协同治理的逻辑框架与网络形式——以兰州市食品安全领域为例
    【Linux】之Centos7卸载KVM虚拟化服务
    iperf 测试网络性能
    【教程】多进程下载百度旋转验证码图片-制作数据集
    二叉搜索树详解以及C++实现二叉搜索树(递归和非递归)
    P1032 [NOIP2002 提高组] 字串变换——dfs、bfs
    scratch大鱼吃小鱼 电子学会图形化编程scratch等级考试二级真题和答案解析2022年6月
  • 原文地址:https://blog.csdn.net/bitucas/article/details/125530135