單純形法演算法實現--java版
阿新 • • 發佈:2019-01-04
一般線性規劃問題具有線性方程組的變數數大於方程個數,這時會有不定的解。當決策變數個數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);
}
}
單純形法在實現的過程中會出現很多難以考慮的問題,雖然我力求完美,但是還有很多地方難以把控,如有更好的方法,歡迎提出。