前言:最近学习使用智能算法求解线性规划问题过程中,为了对算法结果进行比对,思前想后还是使用单纯形法进行验证。原因是单纯形法曾出现于运筹学课堂,并且使用编程语言编写过,还有那么些印象。
单纯形法:
具体步骤是,从线性方程组找出一个个的单纯形,每一个单纯形可以求得一组解,然后再判断该解使目标函数值是增大还是变小了,决定下一步选择的单纯形。通过优化迭代,直到目标函数实现最大或最小值。
原理描述可参考博客:运筹学——线性规划及单纯形法求解。
一般线性规划满足:
max z=c_1x_1+c_2x_2+…+c_nx_n
a_ij*x_j=b_j
x_j>=0
求解过程:
(1)若c_j均小于或者等于0,则当前为最优解;否则,进行下一步;
(2)使用max{c_j|c_j>0}计算进基列;
(3)使用min{b_j/a_ij|a_ij>0}计算变换行;
(4)进行行初等变换;
(5)得到最优解。
对于线性规划:
max z=2x_1+3x_2
2x_1+2x_2<=12
x_1+2x_2<=8
4x_1<=16
4x_2<=12
有最优解z=14,并且x_1=4以及x_2=2。
以下使用单纯形法求解该线性规划问题,表格形式为个人使用习惯
(1)建立如下单纯形表
其中:黄色区域表示b_j;
红色区域表示-z;
绿色区域表示a_ij;
蓝色区域表示b_j。
(2)确定进基列
(3)确定变换行
得到变换行(黄色),和进基列(棕色),
然后将该元素化为1,得到:
(4)行初等变换
(5)判断是否继续迭代
该元素大于0.则继续迭代。
(6)3次迭代以后,得到
根据表格得到计算结果:Z(4,2,0,0,0,4)=14
将该计算过程代码化即可得到原单纯形法求解线性规划问题。
(1)判断原则
//判断原则->c_j是否全小于等于0
double Principle(double target_col[var_num]) {
//初始化目标行
for (int j = 0; j < var_num; j++) {
target_col[j] = simplex_tab[0][j];
}
int count = 0;//计数
for (int i = 0; i < var_num; i++) {
if (target_col[i] <= 0)
count++;
}
if (count == var_num) {
optimal_val = simplex_tab[0][6] * (-1.0);
return true;//满足判断原则:目标系数<=0;
}
else
return false;
}
(2)进基列
//进基列
double EnterCol(double target_col[var_num]) {
//目标行最大的正数
int column_max;//记录最大列
double count_max = target_col[0];
column_max = 0;
for (int i = 1; i < var_num;i++) {
if (target_col[i] > 0) {
if (target_col[i] > count_max) {
count_max = target_col[i];
column_max = i;
}
}
}
return column_max;
}
(3)变换行
//进基行
double EnterRow() {
int row_min;//记录最小行
double specific_val[cons_num+1];//记录比值
int row_val[cons_num+1];//记录行号
for (int i = 0; i < cons_num+1;i++) {
specific_val[i] = -1;
row_val[i] = -1;
}
for (int i = 1; i < cons_num+1;i++) {
if ((simplex_tab[i][enter_var_col] > 0) && (simplex_tab[i][var_num] >= 0)) {
specific_val[i] = simplex_tab[i][var_num] / simplex_tab[i][enter_var_col];
row_val[i] = i;
}
}
for (int i = 0; i < cons_num + 1; i++) {
cout << specific_val[i] << " ";
}
cout << endl;
//比较大小
int count_min = 1000;
for (int i = 0; i < cons_num + 1; i++) {
if (specific_val[i] != -1) {
if (specific_val[i] < count_min) {
count_min = specific_val[i];
row_min = i;
}
}
}
return row_min;
}
(4)单纯形法
//简单单纯形法
void ClassSimplex(){
int count = 0;
//while(count<3){
while (Principle(target_col)==false) {
Principle(target_col);
enter_var_col = EnterCol(target_col);//确定进基列
enter_var_row = EnterRow();//确定进基行
cout << enter_var_col << endl;
cout << enter_var_row << endl;
//进基行列化为1
double row_col_val = simplex_tab[enter_var_row][enter_var_col];
for (int j = 0; j < var_num + 1;j++) {
simplex_tab[enter_var_row][j] = double(int(simplex_tab[enter_var_row][j]*100/ row_col_val))/100.0;
}
for (int i = 0; i < cons_num + 1; i++) {for (int j = 0; j < var_num + 1; j++) {cout << simplex_tab[i][j] << " ";}cout << endl;}cout << endl;
//行初等变换
for (int i = 0; i < cons_num + 1;i++) {
double count_change = (-1.0)*simplex_tab[i][enter_var_col] / simplex_tab[enter_var_row][enter_var_col];
cout << endl<< count_change << endl;
if (i != enter_var_row) {
for (int j = 0; j < var_num + 1; j++) {
simplex_tab[i][j] += simplex_tab[enter_var_row][j] * count_change;
}
}
}
for (int i = 0; i < cons_num + 1; i++) { for (int j = 0; j < var_num + 1; j++) { cout << simplex_tab[i][j] << " "; }cout << endl; }cout << endl;
count++;
}
cout << "最优解:" << optimal_val << endl;
}
(5)变量值
//对应的变量值
void SolveVal(double simplex_tab[cons_num + 1][var_num + 1]) {
//初始化变量值
for (int i = 0; i < var_num;i++) {
var_value[i] = 0;
}
double column_val[cons_num+1];//记录列值
for (int j = 0; j < var_num + 1;j++) {
int count_0 = 0;
int count_1 = 0;
int count_1_index = 0;
for (int i = 0; i < cons_num + 1; i++) {
column_val[i] = simplex_tab[i][j];
if (column_val[i] == 0)
count_0++;
if (column_val[i] == 1) {
count_1_index = i;
count_1++;
}
}
if (count_0==cons_num && count_1==1) {
var_value[j] = simplex_tab[count_1_index][var_num];
}
}
cout << "变量值:";
for (int j = 0; j < var_num + 1; j++) {
cout << var_value[j] << " ";
}
cout << endl;
}
备注:原单纯形法求解线性规划问题是比较直观的,但计算过程复杂。在实际应用过程中,改进单纯形法的应用效果是显著的。