单纯形法算法实现--java版

一般线性规划问题具有线性方程组的变量数大于方程个数,这时会有不定的解。当决策变量个数n和约束条件个数m较大时,单纯形法是求解线性规划问题的通用方法。

对于单纯形法的数学运算,那是理学院学生应该关注的问题,如果有不懂的,大家可以自行百度,我这里只关注用程序实现单纯形法;

本文的单纯形法算法实现,严格按照书本的计算过程实现,建议阅读前对书本进行学习,对基本步骤了解;

文中用到了之前的一篇博文高斯消元法解线性方程--Java实现,大家可以先对高斯消元了解一下,本文关于高斯消元部分,只贴代码,不做讲解。

实现源码:

package com.zly.zyh;

import java.math.BigInteger;
import java.util.Scanner;

import com.sun.org.apache.regexp.internal.recompile;

/**
 * 单纯形算法
 * @author 郑立源
 * @time 2017年09月23日  00:32
 */
public class SimpleMethod {
	private GaoSiXiaoYuanFuZhu gaoSiXiaoYuanFuZhu;
	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");
//			String xishu=sr.next();
//			String [] as = xishu.split(";");
//			for(int j=0;j<unknownNum;j++){
//				fangcheng[i][j]="".equals(as[j])?"0":as[j];
//			}
//		}
//		//函数的结果集合
//		System.out.println("请输入每个方程的结果,用;隔开");
//		String[] result=(sr.next()).split(";");
//		for(int i=0;i<equationNum;i++){
//			for(int j=0;j<equationNum;j++){
//				fangcheng[i][unknownNum]=result[i];
//			}
//		}
//		//请求用户输入目标函数
//		String[] objectFunction=new String[unknownNum];
//		System.out.println("请按顺序输入目标函数中各变量的系数,系数之间用;隔开,例如1;2;3");
//		String ratio=sr.next();
//		String [] as2 = ratio.split(";");
//		for(int i=0;i<unknownNum;i++){
//			objectFunction[i]=as2[i].equals("")?"0":as2[i];
//		}
		//方程
		String[][] fangcheng=new String[][]{{"5","6","7","8","38"},{"2","2","3","3","15"},{"6","3","4","5","24"}};
		//目标函数
		String[] objectFunction=new String[]{"1","2","3","4"};
		int equationNum=3;
		int unknownNum=4;
		//组合矩阵和目标函数
		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();
		}
		GaoSiXiaoYuanFuZhu.gSXY(fangcheng, equationNum, unknownNum+1);
		//输出可行解
		String[] resultNum=new String[unknownNum];
		//初始化可行基
		for(int i=0;i<unknownNum;i++){
			resultNum[i]="0";
		}
		for(int i=0;i<equationNum;i++){
			resultNum[i]=fangcheng[i][unknownNum];
		}
		for(int i=0;i<resultNum.length;i++){
			if(i==0){
				System.out.print("初始化可行基为:["+resultNum[i]+",");
			}else if(i==resultNum.length-1){
				System.out.print(resultNum[i]+"]");
			}else{
				System.out.print(resultNum[i]+",");
			}
		}
		System.out.println();
		System.out.println("********************************************************************");
		getOptimumSolution(fangcheng, objectFunction, equationNum, unknownNum);
	}
	
	/**
	 * 循环获得最优化
	 * @param fangcheng 矩阵方程(包含结果即可行解)
	 * @param objectFunction 目标函数
	 * @param equationNum 方程个数
	 * @param unknownNum 未知数个数
	 * 1、循环求各个变量的检验数,都<=0,证明当前可行解即是最优解,否则,进行2
	 * 2、求出还如变量,求出换出变量
	 * 3、旋转运算
	 * 4、递归调用此函数;
	 */
	public static void getOptimumSolution(String[][] fangcheng,String[] objectFunction,int equationNum,int unknownNum){
		System.out.println("*******************************************************");
		
		//检验数数组
		String[] examine=new String[unknownNum];
		for(int i=0;i<unknownNum;i++){
			if(i<equationNum){
				examine[i]="0";
			}else{
				String sum="0";
				for(int j=0;j<equationNum;j++){
					sum=GaoSiXiaoYuanFuZhu.plus(sum, GaoSiXiaoYuanFuZhu.getMul(fangcheng[j][i], objectFunction[j], 0), 0);
				}
				examine[i]=GaoSiXiaoYuanFuZhu.plus(objectFunction[i], sum, 1);
			}
		}
		System.out.println("此时的检验数为:");
		for(int i=0;i<unknownNum;i++){
			if(i==0){
				System.out.print("["+examine[i]+",");
			}else if(i==unknownNum-1){
				System.out.print(examine[i]+"]");
			}else{
				System.out.print(examine[i]+",");
			}
		}
		System.out.println();
		int bi=0;
		for(int j=0;j<examine.length;j++){
			if(examine[j] != null){
				if(!examine[j].equals("0")&&!(examine[j].toString().substring(0, 1)).equals("-")){
					bi=1;
				}
			}
		}
		if(bi==0){
			System.out.println("当前可行解为最优解!");
			//输出可行解
			String[] resultNum=new String[unknownNum];
			//初始化可行基
			for(int i=0;i<unknownNum;i++){
				resultNum[i]="0";
			}
			for(int i=0;i<equationNum;i++){
				resultNum[i]=fangcheng[i][unknownNum];
			}
			for(int i=0;i<resultNum.length;i++){
				if(i==0){
					System.out.print("最优解为:["+resultNum[i]+",");
				}else if(i==resultNum.length-1){
					System.out.print(resultNum[i]+"]");
				}else{
					System.out.print(resultNum[i]+",");
				}
			}
			System.out.println();
			return;
		}
		//找出换入变量,检验数最大的那一列
		int swapin=getMax(examine);
		System.out.println("此时的换入变量为第"+swapin+"列");
		//计算换出变量
		//换出变量数组
		String[] swapoutArray=new String[equationNum];
		for(int i=0;i<equationNum;i++){
			//结果集除以换入列的系数
			if(fangcheng[i][swapin-1].equals("0")){
				swapoutArray[i]=null;
			}else{
				swapoutArray[i]=GaoSiXiaoYuanFuZhu.getMul(fangcheng[i][unknownNum], fangcheng[i][swapin-1], 1);
			}
		}
		System.out.println("此时的换出变量数组为:");
		for(int i=0;i<equationNum;i++){
			if(i==0){
				System.out.print("["+swapoutArray[i]+",");
			}else if(i==equationNum-1){
				System.out.print(swapoutArray[i]+"]");
			}else{
				System.out.print(swapoutArray[i]+",");
			}
		}
		System.out.println();
		//求出换出变量,检验数最消的那一行
		int swapout=getMin(swapoutArray);
		System.out.println("此时的换出变量为第"+swapout+"行");
		System.out.println("故此时的主元素为方程组第"+swapout+"行和第"+swapin+"列的交叉出,此元素为:"+fangcheng[swapout-1][swapin-1]);
		//消元
		//更换列,并且更换目标函数的位置
		for(int i=0;i<equationNum;i++){
			String temp;
			temp=fangcheng[i][swapin-1];
			fangcheng[i][swapin-1]=fangcheng[i][swapout-1];
			fangcheng[i][swapout-1]=temp;
		}
		//交换目标函数
		String objectTemp;
		objectTemp=objectFunction[swapin-1];
		objectFunction[swapin-1]=objectFunction[swapout-1];
		objectFunction[swapout-1]=objectTemp;
		//调用高斯进行消元
		GaoSiXiaoYuanFuZhu.gSXY(fangcheng, equationNum, unknownNum+1);
		//将更换过的矩阵,目标函数进行递归
		getOptimumSolution(fangcheng, objectFunction, equationNum, unknownNum);
	}
	
	//得到数组中最大的元素位置
	public static int getMax(String[] a){
		//默认最大数为第一个
//		String count_=a[0]==null?"0":a[0];
		String count_=a[0];
		int maxId=0;
		for(int i=1;i<a.length;i++){
			//求每个数和当前最大数的差
			String differ=GaoSiXiaoYuanFuZhu.plus(a[i], count_, 1);
			//如果这个差不为负数,并且不为0,则证明当前叔大于最大数
			if(!differ.substring(0, 1).equals("-")&&!differ.substring(0, 1).equals("0")){
				count_=a[i];
				maxId=i;
			}
		}
		return maxId+1;
	}
	//得到数组中最小的元素位置
	public static int getMin(String[] a){
		String count_=a[0];
		int minId = 0;
		for(int i=0;i<a.length;i++){
			if(!(a[i]==null)){
				count_=a[i];
				minId=i;
				break;
			}
		}
		for(int j=0;j<a.length;j++){
			if(a[j] != null){
				String differ=GaoSiXiaoYuanFuZhu.plus(a[j], count_, 1);
				//如果差为负数
				if(differ.substring(0, 1).equals("-")){
					count_=a[j];
					minId=j;
				}
			}
		}
		return minId+1;
	}
}
上述代码中用到的高斯消元代码:

package com.zly.zyh;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Scanner;
/**
 * 单纯形法需要用到的高斯消元法
 * @author 郑立源
 * @time 2017年09月22日 19:30
 */
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);
	}
}

单纯形法在实现的过程中会出现很多难以考虑的问题,虽然我力求完美,但是还有很多地方难以把控,如有更好的方法,欢迎提出。

猜你喜欢

转载自blog.csdn.net/zly412934578/article/details/78068123
今日推荐