我想当你看到这篇文章的时候,已经对高斯消元法进行了一些了解了,如果还有不明白的地方,请大家自行百度,我就不在这叙述高斯消元法的求解过程了。
刚开始想实现高斯消元的时候,想的比较简单,就是将每个系数存成int型,实现过程如下
package com.zly.base;
import java.util.Iterator;
import java.util.Scanner;
public class Linear {
/**
* 交换两个参数
* @param a
* @param b
*/
public void swap(int a,int b){
int t=0;
t=a;
a=b;
b=t;
}
public static void main(String[] args) {
//获取方程个数和未知数个数
Scanner sr=new Scanner(System.in);
System.out.println("请输入方程个数:");
int equationNum=sr.nextInt();
System.out.println("请输入未知数个数:");
int unknownNum=sr.nextInt();
System.out.println("您输入的方程个数为:"+equationNum);
System.out.println("您输入的未知数个数为"+unknownNum);
int[][] fangcheng=new int[equationNum][unknownNum+1];
for(int i=0;i<equationNum;i++){
System.out.println("请输入第"+(i+1)+"个方程的未知数系数和值,系数之间用;隔开");
String xishu=sr.next();
String [] as = xishu.split(";");
for(int j=0;j<unknownNum+1;j++){
fangcheng[i][j]=Integer.parseInt(as[j]);
}
}
System.out.println("此时的方程为:");
for(int i=0;i<equationNum;i++){
for(int j=0;j<unknownNum+1;j++){
if(j != unknownNum){
System.out.print(fangcheng[i][j]+" ");
}else{
System.out.print("|"+fangcheng[i][j]);
}
}
System.out.println();
}
if(equationNum>unknownNum){
//无解
System.out.println("方程无解");
}else{
//有解
for(int i=0;i<equationNum-1;i++){
//选取主元素
//如果主元素为0,则找该列不为0的行交换
if(fangcheng[i][i]==0){
//查找该列下不为0的行
for(int j=i+1;j<equationNum;j++){
if(fangcheng[j][i] !=0){
//交换两行
for(int k=i;k<equationNum;k++){
for(int m=0;m<unknownNum+1;m++){
//交换操作
int temp;
temp=fangcheng[k][m];
fangcheng[k][m]=fangcheng[j][m];
fangcheng[j][m]=temp;
}
}
break;
}
}
System.out.println("交换后的方程为:");
for(int p=0;p<equationNum;p++){
for(int q=0;q<unknownNum+1;q++){
if(q==unknownNum){
System.out.print("|"+fangcheng[p][q]);
}else{
System.out.print(fangcheng[p][q]+" ");
}
}
System.out.println();
}
}
System.out.println("此时的主元素为:"+fangcheng[i][i]);
//进行消元操作,循环当前行下面的所有行,进行减法运算
for(int i1=i+1;i1<equationNum;i1++){
float gys=fangcheng[i][i]/fangcheng[i1][i];
System.out.println("公约数为:"+gys);
for(int i2=i;i2<unknownNum+1;i2++){
//为每一行从新赋值
if(fangcheng[i1][i2]!=0){
fangcheng[i1][i2]=(int) (fangcheng[i1][i2]*gys-fangcheng[i][i2]);
}
}
}
System.out.println("计算后的上三角为:");
for(int p=0;p<equationNum;p++){
for(int q=0;q<unknownNum+1;q++){
if(q==unknownNum){
System.out.print("|"+fangcheng[p][q]);
}else{
System.out.print(fangcheng[p][q]+" ");
}
}
System.out.println();
}
//消元完成进行反转,当equationNum=unknownNum满秩的时候,有唯一解直接求解,当equationNum<unknownNum,有无数解;
}
}
}
}
在后来的实现过程中发现,int型对精度没有太大的处理能力,可能在经历过有限次的运算之后,答案过于偏离实际,所以经过思考,我决定用分数来表示运算过程中的数值表示,然后将分数的分子和分母分别用int型来表示,将分数的加减乘除独立出来,如下
分数的加减:
public static String plus(String a,String b,int type){
String count="";//计算结果
String umerator="";//分子
String denominator="";//分母
//字符串格式判断
a=getstandard(a);
b=getstandard(b);
String[] a1 = a.split("/");//a的分子和分母
String [] b1 = b.split("/");//b的分子和分母
int fenmu=Integer.parseInt(a1[1])*Integer.parseInt(b1[1]);
int fenzi;
if(type==0){
fenzi=Integer.parseInt(a1[0])*Integer.parseInt(b1[1])+Integer.parseInt(b1[0])*Integer.parseInt(a1[1]);
}else{
fenzi=Integer.parseInt(a1[0])*Integer.parseInt(b1[1])-Integer.parseInt(b1[0])*Integer.parseInt(a1[1]);
}
denominator=String.valueOf(fenmu);
umerator=String.valueOf(fenzi);
if(denominator.equals(umerator)){
count="1";
}else if(umerator.equals("0")){
count="0";
}else{
//加法计算
count=umerator+"/"+denominator;
}
return filter(count);
}
分数的乘除:
public static String plus(String a,String b,int type){
String count="";//计算结果
String umerator="";//分子
String denominator="";//分母
//字符串格式判断
a=getstandard(a);
b=getstandard(b);
String[] a1 = a.split("/");//a的分子和分母
String [] b1 = b.split("/");//b的分子和分母
int fenmu=Integer.parseInt(a1[1])*Integer.parseInt(b1[1]);
int fenzi;
if(type==0){
fenzi=Integer.parseInt(a1[0])*Integer.parseInt(b1[1])+Integer.parseInt(b1[0])*Integer.parseInt(a1[1]);
}else{
fenzi=Integer.parseInt(a1[0])*Integer.parseInt(b1[1])-Integer.parseInt(b1[0])*Integer.parseInt(a1[1]);
}
denominator=String.valueOf(fenmu);
umerator=String.valueOf(fenzi);
if(denominator.equals(umerator)){
count="1";
}else if(umerator.equals("0")){
count="0";
}else{
//加法计算
count=umerator+"/"+denominator;
}
return filter(count);
}
大家会发现里面有几个附加函数,主要是为了计算的时候补位和正负号的过滤以及结果集整数处理的问题:
/**
* 得到标准形式的字符串
* @param a
* @return
*/
public static String getstandard(String a){
// String umerator="";//分子
// String denominator="";//分母
String [] as = a.split("/");
if(as.length !=2){
a=a+"/1";
}
return a;
}
/**
* 过滤字符串,
* @param a
* @return
*/
public static String filter(String a){
String count="";
String [] as = a.split("/");
if(as.length==2){
//分子分母同时存在负号,去掉负号
if(as[0].substring(0, 1).equals("-")&&as[1].substring(0, 1).equals("-")){
as[0]=as[0].substring(1, as[0].length());
as[1]=as[1].substring(1, as[1].length());
}
count=as[0]+"/"+as[1];
}else{
count=a;
}
return count;
}
/**
* 过滤结果集
* @param a
* @return
*/
public static String getint(String a){
String count="";
String [] as = a.split("/");
if(as.length==2){
if(Integer.parseInt(as[0]) % Integer.parseInt(as[1]) == 0){
count=String.valueOf(Integer.parseInt(as[0])/Integer.parseInt(as[1]));
}
}else{
count=a;
}
return count;
}
最后就是整个求解过程:
/**
*
* @param a 参数1
* @param b 参数2
* @param type 0代表加法,其他代表减法
* @return
*/
public static String plus(String a,String b,int type){
String count="";//计算结果
String umerator="";//分子
String denominator="";//分母
//字符串格式判断
a=getstandard(a);
b=getstandard(b);
String[] a1 = a.split("/");//a的分子和分母
String [] b1 = b.split("/");//b的分子和分母
int fenmu=Integer.parseInt(a1[1])*Integer.parseInt(b1[1]);
int fenzi;
if(type==0){
fenzi=Integer.parseInt(a1[0])*Integer.parseInt(b1[1])+Integer.parseInt(b1[0])*Integer.parseInt(a1[1]);
}else{
fenzi=Integer.parseInt(a1[0])*Integer.parseInt(b1[1])-Integer.parseInt(b1[0])*Integer.parseInt(a1[1]);
}
denominator=String.valueOf(fenmu);
umerator=String.valueOf(fenzi);
if(denominator.equals(umerator)){
count="1";
}else if(umerator.equals("0")){
count="0";
}else{
//加法计算
count=umerator+"/"+denominator;
}
return filter(count);
}
/**
*
* @param a 参数1
* @param b 参数2
* @param type 0代表乘法,1代表除法
* @return
*/
public static String getMul(String a,String b,int type){
String count="";
String umerator="";//分子
String denominator="";//分母
//字符串格式判断
a=getstandard(a);
b=getstandard(b);
String[] a1 = a.split("/");//a的分子和分母
String [] b1 = b.split("/");//b的分子和分母
if(type==1){
//除法
String temp;
temp=b1[0];
b1[0]=b1[1];
b1[1]=temp;
}
int fenzi=Integer.parseInt(a1[0])*Integer.parseInt(b1[0]);
int fenmu=Integer.parseInt(a1[1])*Integer.parseInt(b1[1]);
denominator=String.valueOf(fenmu);
umerator=String.valueOf(fenzi);
if(denominator.equals(umerator)){
count="1";
}else if(umerator.equals("0")){
count="0";
}else{
//加法计算
count=umerator+"/"+denominator;
}
return filter(count);
}
public static void main(String[] args) {
//获取方程个数和未知数个数
Scanner sr=new Scanner(System.in);
System.out.println("请输入方程个数:");
int equationNum=sr.nextInt();
System.out.println("请输入未知数个数:");
int unknownNum=sr.nextInt();
System.out.println("您输入的方程个数为:"+equationNum);
System.out.println("您输入的未知数个数为"+unknownNum);
String[][] fangcheng=new String[equationNum][unknownNum+1];
for(int i=0;i<equationNum;i++){
System.out.println("请输入第"+(i+1)+"个方程的未知数系数和值,系数之间用;隔开,例如1;2;3;4");
String xishu=sr.next();
String [] as = xishu.split(";");
for(int j=0;j<unknownNum+1;j++){
fangcheng[i][j]="".equals(as[j])?"0":as[j];
}
}
System.out.println("此时的方程为:");
for(int i=0;i<equationNum;i++){
for(int j=0;j<unknownNum+1;j++){
if(j != unknownNum){
System.out.print(fangcheng[i][j]+" ");
}else{
System.out.print("|"+fangcheng[i][j]);
}
}
System.out.println();
}
if(equationNum>unknownNum){
//无解
System.out.println("方程无解");
}else{
//有解
for(int i=0;i<equationNum-1;i++){
//选取主元素
//如果主元素为0,则找该列不为0的行交换
if(fangcheng[i][i].equals("0")){
//查找该列下不为0的行
for(int j=i+1;j<equationNum;j++){
if(!fangcheng[j][i].equals("0")){
//交换两行
for(int k=i;k<equationNum;k++){
for(int m=0;m<unknownNum+1;m++){
//交换操作
String temp;
temp=fangcheng[k][m];
fangcheng[k][m]=fangcheng[j][m];
fangcheng[j][m]=temp;
}
}
break;
}
}
System.out.println("交换后的方程为:");
for(int p=0;p<equationNum;p++){
for(int q=0;q<unknownNum+1;q++){
if(q==unknownNum){
System.out.print("|"+fangcheng[p][q]);
}else{
System.out.print(fangcheng[p][q]+" ");
}
}
System.out.println();
}
}
System.out.println("此时的主元素为:"+fangcheng[i][i]);
//进行消元操作,循环当前行下面的所有行,进行减法运算
for(int i1=i+1;i1<equationNum;i1++){
// float gys=fangcheng[i][i]/fangcheng[i1][i];
String gys=getMul(fangcheng[i][i], fangcheng[i1][i], 1);
System.out.println("公约数为:"+gys);
for(int i2=i;i2<unknownNum+1;i2++){
//为每一行从新赋值
if(!fangcheng[i1][i2].equals("0")){
// fangcheng[i1][i2]=(int) (fangcheng[i1][i2]*gys-fangcheng[i][i2]);
fangcheng[i1][i2]=plus(getMul(fangcheng[i1][i2], gys, 0), fangcheng[i][i2], 1);
}
}
}
System.out.println("计算后的上三角为:");
for(int p=0;p<equationNum;p++){
for(int q=0;q<unknownNum+1;q++){
if(q==unknownNum){
System.out.print("|"+fangcheng[p][q]);
}else{
System.out.print(fangcheng[p][q]+" ");
}
}
System.out.println();
}
//消元完成进行反转,当equationNum=unknownNum满秩的时候,有唯一解直接求解,当equationNum<unknownNum,有无数解;
}
//创建结果集
String[] x=new String[equationNum];
x[equationNum-1]=getint(getMul(fangcheng[equationNum-1][unknownNum], fangcheng[equationNum-1][equationNum-1], 1));
for(int i2=equationNum-2;i2>=0;i2--){
String sum="0";
for(int j1=i2+1;j1<equationNum;j1++){
sum=plus(getMul(fangcheng[i2][j1], x[j1], 0),sum , 0);
}
x[i2]=getint(getMul(plus(fangcheng[i2][unknownNum], sum, 1), fangcheng[i2][i2], 1));
}
//循环计算各个未知数
if(equationNum==unknownNum){
//满秩,唯一解
System.out.println(Arrays.toString(x));
}else if(equationNum<unknownNum){
//行满秩,无数解
System.out.println(x.toString());
}
}
}
我是先把方程消成上三角,然后一次求解,还有一种方法是消成上三角之后逆向消元,最后形成一种对角形式再一次求。
当然,如果真正深究下去,高斯消元里面还有很多细节需要注意,我这只是一个简单的实现,如果有更好的方法,请提出来,大家相互学习。
*************************2017年09月23日更新***********************************
经过这几天对单纯形算法的实现,逐渐发现上面这个高斯消元是存在一些问题的,问题出现的一下两个方面:
(1)、在进行矩阵运算的时候对0元素的处理不够严格;
(2)、考虑用数组表示分数,进行运算的时候,因为没有严格的进行约分操作,所有会导致经过几次简单运算后,数值超出int型最大长度,数据溢出,在改善的过程中,用BigInteger代替int,对大整数进行运算操作;
修改后的代码如下:
package com.zly.zyh;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Scanner;
/**
* 高斯消元法
* @author Administrator
*
*/
public class GaoSiXiaoYuanFuZhu {
/**
* 得到标准形式的字符串
* @param a
* @return
*/
public static String getstandard(String a){
// String umerator="";//分子
// String denominator="";//分母
String [] as = a.split("/");
if(as.length !=2){
a=a+"/1";
}
return a;
}
/**
* 过滤字符串,
* @param a
* @return
*/
public static String filter(String a){
String count="";
String [] as = a.split("/");
if(as.length==2){
//分子分母同时存在负号,去掉负号
if(as[0].substring(0, 1).equals("-")&&as[1].substring(0, 1).equals("-")){
as[0]=as[0].substring(1, as[0].length());
as[1]=as[1].substring(1, as[1].length());
}
count=getint(as[0]+"/"+as[1]);
}else{
count=a;
}
return count;
}
/**
* 过滤结果集
* @param a
* @return
*/
public static String getint(String a){
String count=a;
String [] as = a.split("/");
if(as.length==2){
BigInteger as0=new BigInteger(as[0]);
BigInteger as1=new BigInteger(as[1]);
if(String.valueOf(as0.remainder(as1)).equals("0")){
count=String.valueOf(as0.divide(as1));
}
}else{
count=a;
}
return count;
}
/**
*
* @param a 参数1
* @param b 参数2
* @param type 0代表加法,其他代表减法
* @return
*/
public static String plus(String a,String b,int type){
String count="";//计算结果
String umerator="";//分子
String denominator="";//分母
//字符串格式判断
a=getstandard(a);
b=getstandard(b);
String[] a1 = a.split("/");//a的分子和分母
String [] b1 = b.split("/");//b的分子和分母
BigInteger a10=new BigInteger(a1[0]);
BigInteger a11=new BigInteger(a1[1]);
BigInteger b10=new BigInteger(b1[0]);
BigInteger b11=new BigInteger(b1[1]);
BigInteger fenzi;
BigInteger fenmu=a11.multiply(b11);
// int fenmu=Integer.parseInt(a1[1])*Integer.parseInt(b1[1]);
// int fenzi;
if(type==0){
// fenzi=Integer.parseInt(a1[0])*Integer.parseInt(b1[1])+Integer.parseInt(b1[0])*Integer.parseInt(a1[1]);
fenzi=(a10.multiply(b11)).add((b10.multiply(a11)));
}else{
// fenzi=Integer.parseInt(a1[0])*Integer.parseInt(b1[1])-Integer.parseInt(b1[0])*Integer.parseInt(a1[1]);
fenzi=(a10.multiply(b11)).subtract((b10.multiply(a11)));
}
denominator=String.valueOf(fenmu);
umerator=String.valueOf(fenzi);
if(denominator.equals(umerator)){
count="1";
}else if(umerator.equals("0")){
count="0";
}else{
//加法计算
count=umerator+"/"+denominator;
}
return filter(count);
}
/**
*
* @param a 参数1
* @param b 参数2
* @param type 0代表乘法,1代表除法
* @return
*/
public static String getMul(String a,String b,int type){
String count="";
String umerator="";//分子
String denominator="";//分母
//字符串格式判断
a=getstandard(a);
b=getstandard(b);
String[] a1 = a.split("/");//a的分子和分母
String [] b1 = b.split("/");//b的分子和分母
if(type==1){
//除法
String temp;
temp=b1[0];
b1[0]=b1[1];
b1[1]=temp;
}
BigInteger a10=new BigInteger(a1[0]);
BigInteger a11=new BigInteger(a1[1]);
BigInteger b10=new BigInteger(b1[0]);
BigInteger b11=new BigInteger(b1[1]);
BigInteger fenzi=a10.multiply(b10);
BigInteger fenmu=a11.multiply(b11);
// int fenzi=Integer.parseInt(a1[0])*Integer.parseInt(b1[0]);
// int fenmu=Integer.parseInt(a1[1])*Integer.parseInt(b1[1]);
denominator=String.valueOf(fenmu);
umerator=String.valueOf(fenzi);
if(denominator.equals(umerator)){
count="1";
}else if(umerator.equals("0")){
count="0";
}else{
//加法计算
count=umerator+"/"+denominator;
}
return filter(count);
}
public static void gSXY(String a[][],int equationNum,int unknownNumS){
int unknownNum=unknownNumS-1;
String[][] fangcheng=a;
System.out.println("此时的方程为:");
for(int i=0;i<equationNum;i++){
for(int j=0;j<unknownNum+1;j++){
if(j != unknownNum){
System.out.print(fangcheng[i][j]+" ");
}else{
System.out.print("|"+fangcheng[i][j]);
}
}
System.out.println();
}
if(equationNum>unknownNum){
//无解
System.out.println("方程无解");
}else{
//有解
for(int i=0;i<equationNum-1;i++){
//选取主元素
//如果主元素为0,则找该列不为0的行交换
if(fangcheng[i][i].equals("0")){
//查找该列下不为0的行
for(int j=i+1;j<equationNum;j++){
if(!fangcheng[j][i].equals("0")){
//交换两行
for(int k=i;k<equationNum;k++){
for(int m=0;m<unknownNum+1;m++){
//交换操作
String temp;
temp=fangcheng[k][m];
fangcheng[k][m]=fangcheng[j][m];
fangcheng[j][m]=temp;
}
}
break;
}
}
System.out.println("交换后的方程为:");
for(int p=0;p<equationNum;p++){
for(int q=0;q<unknownNum+1;q++){
if(q==unknownNum){
System.out.print("|"+fangcheng[p][q]);
}else{
System.out.print(fangcheng[p][q]+" ");
}
}
System.out.println();
}
}
System.out.println("此时的主元素为:"+fangcheng[i][i]);
//进行消元操作,循环当前行下面的所有行,进行减法运算
for(int i1=i+1;i1<equationNum;i1++){
if(!fangcheng[i][i].equals("0")&&!fangcheng[i1][i].equals("0")){
String gys=getMul(fangcheng[i][i], fangcheng[i1][i], 1);
System.out.println("公约数为:"+gys);
for(int i2=i;i2<unknownNum+1;i2++){
//为每一行从新赋值
if(!fangcheng[i1][i2].equals("0")){
fangcheng[i1][i2]=plus(getMul(fangcheng[i1][i2], gys, 0), fangcheng[i][i2], 1);
}
}
}
}
System.out.println("计算后的上三角为:");
for(int p=0;p<equationNum;p++){
for(int q=0;q<unknownNum+1;q++){
if(q==unknownNum){
System.out.print("|"+fangcheng[p][q]);
}else{
System.out.print(fangcheng[p][q]+" ");
}
}
System.out.println();
}
}//正向结束
//反向开始
//消元完成进行反转,当equationNum=unknownNum满秩的时候,有唯一解直接求解,当equationNum<unknownNum,有无数解;
for(int m=equationNum-1;m>=0;m--){
System.out.println("反向旋转此时的主元素为:"+fangcheng[m][m]);
for(int m1=m-1;m1>=0;m1--){
if(!fangcheng[m][m].equals("0")&&!fangcheng[m1][m].equals("0")){
String gys=getMul(fangcheng[m][m], fangcheng[m1][m], 1);
System.out.println("公约数为:"+gys);
for(int m2=0;m2<unknownNum+1;m2++){
if(!fangcheng[m1][m2].equals("0")){
fangcheng[m1][m2]=plus(getMul(fangcheng[m1][m2], gys, 0), fangcheng[m][m2], 1);
}
}
}
}
System.out.println("计算后的对角矩阵为:");
for(int p=0;p<equationNum;p++){
for(int q=0;q<unknownNum+1;q++){
if(q==unknownNum){
System.out.print("|"+fangcheng[p][q]);
}else{
System.out.print(fangcheng[p][q]+" ");
}
}
System.out.println();
}
}//反向求解结束
//化简称单位矩阵
for(int n=0;n<equationNum;n++){
String fuzhu=fangcheng[n][n];
for(int n1=0;n1<unknownNum+1;n1++){
if(!fangcheng[n][n1].equals("0")){
fangcheng[n][n1]=getMul(fangcheng[n][n1], fuzhu, 1);
}
}
}
System.out.println("转化后的单位矩阵为:");
for(int p=0;p<equationNum;p++){
for(int q=0;q<unknownNum+1;q++){
if(q==unknownNum){
System.out.print("|"+fangcheng[p][q]);
}else{
System.out.print(fangcheng[p][q]+" ");
}
}
System.out.println();
}
}
}
public static void main(String[] args) {
String[][] a={{"5","6","7","8","38"},{"0","2","3","3","15"},{"6","3","4","5","24"}};
gSXY(a, 3, 5);
}
}
如果有更好的方法,请提出来,大家相互学习。