conArray[i][j]=$(".cells")[j].value;
}
Else If(j==varNum&&i>0){
conArray[i][j]=$(".sign"+(i))[0].value;
}
Else if(i>0){
If(j==varNum+1)
conArray[i][j]=$(".cells"+i)[j-1].value;
Else
conArray[i][j]=$(".cells"+i)[j].value;
}
}
}
说明:使用jquery的语法”$(“.cells”)”是为了简化编程编程,这里只使用了jquery的选择器用来代替javaScript的document.getElementById()。“.cells”和”.sign”,是前面所说的每行的class属性。
2.2显示线性规划数学模型模块
2.2.1模块描述
首先显示目标函数,根据变量个数VarNum显示出线性规划模型,“Max Z=”是目标函数的固定显示方式,其后的目标函数系数和变量的下标是根据每行的数组元素和变量个数VarNum决定的。显示的方式根据变量的不同有两种表达方法:若不是最后一个变量,X(i)后要加上“+”号;若是最后一个变量,则其后不需要加上“+”号,同时要是约束条件是系数如果是负数也不需要加上“+”号。如此目标函数的显示就完成了。
其次是约束条件的显示,约束条件中有一个非负约束。根据约束个数ConNum分行显示约束条件,变量个数VarNum+1在显示变量的情况下还要显示约束常量b,约束条件系数和约束常量都存放在约束条件系数数组ConArray()中。第一个约束条件前要显示“S.T.”,然后再显示约束条件,根据变量个数显示,最后一个变量后加上“<=b”,其他变量后边都自带一个“+”号。
最后显示非负约束,非负约束没有系数,只需要根据变量个数VarNum显示变量即可。与显示约束条件不同的是:各变量之间不是用“+”号连接,而是用“,”号分隔,最后一个变量后是“>=0”,这表示线性规划数学模型中的变量是非负的。
2.2.2关键算法描述
(1)显示目标函数的关键算法描述:
For (i = 1; i<=变量个数; i++){
If ( i == 最后一个变量||后一个变量的系数是负数)
最后一个变量后无“+”号
Else
不是最后一个变量则每个变量后都要加上“+”号
}
(2)显示约束条件的关键算法描述:
For (j = 1; j<= 约束个数; j++){
For ( i= 1; i<=变量个数+1; i++){
If ( i == 最后一个变量||后一个变量的系数是负数)
最后一个变量后无“+”号
Else
If(i == 变量个数+1)
则在每个约束条件后加上“<=”约束常量
Else
若不是最后一个变量则每个变量后边都自带一个“+”号
}
}
(3)显示非负约束的关键算法描述:
For (i = 1; i<= 变量个数; i++){
If ( i == 最后一个变量)
变量之后加上“>=0”
Else
不是最后一个变量则在变量后自带“,”号
}
备注:因为变量和约束系数的都存入到二维数组ConArray中,在显示线性规划模型时是从数组中拿出每个变量和约束系数的,上面“j<=约束条件个数”等for语句中的条件其实是根据数组的长度来定,这里这样写主要是为了让描述更容易理解,后面也是同样的。
2.3.1模块描述
线性规划的标准形式有四个特点:目标最大化、约束为等式、决策变量均非负、右端项非负。对于各种非标准形式的线性规划问题,将其转换为标准形式后用单纯形算法求最优解。
线性规划模型的规范形式:
线性规划模型的标准形式:
标准化模块与显示线性规划数学模型模块类似,唯一的区别在于:在每个约束条件后加了一个松弛变量,有几个约束条件就有几个松弛变量,另外在非负约束中把新增的松弛变量加入即可。
标准化后的目标函数与显示模块中的目标函数一致,无变化。
约束条件的标准化,根据约束个数ConNum分行标准化并显示约束条件,变量个数Varnum+1在标准化变量的情况下还要显示约束常量b,约束条件系数和约束常量都存放在约束条件系数数组ConArray( )中。第一个约束条件前要显示“S.T.”,然后再显示约束条件,根据变量个数显示并且每一个约束条件中要加一个松弛变量,松弛变量后加上“=b”,b为约束常量,非松弛变量后都自带一个“+”号,如果该变量的后一个变量是负数时就不需要加上一个“+”号。
标准化非负约束,非负约束没有系数,只需要根据变量个数VarNum和约束个数ConNum显示VarNum+ConNum个变量即可。与显示约束条件不同的是:其中加入了ConNum个松弛变量,而且各变量之间不是用“+”号连接,而是用“,”号分隔,最后一个变量后加上“>=0”,这表示线性规划数学模型中的变量是非负的。
同时在标准化之后要将ConArray中的数以及加好了松弛变量后松弛变量的系数从新倒入到一个新的数组ConArray1中,因为在标准化时本人是从数组中再拿出那些系数的,这样的话做起来比较方便,而且javaScript的一个好处就是在创建二维数组的时候不需要考虑你要是几行几列是数组,只要你往里面加了内容他会帮你自动扩充数组的长度。
2.3.2关键算法描述
(1)标准化约束条件的关键算法描述:
For (j = 1; j<=约束条件个数; j++){
For(i=1;i<=变量个数+1;i++){
If(i==变量个数+1)
最后一个变量后不显示“+”号
Else
{
If( i == 变量个数+1)
加入松弛变量和约束常量b
Else if(后一个变量的系数大于零)
每个变量后都自带一个“+”号
}
}
显示约束条件
}
说明:因为变量和约束系数的都存入到二维数组ConArray1中,在标准化的时候是从数组中拿出每个变量和约束系数的,上面“j<=约束条件个数”等for语句中的条件其实是根据数组的长度来定,这里这样写主要是为了让描述更容易理解,后面也是同样的。
(2)标准化非负约束的关键算法描述:
For (i = 1;i<=变量个数+约束个数;i++){
If (i == 最后一个变量)
变量后加上“>=”符号
Else
其他变量后加上“,”号
}
2.4.1模块描述
该模块的功能是完成线性规划数学模型的计算,应用单纯形算法计算最优解和最优目标值。首先找出初始基本可行基,这里都是用松弛变量作为基本可行基的,因为它们组合起来正好是一个单位矩阵,然后就是把ConArray1中的数再倒入到mydata数组中,其中mydata数组中的值正好跟初始单纯形表中的每个单元格的数一一对应的。这样是为了更好的生成单纯形表,同时在以后的迭代过程中只要在改变数组中的值就可以生成下一步迭代的单纯形表了。
接下来就开始计算,计算分为以下几个步骤:
(1)计算变量的检验数Check( ),本来只要计算非基变量的检验数,因为基变量的检验数肯定是0,但是为了计算简单同时因无论是基变量还是非基变量它们检验数的计算规则都是一样的,所以我们不管是不是非基变量都计算它的检验数,同时把新计算出来的检验数存入到mydata的最后一行,在计算出变量的检验数后,若检验数全小于或等于0,则当前解为最优解MaxVal,不用再计算;否则若存在检验数大于0,则选取最大检验数所对应的变量作为进基变量然后记录进基变量在二维数组mydata中的下标indexIn,本次计算的函数为InBase( )。
(2)根据进基变量在二维数mydata中的下标indexIn系数求出基变量在mydata中的下标indexOut。indexOut的计算规则是将mydata数组中存放b的那一列除以indexIn的那一列,如果indexIn的那一列有小于0的数则θ的对应那列的该行就用“-”代替,否则就存入那个商,找出mydata数组中存θ那列最小的那行,该行就是出基变量对应的那行记录他的系数就是indexOut。若θ那列的数全是“-”的话说明出基变量所对应的系数都小于0,这样就可以判断出该模型是没有有限最优解的。在indexIn和indexOut相交的那一个即mydata[indexOut-1][indexIn-1]存入的就是本次迭代的主元,本次计算的函数为OutBase( )。
(3)更新系数矩阵updataMydata( )。更新的方法是出基变量的系数行的各个系数除以主元后代替原来的系数。非出基变量的系数行乘以负的进基变量对应列与本行交汇的系数除以主元后加上本行的各个系数后的值代替原来的系数。约束常量也是这样计算并替换。在基变量BasVar( )中,进基变量替换出基变量的下标值。
(4)接下来再根据新的系数矩阵求检验数Check( )、进基变量InBase( )、出基变量OutBase r ( )、和更新系数矩阵updataMydata ( ),如此循环,直到计算出最优解。最优目标值MaxVal则等于基变量的目标函数系数与其对应约束常量b的累加和。
但是问题来了,循环几次呢?如何知道本次计算的MaxVal就是最优值呢?
我们可以通过一个递归来完成循环的次数,同用一个变量flag存放IndexIn是否在本次迭代后是否改变,如果改变就继续迭代如果没有改变说明本次就是最优的情况。
2.4.2关键算法描述
(1)迭代时递归关键算法描述:
Function 递归方法( ){
Check( );
InBase( );
OutBase( );
If(本次迭代后indexIn的值改变了){
调用本方法
}
}
(2)计算检验数关键算法描述:
基变量的检验数计算:
For(k = 1; k<=约束个数; k++){
是基变量则基变量所对应的检验数δ为0
}
非基变量的检验数计算:
For(g =1; g<=约束个数; g++){
检验数 = 基变量的目标函数系数×进基变量所对应的系数
}
(3)寻找进基变量关键算法描述:
For( i = 1; i<=变量个数+约束个数; i++){
If (检验数>0)
在大于零的检验数中找最大值其所对应的变量作为进基变量
}
(4)计算出基规则关键算法描述:
For(i = 1; i<=约束个数; i++){
If (进基变量的系数<=0 || 进基变量检验数<=0)
进基规则无穷大,用“-”表示
Else{
若是进基变量所对应的系数大于零则计算出基规则
出基规则=基变量所对应的约束常量÷进基变量所对应系数
}
}
(5)寻找出基变量的关键算法描述:
For (i = 1; i<=约束个数; i++){
If (出基规则中比较出最小的出基规则)
最小出基规则所对应的变量即为出基变量
}
(6)更新系数矩阵关键算法描述:
For(i = 1; i<=约束个数; i++){
If (非出基变量所对应的系数)
非出基变量的系数行=出基变量的系数行×-(进基变量对应列与本行交汇的系数÷主元)+本行的各个系数
Else
出基变量的系数行=出基变量的系数行×(1÷主元)
}
(7)计算临时最优解的关键算法描述:
For(i = 1; i<=约束个数; i++){
临时最优解=各基变量的目标函数系数×各约束常量系数的累加和
}
(8)生成单纯形表的关键算法
首先同过document.createElement("table");添加一个表格对象
For(i=1; i<=约束条件个数+3; i++){
在表格中插入一行
For(j=1; j<=更新后的系数矩阵的列数; j++){
在该行中插入个单元格(单元格中的内容等于mydata中的值)
}
}
说明:为了方便学生学习单纯形表格的迭代计算过程,本系统将所有迭代过程放入一个表格里,这样使学生浏览起来更加简单、明了。
2.5显示详细迭代过程模块
2.5.1模块描述
详细迭代过程模块旨在把计算的每一个过程及每个过程中变量的变化显示出来,让学习者更确切的懂得计算的细节。每次迭代中的计算方式都是相同的,直到求出最优解或无解才停止计算。其中每次迭代包括计算检验数δ、确定进基变量、计算出基规则θ以及计算临时最优解的详细计算过程。
其详细计算过程描述如下:
把变量名的信息全部存到一个二维数组StrArray中,就拿我们经常使用的模型来说StrArray中的内容如下表:
-- |
-- |
-- |
C(1) |
C(2) |
C(3) |
C(4) |
C(5) |
θ |
CB |
XB |
b |
X(1) |
X(2) |
X(3) |
X(4) |
X(5) |
C(3) |
X(3) |
b(1) |
A(1,1) |
A(1,2) |
A(1,3) |
A(1,4) |
A(1,5) |
-- |
C(4) |
X(4) |
b(2) |
A(2,1) |
A(2,2) |
A(2,3) |
A(2,4) |
A(2,5) |
-- |
C(5) |
X(5) |
B(2) |
A(3,1) |
A(3,2) |
A(3,3) |
A(3,4) |
A(3,5) |
-- |
-Z |
δ(1) |
δ(2) |
δ(3) |
δ(4) |
δ(5) |
-- |
详细迭代过程的表达式如:δ(1) = C(1)-C(3)*A(1,1)-C(4)*A(2,1)- C(5)*A(3,1) =1500-0*3-0*2-0*0=1500。这个表达式是通过字符串的拼接完成的,在迭代过程时是通过递归完成的,要完成详细迭代过程只需在迭代的方法中多加入几个函数,就可以完成详细迭代的过程,函数messageAbCheck完成检验数的详细迭代过程和该次最优解的详细迭代过程,函数calCoe()完成约束系数的详细迭代过程,最优解的详细迭代表达式为:Z=C(3)*b(1)+C(4)*b(2)+C(5)*b(3)=0-0*65+0*40+0*75=0。约束系数详细迭代过程的表达分为两种,当它同主元在同一行的时候表达式为:A(3,1)=A(3,1)/A(3,2)=0/3=0 (A(3,2)是主元) ,当与主元不在同一行的时候表达式是:
A(1,1)=A(3,1)*[-A(1,2)/A(3,2)]+A(1,1)=0* (-2/3)+3=3 。
是否显示详细迭代过程,根据各人的不同需要我们做了比较人性化的设计,若只需得到计算结果,就不显示详细计算过程;若是学习运筹学的算法,那么在运行软件的过程中就可以点击“显示详细迭代过程”按钮,就可以看到详细迭代过程,一目了然,过程清晰,比较容易理解单纯形法的原理。
2.5.2关键算法描述
(1)显示计算检验数详细及最优解过程的函数代码:
Function messageAbCheck(){
//mymessage存放
mymessage=mymessage+"
检验数的计算过程:
";
//拼接表达式一的字符串如C(1)-C(3)*A(1,1)-C(4)*A(2,1)-
C(5)*A(3,1)
var str1="";
//拼接表达式一的字符串如1500-0*3-0*2-0*0
var str2="";
//因为在strArray中最后一行检验数的计算是从第三列开始的
For(var i=3;i<加上松弛变量后约束系数的个数+3;i++){
str1="";
str2="";
str1=strArray[conNum+2][i]+"="+strArray[0][i]+"-";
str2=mydata[0][i]+"-";
For(var j=2;j
//从strArray数组中第一个存放检验数的单元开始拼接
//如果是最后一个是话
If(j==conNum+1){
str1=str1+strArray[j][0]+"*"+strArray[j][i]+"=";
str2=str2+mydata[j][0]+"*"+mydata[j][i]+"="+mydata[conNum+2][i]+"
";
}
//其他情况
else {
str1=str1+strArray[j][0]+"*"+strArray[j][i]+"-";
str2=str2+mydata[j][0]+"*"+mydata[j][i]+"-";
}
}
//把两段字符串加在一起
mymessage=mymessage+str1+str2;
}
//本次最优目标值的详细计算过程
//将str1,str2重新清空
str1="";
str2="";
mymessage=mymessage+"本次迭代最优目标值的详细计算过程:
";
For(var i=2;i
If(i==conNum+1){
str1=str1+strArray[i][0]+"*"+strArray[i][2]+"=";
str2=str2+mydata[i][0]+"*"+mydata[i][2]+"="+(-mydata[conNum+2][2])+"
";
}
else{
str1=str1+strArray[i][0]+"*"+strArray[i][2]+"+";
str2=str2+mydata[i][0]+"*"+mydata[i][2]+"+";
}
}
mymessage=mymessage+"Z="+str1+str2;
}
(2)显示更新系数矩阵详细过程的函数描述:
Function calCoe(){
mymessage=mymessage+"第"+(count)+"次迭代
";
mymessage=mymessage+"约束系数的详细计算过程:
";
mymessage=mymessage+"本次迭代主元为:"+strArray[indexOut] [indexIn] +"
";
var str1="";
var str2="";
//result是用来存放结果的数即表达式的计算结果
var result;
For(var i=2;i
For(var j=3;j
str1="";
str2="";
//如果跟主元在同一行的话
If(i==indexOut){
str1=strArray[i][j]+"="+strArray[i][j]+"/"+strArray[indexOut][indexIn]+"=";
str2=mydata[i][j]+"/"+mydata[indexOut][indexIn]+"=";
result=parseFloat((mydata[i][j]/mydata[indexOut][indexIn]).toFixed(2));
mymessage=mymessage+str1+str2+result+"
";
}
else {
str1=strArray[i][j]+"="+strArray[indexOut][j]+"*[-"+strArray[i][indexIn]+"/"
+strArray[indexOut][indexIn]+"]+"+strArray[i][j]+"=";
str2=mydata[indexOut][j]+"*["+(-mydata[i][indexIn])+"/"
+mydata[indexOut][indexIn]+"]+"+mydata[i][j]+"=";
//计算表达式的结果 result=parseFloat((parseFloat(mydata[indexOut][j])*(parseFloat(-mydata[i][indexIn])/parseFloat(mydata[indexOut][indexIn]))+ parseFloat(mydata[i][j])).toFixed(2));
//将表达式和结果都拼接到一起
mymessage=mymessage+str1+str2+result+"
";
}
}
}
}
4 软件测试
输入六组线性规划模型对软件进行测试,模型及其结果如下所示:
在输入模型界面输入如下数据:
变量个数:3
约束条件个数:4
-- |
X(1) |
X(2) |
X(3) |
b |
C(1) |
1 |
1 |
2 |
9 |
C(2) |
1 |
0 |
2 |
15 |
C(3) |
4 |
1 |
5 |
25 |
C(4) |
2 |
2 |
3 |
17 |
目标函数系数 |
10 |
20 |
15 |
-- |
计算后得到的结果:非基变量的检验数都不是正数,最优值是:170,最优解为:x(4)=0.5 ,x(5)=15 ,x(6)=16.5 , x(2)=8.5其他的未知数都等于0。
在输入模型界面输入如下数据:
变量个数:3
约束条件个数:2
-- |
X(1) |
X(2) |
X(3) |
b |
C(1) |
2 |
7 |
1 |
30 |
C(2) |
6 |
1 |
3 |
45 |
目标函数系数 |
15 |
30 |
28 |
-- |
计算后得到结果:非基变量的检验数都不是正数最优值是:467.44,最优解为:x(2)=2.3 ,x(3)=14.23 ,其他的未知数都等于0。
在输入模型界面输入如下数据:
变量个数:2
约束条件个数:4
-- |
X(1) |
X(2) |
b |
C(1) |
2 |
3 |
65 |
C(2) |
4 |
6 |
83 |
C(3) |
2 |
4 |
30 |
C(4) |
4 |
1 |
45 |
目标函数系数 |
2 |
1 |
-- |
计算后得到结果:非基变量的检验数都不是正数,最优值是:3293.62,最优解为:x(3)=37.14 ,x(4)=27.29 , ,x(2)=2.14 ,x(1)=10.71 ,其他的未知数都等于0。
在输入模型界面输入如下数据:
变量个数:2
约束条件个数:2
-- |
X(1) |
X(2) |
b |
C(1) |
2 |
-3 |
3 |
C(2) |
-1 |
1 |
5 |
Z |
3 |
2 |
-- |
计算后得到结果:进基变量所对应的系数小于等于0,所以无有限最优解
在输入模型界面输入如下数据:
变量个数:3
约束条件个数:3
-- |
X(1) |
X(2) |
X(3) |
b |
C(1) |
2 |
1 |
3 |
24 |
C(2) |
0 |
2 |
3 |
43 |
C(3) |
4 |
2 |
4 |
26 |
目标函数系数 |
34 |
62 |
15 |
-- |
计算后得到结果:非基变量的检验数都不是正数,最优值是: 806,最优解为:x(4)=11 ,x(5)=17 ,x(2)=13 ,其他的未知数都等于0。
在输入模型界面输入如下数据:
变量个数:4
约束条件个数:4
-- |
X(1) |
X(2) |
X(3) |
X(4) |
b |
C(1) |
3 |
2 |
1 |
4 |
45 |
C(2) |
3 |
5 |
2 |
4 |
65 |
C(3) |
2 |
3 |
5 |
2 |
34 |
|
2 |
4 |
2 |
5 |
35 |
目标函数系数 |
53 |
24 |
64 |
34 |
-- |
计算后得到结果:非基变量的检验数都不是正数,最优值是:837.45,最优解为:x(1)=14.69 ,x(6)=19.08 ,x(3)=0.92 , ,x(8)=3.77,其他的未知数都等于0。
由以上线性规划模型多次测试可知:《运筹学》线性规划模型单纯形教学演示程序运行正常,运算结果正确,有解和无解情况都能计算出来,该软件能够达到教学演示的目的。
[1] 吴祈宗.运筹学(第2版)[M].机械工业出版社
[2] 胡运权.运筹学教程(第二版).清华大学出版社
[3] 胡运权.运筹学导论(第8版).清华大学出版社
[4] 单东林,张晓菲,魏然.锋利的Jquery.人民邮电出版社
[5] 大藤幹,半场方人.HTML&CSS&JavaScript语法辞典.中国青年出版社
[6] 石磊.关于运筹学课程教学改革的几点思考.广西教育学院学报,2010年2期
[7] 唐开元,王华.浅析运筹学与计算机技术的结合.才智,2009年06期