矩阵初等行变换法则
- 任一行可以与另一行进行加减。
- 任一行可以乘或除以一个非零常数(除其实就是乘一个倒数)。
- 任两行可以交换位置。
线性方程组
形如
其中,系数矩阵为
右值向量(矩阵)为
解向量为
因此,方程组可表示为
矩阵的秩
- 矩阵可由初等行变换化为行最简形矩阵,所谓行最简型矩阵,即在阶梯形矩阵中,若非零行的第一个非零元素全是1,且非零行的第一个元素1所在列的其余元素全为零,就称该矩阵为行最简形矩阵。矩阵的秩就是行最简形矩阵非零行的个数,以来表示矩阵的秩。如:
则
高斯消元法(列主元法)
- 其实就是线性代数中的矩阵行化简算法。
思路
要解上述方程组,需要引入增广矩阵
其实就是在系数矩阵右侧添加右值向量
- 若且,则方程组有唯一解
- 若且,则方程组有无穷个解
- 若,则方程组无解
算法思想
- 假设行数为,列数为
化简矩阵
- 初始化当前行为
- 在行中寻找绝对值最大的所在行 (最大系数可减小误差)
- 若则说明,无唯一解,返回
- 交换第两行,使得增广矩阵保持为上三角矩阵
- 第行所有元素除以系数
- 若说明这是末尾行,结束矩阵化简,在求解向量后返回1
- 第行,减去倍第行,消除其余行的第列系数
- ,跳回到第步,寻找下一行
求解向量
此时矩阵为
即
此时有
解向量为
算法模板
int gauss(double num[100][101],int n,double x[]){
for(int i=0;i<n;i++){//循环n次,第i轮循环行为i~n-1,列为i~n
int maxRow=i;//maxRow记录系数最大的行,作为被减行减小误差
for(int j=i+1;j<n;j++){
if(abs(num[j][i])>abs(num[maxRow][i])) maxRow=j;
}
if(abs(num[maxRow][i])<zero) return 0;//x系数为0则增广矩阵无唯一解,返回0
if(maxRow!=i){//交换最大行到i行,使之保持为上三角矩阵
for(int j=i;j<n+1;j++){
swap(num[maxRow][j],num[i][j]);
}
}
for(int j=n;j>=i;j--){//化最大行第一个系数为1
num[i][j]/=num[i][i];//从后向前除以系数,否则需要临时变量记录[i][i]的系数
}
for(int j=i+1;j<n;j++){//被系数行减去
for(int k=n;k>=i;k--){
num[j][k]-=num[j][i]*num[i][k];//减去了系数行乘以对应系数
}
}
}
for(int i=n-1;i>=0;i--){//逆向求解向量
x[i]=num[i][n];//赋初值使得ax=b
for(int j=i+1;j<n;j++)
x[i]-=num[i][j]*x[j];//减去其他解向量
}
return 1;
}
例题
题目背景
Gauss消元
题目描述
给定一个线性方程组,对其求解
输入格式
第一行,一个正整数 第二至 行,每行个整数,为和,代表一组方程。
输出格式
共行,每行一个数,第行为(保留2位小数) 如果不存在唯一解,在第一行输出"No Solution".
输入输出样例
- 输入 #1
3
1 3 4 5
1 4 7 3
9 3 2 2
- 输出 #1
-0.97
5.18
-2.39
- 说明/提示
AC代码
#include <iostream>
#include <algorithm>
#define zero 1e-10
using namespace std;
int gauss(double num[100][101],int n,double x[]){
for(int i=0;i<n;i++){//循环n次,第i轮循环行为i~n-1,列为i~n
int maxRow=i;//maxRow记录系数最大的行,作为被减行减小误差
for(int j=i+1;j<n;j++){
if(abs(num[j][i])>abs(num[maxRow][i])) maxRow=j;
}
if(abs(num[maxRow][i])<zero) return 0;//x系数为0则增广矩阵无唯一解,返回0
if(maxRow!=i){//交换最大行到i行,使之保持为上三角矩阵
for(int j=i;j<n+1;j++){
swap(num[maxRow][j],num[i][j]);
}
}
for(int j=n;j>=i;j--){//化最大行第一个系数为1
num[i][j]/=num[i][i];//从后向前除以系数,否则需要临时变量记录[i][i]的系数
}
for(int j=i+1;j<n;j++){//被系数行减去
for(int k=n;k>=i;k--){
num[j][k]-=num[j][i]*num[i][k];//减去了系数行乘以对应系数
}
}
}
for(int i=n-1;i>=0;i--){//逆向求解向量
x[i]=num[i][n];//赋初值使得ax=b
for(int j=i+1;j<n;j++)
x[i]-=num[i][j]*x[j];//减去其他解向量
}
return 1;
}
int main(){
int n;
double num[100][101];//矩阵大小是n*n+1
double x[100];//存储解向量x
scanf("%d",&n);
for(int i=0;i<n;i++){
for(int j=0;j<n+1;j++){
scanf("%lf",&num[i][j]);
}
}
if(gauss(num,n,x)){
for(int i=0;i<n;i++){
printf("%.2lf\n",x[i]);
}
}else{
printf("No Solution");
}
return 0;
}
/*
3
1 3 4 5
1 4 7 3
9 3 2 2
*/