1. 程式人生 > >單純形法演算法實現--java版

單純形法演算法實現--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);
	}
}

單純形法在實現的過程中會出現很多難以考慮的問題,雖然我力求完美,但是還有很多地方難以把控,如有更好的方法,歡迎提出。