输入一个包含n个方程n个未知数的线性方程组。
方程组中的系数为实数。
求解这个方程组。
下图为一个包含m个方程n个未知数的线性方程组示例:
输入格式
第一行包含整数n。
接下来n行,每行包含n+1个实数,表示一个方程的n个系数以及等号右侧的常数。
输出格式
如果给定线性方程组存在唯一解,则输出共n行,其中第i行输出第i个未知数的解,结果保留两位小数。
如果给定线性方程组存在无数解,则输出“Infinite group solutions”。
如果给定线性方程组无解,则输出“No solution”。
数据范围
1 ≤ n ≤ 100
所有输入系数以及常数均保留两位小数,绝对值均不超过100。
输入样例:
3
1.00 2.00 -1.00 -6.00
2.00 1.00 -3.00 -9.00
-1.00 -1.00 2.00 7.00
输出样例:
1.00
-2.00
3.00
高斯消元 O(n3)
- 通过初等行变换 把 增广矩阵 化为 阶梯型矩阵 并回代 得到方程的解
- 适用于求解 包含
n
个方程,n
个未知数的多元线性方程组
前置知识:初等行(列)变换
- 把某一行乘一个非0的数 (方程的两边同时乘上一个非0数不改变方程的解)
- 交换某两行 (交换两个方程的位置)
- 把某行的若干倍加到另一行上去 (把一个方程的若干倍加到另一个方程上去)
接下来,运用初等行变换,把增广矩阵,变为阶梯型矩阵
算法步骤
- 枚举每一列c
- 找到当前列绝对值最大的一行
- 用初等行变换把这一行换到最上面(未确定阶梯型的行,并不是第一行)
- 用初等行变换将该行的第一个数变成 1 (其余所有的数字依次跟着变化)
- 用初等行变换将下面所有行的当且列的值变成 0
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
static int N = 110;
static double eps = 1e-6;
static int n;
static double a[][] = new double[N][N];
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(bf.readLine());
for (int i = 0; i < n; i ++ )
{
String[] str = bf.readLine().split(" ");
for (int j = 0; j < n + 1; j ++ )
a[i][j] = Double.parseDouble(str[j]);
}
int t = gauss();
if (t == 0)
{
for (int i = 0; i < n; i ++ )
System.out.printf("%.2f\n", a[i][n]);
}
else if (t == 1) System.out.println("Infinite group solutions");
else System.out.println("No solution");
}
private static int gauss() {
int c;//列
int r;//行
//枚举每一列c
for (c = 0, r = 0; c < n; c ++ )
{
//1.找到绝对值最大的一行
int t = r;//t为绝对值最大的一行
//遍历每一行
for (int i = r; i < n; i ++ )
//比较每一行第c列的值
if (Math.abs(a[i][c]) > Math.abs(a[t][c]))
t = i;//如果找到比之前更大的就更新t值
//如果绝对值为0,那么这一列所有数都为0,没有必要继续运算
if (Math.abs(a[t][c]) < eps) continue;
//2.将绝对值最大的这一行换到最上面
//从第c列开始是因为第0 ~ c-1列都为0,不需要继续进行交换
for (int i = c; i < n + 1; i ++ ) swap(a[t][i], a[r][i]);
//3.将绝对值最大的这一行的第一个系数变为1
//即等号两边同时除以这个系数,如果从前往后除那么第一个系数除完后为1,后面的也都是除以1了,所以要从后往前除
for (int i = n; i >= c; i --) a[r][i] /= a[r][c];
//4.将绝对值最大的这一行的下面所有行的第c列变为0
for (int i = r + 1; i < n; i ++ )
if (Math.abs(a[i][c]) > eps) //如果是非0再操作,剪枝
//从后往前,当前行的每个数字,都减去对应列 * 行首非0的数字,这样就能保证第一个数字是 a[i][0] -= 1*a[i][0]
for (int j = n; j >= c; j -- )
a[i][j] -= a[r][j] * a[i][c];
r ++;//更新r
}
//高斯消元后成上三角形式,再迭代回去求解
//说明剩下的方程小于n个,不是唯一解
if (r < n)
{
//从r ~ n - 1行可能是0 = 非0 或者 0 = 0
for (int i = r; i < n; i ++ )
if (Math.abs(a[i][n]) > eps) return 2;//无解
return 1;//无穷解
}
//只有r = n 才是n个方程n个未知数才会有唯一解,迭代求解
for (int i = n - 1; i >= 0; i -- )
for (int j = i + 1; j < n; j ++ )
//要用后面的方程带入前面的方程求解,将前面的方程的未知数变为一个,也就是其他的未知数的系数为0,也就是a[i][n]要减去后面方程倍数的Xn
a[i][n] -= a[j][n] * a[i][j];
return 0;//唯一解
}
private static void swap(double a, double b) {
double t = b;
b = a;
a = t;
}
}