一般线性规划问题具有线性方程组的变量数大于方程个数,这时会有不定的解。当决策变量个数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);
}
}
单纯形法在实现的过程中会出现很多难以考虑的问题,虽然我力求完美,但是还有很多地方难以把控,如有更好的方法,欢迎提出。