Matlab二阶段单纯形法
- dcxsf.m
- secP.m
- .m(test)
dcxsf.m
% 第一阶段函数
function [X,zR] = dcxsf(A,b,cOut,outMin,cSec,secMin)
format rat %数据以分数形式输出
%如果是求min,需要化为max = -min,此处是把系数求负,
%后续还有一步是,求出zR后,再给他求负
if outMin == 1
cOut = -cOut;
end
% my ny 分别为目标函数系数矩阵的行和列,此题my = 1, mn = 6;
[my ny]=size(cOut);
Ay = []; % 存储人工变量所在列的索引,即人工变量在第几列
AyR = []; % 存储非人工变量所在列的索引
yIndex = 0;
yRIndex = 0;
%分别找出人工变量和非人工变量,然后分别存入Ay 和 AyR里
for j = 1:ny
if cOut(j)~=0
yIndex = yIndex + 1;
Ay(yIndex) = j;
else
yRIndex = yRIndex + 1;
AyR(yRIndex) = j;
end
end
% my ny 分别为约束系数矩阵的行和列,此题mOutSize = 3, nOutSize = 6;
[mOutSize,nOutSize]=size(A);
% AmOut 存储基变量的索引,比如基变量之一为x3,则把3存入AmOut,用于记录那些变量是基变量
% Am1Out 存储非基变量的索引,比如非基变量之一为x4,则把4存入Am1Out,用于记录那些变量是非基变量
AmOut = zeros(mOutSize,1);
Am1Out = zeros(1,nOutSize - mOutSize);
% x1 x2 x3 x4 x5 x6
% 1 2 1 1 0 0
% 1 2 3 0 1 0
% 2 1 5 0 0 1
% 其中x4 x5 x6 为基变量,把基变量对应索引(第几列)存入AmOut,非基变量索引存入Am1Out
% 接下来这个for就是做这个工作的
iAm1Out = 0;
% 依次遍历每列
for jj = 1:nOutSize %jj 列数
flag1 = 0;
% 对这一列遍历行,检测是否有1,
for ii = 1:mOutSize % ii 行数
if A(ii,jj)==1
flag1 = ii;
end
end
% 没有1的话则说明这列不可能是基变量所在的列,把索引(第几列)存入Am1Out中
if flag1 == 0 %没有1
iAm1Out = iAm1Out + 1;
Am1Out(iAm1Out) = jj;
continue; % 如果能到此步,说明这一行的检测完毕,为非基变量所在的列,continue出去进行下一行的检测
end
% 再检测这一列的其他元素是不是都是0,是0则说明这一列是基变量在的列,把索引(第几列)存入AmOut
% 如果有不是0的元素,则说明这列是非基变量在的列,把索引存入Am1Out中
continueAll = 0;
for ii = 1:mOutSize %jj 行数
if ii == flag1
continue;
end
if A(ii,jj)~=0
iAm1Out = iAm1Out + 1;
Am1Out(iAm1Out) = jj;
continueAll = 1;
break;
end
end
if continueAll == 1
continue;
end
AmOut(flag1) = jj;
end
% 把b加入到A最后一列,方便后面的换元迭代中的运算
% 1 2 1 1 0 0 10
% 1 2 3 0 1 0 15
% 2 1 5 0 0 1 20
A = [A b];
% G存入的是检验数,也就是书中 cj - zj , j = m+1,...,n
% zj = ci*aij , i= 1,...,m ,j = m+1,...,n
% 求 cj ,就是从Am1Out(非基变量)中取出索引(列数),到目标函数系数矩阵cOut去找对应的系数
% 对应求ci,就是从AmOut(基变量)中取出索引,到目标函数系数矩阵cOut去找对应的系数
% 求 aij ,其中i 为行数,j为每一行非基变量的列数,到 A(约束系数矩阵)中取出相应的系数
G = zeros(1,nOutSize - mOutSize);
for j = 1:nOutSize - mOutSize
G(j) = cOut(Am1Out(j));
for i = 1:mOutSize
G(j) = G(j) - cOut(AmOut(i))*A(i,Am1Out(j));
end
end
% decide:找出检验数中大于0的数的索引(列数),存入decide中
decide = find(G(:)>0);
[dRow,dCol]=size(decide);
while 1
if isempty(decide) %检验数全部小于0
disp('第一阶段最优解')
% 计算最优解,如果最优解等于0,进入第二阶段,如果不等于0,无可行解
zR = 0;
for i = 1:mOutSize
zR = zR + cOut(AmOut(i))*A(i,nOutSize + 1);
end
if zR ~= 0
disp('无可行解')
X = [];
zR = [];
break;
else
% 如果基变量中有人工变量,删除人工变量
% Adelete:AmOut中记录着基变量,Ay记录着人工变量
% 依次遍历AmOut,检验AmOut中有没有Ay中的元素,如果有的话则保存行数到Adelete中
Adelete = [];
deIndex = 0;
for i = 1:mOutSize
for iY = 1:yIndex
if AmOut(i) == Ay(iY) %基中含有自由变量
zeroAll = 1; %指系数全部为0
for iYR = 1:yRIndex %ny - yIndex
if A(i,AyR(iYR)) ~= 0
zeroAll = 0; %指系数有不全为0的
break;
end
end
if zeroAll == 1
%删除基变量中的人工变量行和人工变量列
deIndex = deIndex + 1;
Adelete(deIndex) = i;
else % zeroAll == 0
% 此处是另外一种情况,也就是,最优解是基变量中含有人工变量,
% 而此时非基变量的系数不全为0,网上说这种情况还要进行换元迭代,
% 但那是没有具体例子进行分析,所以先放着未写,如果出现这种情况
% 则会disp输出'出现系数不全为0的情况',自己知道就行
%如果有合适的例子可以学习下就好编了
disp('出现系数不全为0的情况')
end
break;
end
end
end
% 此处是删除人工变量所在的行
% (其实是把除了人工变量(基变量中的人工变量)所在的行外,其他的行复制到secA里了)
% 如果没有人工变量,就不会删除任何东西
secA = [];
dsAIndex = 0;
for i = 1:mOutSize
wIn = 0;
for j = 1:deIndex
if i==Adelete(j)
wIn = 1;
break;
end
end
if wIn == 0
dsAIndex = dsAIndex + 1;
secA(dsAIndex,:) = A(i,:);
end
end
secb = secA(:,nOutSize+1);
% 此处是删除人工变量所在的列(其实是把除了人工变量所在的列外,其他的列复制到secAA里了)
% 人工变量都在Ay中记录着呢
secAA = [];
dsAAIndex = 0;
for i = 1:nOutSize
wIn = 0;
for j = 1:yIndex
if i == Ay(j)
wIn = 1;
break;
end
end
if wIn == 0
dsAAIndex = dsAAIndex + 1;
secAA(:,dsAAIndex) = secA(:,i);
end
end
% 调用第二阶段的函数,基本思路与第一阶段相似
[X,zR] = secP(secAA,secb,cSec,secMin);
break;
end
else % 如果检验数中有大于0的,则要换元迭代
%进行换基运算
% ------------------------------------------------------------
% 此处是遍历G,找出检验数中最大的那个,并存储他的列数
% decide(jMax) 就是检验数中最大的那个的列数
jMax = 1;
for j = 2:dRow
if G(decide(j)) > G(decide(jMax))
jMax = j;
end
end
% ************************************************************
% ------------------------------------------------------------
% 此处是找出最小比值,上一步是确定换出基的列数,这步是确定换出基的行数,
% 即是iMin
iMin = 0;
ratioM = inf;
for ii = 1:mOutSize
if A(ii,Am1Out(decide(jMax)))<=0 %不需要负的系数
continue;
end
%下面条件 系数均大于0
ratioP = A(ii,nOutSize+1)/A(ii,Am1Out(decide(jMax)));
if ratioP < ratioM
ratioM = ratioP;
iMin = ii;
end
end
% ************************************************************
% ------------------------------------------------------------
% 这个地方是对其他行进行初等变换,保证这行是类似 1 0 0 0 的形式
A(iMin,:) = A(iMin,:)/A(iMin,Am1Out(decide(jMax)));
for i = 1:mOutSize
if i ~= iMin
A(i,:) = A(i,:) - A(i,Am1Out(decide(jMax))).*A(iMin,:);
end
end
% ************************************************************
% ------------------------------------------------------------
% 这一步是一次迭代后,更新 AmOut 和 Am1Out
proc = AmOut(iMin);
AmOut(iMin) = Am1Out(decide(jMax));
Am1Out(decide(jMax)) = proc;
% ************************************************************
end
% ------------------------------------------------------------
% 再次计算检验数
G = zeros(1,nOutSize - mOutSize);
for j = 1:nOutSize - mOutSize
G(j) = cOut(Am1Out(j));
for i = 1:mOutSize
G(j) = G(j) - cOut(AmOut(i))*A(i,Am1Out(j));
end
end
decide = find(G(:)>0);
[dRow,dCol]=size(decide);
% ************************************************************
% 再次调转回头进行while循环
end