工程计算助手
1 概述 | 3 基本计算 | 4 软件组成与结构 |
2 基础知识 | 3.1 表达式计算 | 4.1 文件及文件夹 |
2.1 启动界面及工作模式 | 3.2 求和与求积 | 4.2 开放式软件系统 |
2.2 代码格式 | 3.3 解方程 | 5 更详细的帮助 |
2.3 整数、实数、复数和三维向量计算 | 3.4 微积分 | 6 问题及建议 |
2.4 常用数学函数 | 3.5 微分方程求解 | |
2.5 运算符 | 3.6 优化拟合 | |
2.6 函数及模块 | 3.7 矩阵运算 | |
2.7 使用命名空间 | 3.8 使用常量文件 | |
2.8 流程控制 | 3.9 自定义函数库 | |
2.9 结果输出 | 3.10 创建命令菜单 | |
2.10 代码重用 |
本程序内部名称为“开放式计算软件OpenLu”,本说明称之为“工程计算助手”,旨在以工程计算助手的方式实现开放式计算。
本软件力求方便快捷地进行各种工程数值计算。无需专门学习,通过看实例做计算是本软件的基本特点。基本计算内容包括表达式计算、解非线性方程(组)、多元积分、微分方程求解、参数优化拟合、矩阵运算等等。
OpenLu启动时界面上有2个窗口,上面是代码窗口,下面是运算结果输出窗口。
OpenLu工作模式有三种,可通过命令菜单进行切换:
(1)普通编译模式:在代码窗口写好代码后,通过菜单、工具栏或快捷键F8进行编译计算。
(2)即时编译模式:在代码窗口写代码时,即时给出代码中的错误。
(3)即时编译计算模式:在代码窗口写代码时,即时给出代码中的错误,若没有错误,将进行计算给出结果。
为了更好地使用OpenLu时,建议进行以下操作:
(1)给OpenLu创建一个快捷方式,然后把该快捷方式放在桌面上或“开始”菜单中。
(2)用OpenLu打开文件夹“Ini”中的文件“OpenLu.ini”(通常会提示该文件已经打开),或者其他自定义的工作区文件。
a、执行菜单命令:设置 -> 设置当前文件为工作区。
b、执行菜单命令:设置 -> 设置当前文件为缺省工作区。
2.2 代码格式 [返回页首]
OpenLu由Lu脚本支持,采用Lu脚本源代码格式。简单地,即:源代码文件由若干函数(或表达式)组成,函数(或表达式)由分号分隔,函数(或表达式)由语句组成,语句由逗号、冒号或分号分隔,函数(或表达式)中可使用三对等价的括号( )、[ ]和{ },源代码中可使用C++风格的注释。如下例:
//每行中两个反斜杠后的内容为注释
/*
这是多行注释。
这是多行注释。
*/
2.5+sin[1.2-cos(0.8)];
sin[2.3-5i]; //i表示一个虚数
2.3 整数、实数、复数和三维向量计算 [返回页首]
2+20/3; //数字中不带小数点时进行整数运算。例如:20/3=6
2+20./3; //数字中带小数点时进行实数运算。例如:20./3=6.666666666666667,本例中3虽然是整数,但自动转换为实数进行计算
2+3i; //数字后的i表示该数是一个虚数
2$3; //运算符并“$”将2个实数(包含整数)转换为一个复数
2$3$5; //运算符并“$”将1个复数和一个实数(包含整数)合并为一个三维向量
(2+3i)$5; //运算符并“$”将1个复数和一个实数(包含整数)合并为一个三维向量
可以看出,Lu脚本可自动为数学混合算式进行数据类型转换,低一级数据类型将自动转换为高一级数据类型,即:整数→实数→复数→三维向量。
函 数 | 参数类型 | 说 明 |
sin(x) | 实数、复数 | 正弦函数 |
cos(x) | 实数、复数 | 余弦函数 |
tan(x) | 实数、复数 | 正切函数 |
asin(x) | 实数、复数 | 反正弦函数 |
acos(x) | 实数、复数 | 反余弦函数 |
atan(x) | 实数、复数 | 反正切函数 |
sqrt(x) | 实数、复数 | 平方根函数 |
exp(x) | 实数、复数 | 指数函数 |
ln(x) | 实数、复数 | 自然对数函数 |
lg(x) | 实数、复数 | 常用对数函数 |
sinh(x) | 实数、复数 | 双曲正弦函数,[exp(x)-exp(-x)]/2 |
cosh(x) | 实数、复数 | 双曲余弦函数,[exp(x)+exp(-x)]/2 |
tanh(x) | 实数、复数 | 双曲正切函数,[exp(x)-exp(-x)]/[exp(x)+exp(-x)] |
abs(x) | 实数、复数 | 绝对值函数 |
floor(x) | 实数 | 返回不大于x的最大整数 |
ceil(x) | 实数 | 返回不小于x的最小整数 |
itor(x) | 整数 | 将整数转换成实数,大数转换时有精度损失 |
rtoi(x) | 实数 | 将实数转换成整数,大数转换时有误差 |
con(x) | 复数 | 求复数的共轭复数 |
atan2(x,y) | 实数 | 反正切函数,求x/y的反正切值,所在象限由x和y的符号确定 |
fmod(x,y) | 实数 | 求x/y的余数 |
Lu支持的运算符比较多,大家只使用自己熟悉的运算符即可。
运算符类型 | 运算符 | 名称 | Lu核心库支持的运算 | 说 明 |
双括号连接运算符 | := | 双括号连接 | 冒号前和等号后都必须是括号 | |
单括号连接运算符 | ( )= | 单括号连接 | 等号后是一个表达式 | |
[ ]= | 单括号连接 | |||
{ }= | 单括号连接 | |||
括号运算符 | ( ) | 小括号 | 括号运算 | 返回最后一个语句的值 |
[ ] | 中括号 | 括号运算 | ||
{ } | 大括号 | 括号运算 | ||
命名空间成员访问符 | :: | 双冒号 | 访问命名空间成员 | 访问命名空间成员 |
对象成员运算符 | . | 点 | 访问对象成员或传递函数参数 | 也称为函数参数运算符,或者变量函数调用运算符 |
后置单目运算符 | ++ | 后置自增 | 整数、实数 | 后置单目运算符(自增、自减、转置、点转置) |
-- | 后置自减 | 整数、实数 | ||
' | 转置 | 未定义 | ||
.' | 点转置 | 未定义 | ||
前置单目运算符 | ! | 非 | 逻辑值、整数、实数 | 前置单目运算符(非、正、负、自增、自减、按位非) 对整数或实数求非时,返回逻辑值,且规定!0=true,!0.0=true,其他情况均返回false。 |
+ | 正 | 被忽略 | ||
- | 负 | 整数、实数、复数、三维向量 | ||
++ | 前置自增 | 整数、实数 | ||
-- | 前置自减 | 整数、实数 | ||
!! | 按位非 | 整数 | ||
乘方算术运算符 | ^ | 乘方 | 整数、实数、复数 | 算术运算符(乘方、点乘方) |
.^ | 点乘方 | 未定义 | ||
乘除算术运算符 | * | 乘 | 整数、实数、复数、三维向量 | 算术运算符(乘、左除、右除、求模、点乘、点左除、点右除) |
/ | 左除 | 整数、实数、复数 | ||
\ | 右除 | 未定义 | ||
% | 求模 | 整数 | ||
.* | 点乘 | 未定义 | ||
./ | 点左除 | 未定义 | ||
.\ | 点右除 | 未定义 | ||
加减算术运算符 | + | 加 | 整数、实数、复数、三维向量、字符串 | 算术运算符(加、减) |
- | 减 | 整数、实数、复数、三维向量 | ||
移位运算符 | << | 左移位 | 整数 | 左移位、右移位 |
>> | 右移位 | 整数 | ||
关系运算符 | > | 大于 | 整数、实数 | 关系运算符(大于、大于等于、小于、小于等于、等于、不等于) |
>= | 大于等于 | 整数、实数 | ||
< | 小于 | 整数、实数 | ||
<= | 小于等于 | 整数、实数 | ||
== | 等于 | 整数、实数 | ||
!= | 不等于 | 整数、实数 | ||
按位与 | && | 按位与 | 整数 | 按位与 |
按位异或 | ~~ | 按位异或 | 整数 | 按位异或 |
按位或 | || | 按位或 | 整数 | 按位或 |
逻辑与 | & | 逻辑与 | 逻辑值 | 逻辑与 |
逻辑异或 | ~ | 逻辑异或 | 逻辑值 | 逻辑异或 |
逻辑或 | | | 逻辑或 | 逻辑值 | 逻辑或 |
并 | $ | 并 | 整数、实数、复数 | 并。核心库并运算的结果为复数或三维向量。 |
赋值运算符 | = | 赋值 | 赋值 | 赋值运算符 |
对象赋值运算符 | .= | 对象赋值 | 对象赋值 | 变量函数调用运算符与赋值运算符的结合,一般用于对象赋值 |
语句分隔符 | , | 逗号 | 分隔语句 | 逗号、冒号、分号运算符 |
: | 冒号 | 分隔语句 | ||
; | 分号 | 分隔语句 |
Lu使用等号定义一个函数,如下例:
f(x,y) = x+y; //函数定义
f[2,3]; //函数调用
加(数1,数2) = 数1+数2; //函数定义
加(2,3); //函数调用
函数中可以使用各种变量,如下例:
f(x,y : a,b : c,d) = //x和y是自变量,第一个冒号后的变量是动态变量,第二个冒号后的变量是模块变量
a=x+y, b=x-y, c=x*y, d=x/y,
a+b+c+d;
f[2.0,3.0];
自变量和动态变量只能被定义该变量的函数所访问;模块变量可被同一模块的所有表达式所访问,其他模块的表达式无法访问。自变量用于向表达式传递参数,因此自变量也称为形式参数。动态变量只在表达式执行时起作用,一旦表达式执行完毕,动态变量也随之失效。
在编译符#MODULE#和#END#之间的代码属于一个模块,如下例:
#MODULE#
(:: a) = a=5; //这个函数没有名字,这是允许的。该函数的作用是给模块变量a赋值
f(x :: a) = x+a; //该函数是私有函数,只能被该模块的表达式所调用
:::g(x) = x+f[x]; //该函数以编译符:::开头,是一个全局函数,全局函数名是唯一的,可被所有的表达式所调用
#END#
#MODULE#
(:: a) = a=8; //这个函数没有名字,这是允许的。该函数的作用是给模块变量a赋值
f(x :: a) = x+a; //该函数是私有函数,只能被该模块的表达式所调用
:::h(x) = x+f[x]; //该函数以编译符:::开头,是一个全局函数,全局函数名是唯一的,可被所有的表达式所调用
#END#
//以下两个语句不属于以上两个模块
g[2]; //调用全局函数g,结果为9
h[2]; //调用全局函数g,结果为12
若源代码中没有编译符#MODULE#和#END#,则整个源代码属于同一个模块。
一般,Lu脚本中的变量须先声明后使用,使用编译符 mvar: 后,未声明的变量都看作模块变量,如下例:
mvar:
a=2, b=3; //模块变量a和b赋值
a+b; //模块变量a和b相加
许多Lu扩展库中的函数是通过命名空间方式输出的。例如LuIMSL库中的函数使用命名空间“IMSL”输出,所有函数均具有类似“IMSL::ode”的格式,使用!!!using("IMSL");可简化LuIMSL中的函数访问。例如:
[例子] 设一阶微分方程组及初值为:
r'=2r-2rf, r(0)=1
f'=-f+rf, f(0)=3
计算t=1,2,...,10时的r、f的值。
程序如下:
!!!using["IMSL","math"]; //使用命名空间"IMSL"和"math"。编译符“!!!”使函数using立即执行。
f(t,r,f,dr,df)={dr=2*r-2*r*f, df=-f+r*f}; //函数定义
ode[@f,ra1[0,1,2,3,4,5,6,7,8,9,10],ra1[1,3]].outa[];
如果不使用using函数,程序如下:
f(t,r,f,dr,df)={dr=2*r-2*r*f, df=-f+r*f};
IMSL::ode[@f,math::ra1[0,1,2,3,4,5,6,7,8,9,10],math::ra1[1,3]].math::outa[];
程序输出结果均为:
0. 1. 3.
1. 7.73453e-002 1.46445
2. 8.49774e-002 0.577954
3. 0.290891 0.249253
4. 1.4466 0.187219
5. 4.05146 1.43948
6. 0.175618 2.2586
7. 6.53112e-002 0.9088
8. 0.147227 0.366718
9. 0.650596 0.187575
10. 3.14433 0.348821
在Lu中,表达式的各个语句一般是顺序执行的。但是某些函数可以改变语句执行的顺序,称为流程控制函数。
(1)立即返回函数 return(x)
结束计算并立即返回表达式的值为x。
(2)判断函数 if(x,y1,y2,... ...,yn)
当逻辑语句x的值为真(或者对象x值非0)时,依次执行语句y1,y2,... ...,yn,否则,不执行语句y1,y2,... ...,yn。
该函数至少要有2个自变量参数,其中第一个参数为逻辑语句。
该函数的返回值无意义。
(3)自定义分段函数
which{
逻辑语句1 : 语句1,
逻辑语句2 : 语句2,
... ...,
逻辑语句n : 语句n,
缺省语句
};
Lu从前往后计算并检测逻辑语句的值,当检测到逻辑真(或者对象值非0)时,计算与此逻辑真对应的语句并返回该语句的值,如果没有检测到逻辑真,则计算缺省语句的值作为返回值,若此时没有缺省语句,则产生一个运行错误。
该函数至少要有2个自变量参数。
例如下式定义了一个分段函数:
(x)=which{x>0, 2*x-1,
x*x-1
};
如果舍弃该函数的返回值,则该函数可以作为一个选择计算函数使用。
(4)while循环函数
while循环是“当型”循环,其特点是:先判断条件是否成立,当条件成立时,则执行循环体,否则退出循环体,即“当条件成立时执行循环”。“当型”循环的循环体有可能一次也不执行。
while循环函数的格式如下:
while{x,
y1,y2,
...,
break(),
...,
continue(),
...,
yn
};
其中x为逻辑语句;y1,y2,...,break(),...,continue(), ...yn为循环体语句。当x的值为真(或者对象x值非0)时,依次执行循环体语句,直到x的值为假时退出循环。当执行到break()函数时,跳出while循环,执行while循环后面的语句部分;当执行到continue()函数时,返回到while循环的开始语句x处继续执行。
在循环体语句中,必须有能修改逻辑语句x的值的语句,使x的值为假,退出循环体,否则将产生无限循环。
该函数至少要有2个自变量参数,其中第一个参数为逻辑语句。
该函数的返回值无意义。
以下是一个while循环的例子:
(:i,k)=
{
i=0,k=0,
while{i<=1000000,k=k+i,i++}, //当i<=1000000时,计算k=k+i,然后i++;
k
};
(5)until循环函数
until循环是“直到型”循环,其特点是:先执行循环体,再判断条件是否成立,当条件成立时,退出循环体,否则继续执行循环体,即“执行循环直到条件成立”。“直到型”循环的循环体至少执行一次。
until循环函数的格式如下:
until{x1,x2,
...,
break(),
...,
continue(),
...,
y
};
until为先执行后判断的循环函数。即先执行循环体语句x1,x2,...,break(),...,continue(),...,然后计算逻辑语句y的值,直到y的值为真(或者对象y值非0)时退出循环。当执行到break()函数时,跳出until循环,执行until循环后面的语句部分;当执行到continue()函数时,返回到until循环的开始语句x1处继续执行。
在循环体语句中,必须有能修改逻辑语句y的值的语句,使y的值为真,退出循环体,否则将产生无限循环。
该函数至少要有2个自变量参数,其中最后一个参数为逻辑语句。
该函数的返回值无意义。
以下是一个until循环的例子:
(:i,k)=
{
i=0,k=0,
until{k=k+i,i++,i>1000000}, //计算k=k+i,i++,当i>1000000时退出;
k
};
注意:break()和continue()是两个无参函数,只能用在while和until两个循环函数中。
通常,本程序会计算并输出每一个表达式的结果。若要在一个表达式中输出多个结果,可使用o函数(o函数本身返回输出的字符个数),如下例:
o[25+5,"\r\n",sin[2.5],"\r\n"]; //在双引号 " " 之间的内容表示一个字符串,而 "\r\n" 将输出一个换行符。
结果:
30
0.59847214410395644
25
注意:最后的数字25是o函数输出的字符个数。
若只希望用函数o输出结果,不希望看到每一个表达式的结果,需要使用函数SetTalk[false]关闭计算结果输出,如下例:
SetTalk[false];
o[25+5,"\r\n",sin[2.5],"\r\n"];
结果:
30
0.59847214410395644
使用函数SetTalk[true]可恢复计算结果输出。
将常用的计算式保存为文件,进行代码重用,以提高工作效率。
编写常量文件,使OpenLu自动加载,以简化计算。
编写公共的函数放到函数库中,以增加代码重用。
常用的计算编写成命令文件,添加到OpenLu的命令菜单。
以上本文都有相关的例子。
这是最常用的计算,OpenLu为此提供了即时编译计算方式。如下例:
f(x,y)=sin[x-cos(y)]; //函数定义
2+10/3;
2.5+f[1.2,0.8];
sqrt[1-2i]; //复数表达式
(2$3$5) * (5$3$2); //三维向量乘
结果(注意复数和三维向量在OpenLu中的输出格式):
5
2.982313067962473
{1.272019649514069$(-0.7861513777574233)}
{(-9.)$21.$(-9.)}
求和函数 sum(@F,y1min,y1max,y1dy;y2min,y2max,y2dy;... ...):
F为求和函数句柄;y1min,y1max,y1dy为第一个自变量的初值、终值和参数增量[初值<终值,参数增量>0],依次类推。
例子:
F(x,y)=sin[x]+0.8-y;
sum(@F,1.,20.,1.;-2.,10.,1.);
结果:
-819.023115502543
上例x和y的增量均为1,更一般的情况是增量非1。仍以上式为例,但x增量为0.1,y的增量为0.05,代码如下:
F(x,y)=sin[x]+0.8-y;
sum(@F,1.,20.,0.1;2.,5.,0.05);
结果:
-31323.60317910476
求积函数pro的用法同求和函数sum,但pro用于求积。
方程(组)的求解,难易程度差别较大。在OpenLu中,普通的方程(组)可借助LuMath库中的拟牛顿法netn和对分法btFindRoot求解,难度大的方程(组)须借助优化库LuOpt中的iFind、Find和Opt函数求解。
(1)math::btFindRoot(f,a,b,h,k,eps):对分法求非线性方程的实根
f:自定义一元函数句柄。函数自变量不能重新赋值。
a,b:求根区间的左端点和右端点(b>a)。
h:搜索求根时采用的步长(h>0)。
k: 可能的实根个数。可缺省,缺省值为10。
eps:控制精度(eps>0)。可缺省,缺省值为1e-6。
返回值:返回一个数组(存放搜索到的实根),或者返回nil。若返回数组的长度为k,则有可能在求根区间[a,b]内的实根未搜索完。
例子:
f(x)=x^6-5*x^5+3*x^4+x^3-7*x^2+7*x-20; //函数定义
math::btFindRoot[@f,-2.,5.,0.2].o[]; //函数o用于输出一个对象
结果:
-1.40246 4.33376
(2)math::netn(f,x,eps,t,h,k):求非线性方程组一组实根的拟牛顿法
f:自定义二元或2n(n>1)元函数句柄,由用户自编。
如果f是二元函数,则两个参数为等长的一维数组:
f(x,y)= //二元函数,x[i]为自变量,y[i]为方程右端点函数值
{
y[0]=f1(x[0],x[1],...,x[n-1]),
y[1]=f2(x[0],x[1],...,x[n-1]),
... ...
y[n-1]=fn(x[0],x[1],...,x[n-1])
};
或者:
f(x1,x2,...,xn,y1,y2,...,yn)= //2n元函数,xi为自变量,yi为方程右端点函数值
{
y1=f1(x1,x2,...,xn),
y2=f2(x1,x2,...,xn),
... ...
yn=fn(x1,x2,...,xn)
};
x:双精度实型一维数组,长度不小于n,存放一组初值,返回方程组的一组实根。
eps:控制精度(eps>0)。可缺省该参数,缺省值eps=1e-6。
t:控制h大小的变量,0<t<1。可缺省该参数,缺省值h=0.1。
h:增量初值,在本函数中将被破坏。可缺省该参数,缺省值h=0.1。
k:允许的最大迭代次数。可缺省该参数,缺省值k=100。
返回值:返回值为实际迭代次数。若返回值为-1表示代数方程奇异,若返回值为-2表示β=0,这两种情况可放宽精度要求、改变各个初值或改变各个方程顺序再试;若返回值等于0说明迭代了k次仍未满足精度要求,程序工作失败。
例子:解下例方程组
x1*x1+x2*x2+x3*x3-1.0=0
2.0*x1*x1+x2*x2-4.0*x3=0
3.0*x1*x1-4.0*x2+x3*x3=0
代码:
f(x1,x2,x3,y1,y2,y3)= //函数定义
{
y1=x1*x1+x2*x2+x3*x3-1.0,
y2=2.0*x1*x1+x2*x2-4.0*x3,
y3=3.0*x1*x1-4.0*x2+x3*x3
};
!!!using["math"]; //使用命名空间math
main(:x,i)={
//申请一维数组并赋初值的常规方法。函数new表示申请一个对象,real_s表示数组对象,data后的数据为初值1.0,1.0,1.0
x=new{real_s,data : 1.0,1.0,1.0},
i=netn[@f,x], //拟牛顿法解方程
x.outa(), //用函数outa输出一个数组
i //返回迭代次数
};
或者:
f(x,y)=
{
y[0]=x[0]*x[0]+x[1]*x[1]+x[2]*x[2]-1.0,
y[1]=2.0*x[0]*x[0]+x[1]*x[1]-4.0*x[2],
y[2]=3.0*x[0]*x[0]-4.0*x[1]+x[2]*x[2]
};
!!!using["math"];
main(:x,i)={
//申请一维数组并赋初值的简便方法。函数ra1(realarray1的缩写)表示申请一维数组,初值为1,1.0,1(ra1自动将整数转换为实数)
i=netn[@f,x=ra1(1,1.0,1)],
x.o(), //用函数o输出一个对象
i
};
结果:
0.785197 0.496611 0.369923
4
(3)luopt::iFind(f, luopt::optmax,m, luopt::optrange,min,max, luopt::opteps,eps, luopt::optpara,x1,x2,..., luopt::optthis,i):求单变量方程的全部解
f:自定义n元函数句柄,不可缺省。格式如下:
f(x1,x2,...,xn)=
{
... ...
};
默认求方程f(x1,x2,...,xn)=0第一个自变量的全部解,而其他自变量赋值为0.0。可以用参数optthis指定求解的自变量,也可以用参数optpara给出其他自变量的值。
luopt::optmax,m:区间分割数目(大于等于10),区间分割数目越多精度越高。可以缺省该参数,缺省值为200。
luopt::optrange,min,max:指定求解区间。若缺省该参数,则min为-1e50,max为1e50。
luopt::opteps,eps:控制精度要求,eps>0。可以缺省该参数,缺省值为1e-6。
luopt::optpara,x1,x2,...:给指定求解自变量之外的其他自变量赋值,参数x1,x2,...的个数比全部自变量个数少1。若缺省该参数,其他自变量缺省值均为0.0。
luopt::optthis,i:指定求解的自变量。0表示第一个自变量;1表示第二个自变量,以此类推。若缺省该参数,i为0。
返回值:解的个数。
例子1:求方程的全部解:f(x)=x^6-5*x^5+3*x^4+x^3-7*x^2+7*x-20;
f(x)=x^6-5*x^5+3*x^4+x^3-7*x^2+7*x-20;
luopt::iFind[@f];
结果(最后一个值为误差,下同):
-1.402463030422577 3.552713678800501e-015
4.333755446919994 -2.984279490192421e-013
2
例子2:求方程的全部解(x取-8~8,y取1):f(x,y)=(x^2+y^2)^3-36*(x^2-y^2)^2;
!!!using("luopt");
f(x,y)=(x^2+y^2)^3-36*(x^2-y^2)^2;
iFind[@f,optrange,-8.,8.,optpara,1.];
结果:
-5.530393535267971 1. -3.637978807091713e-012
-1.32936186218296 1. -7.105427357601002e-015
-0.8047014256478491 1. -1.776356839400251e-015
0.8047014256478491 1. -1.776356839400251e-015
1.32936186218296 1. -7.105427357601002e-015
5.530393535267971 1. -3.637978807091713e-012
6
例子3:求方程的全部解(x取1,y取-8~8):f(x,y)=(x^2+y^2)^3-36*(x^2-y^2)^2;
!!!using("luopt");
f(x,y)=(x^2+y^2)^3-36*(x^2-y^2)^2;
iFind[@f, optrange,-8.,8., optthis,1, optpara,1.];
结果:
1. -5.530393535267971 -3.637978807091713e-012
1. -1.32936186218296 -7.105427357601002e-015
1. -0.8047014256478491 -1.776356839400251e-015
1. 0.8047014256478491 -1.776356839400251e-015
1. 1.32936186218296 -7.105427357601002e-015
1. 5.530393535267971 -3.637978807091713e-012
6
(4)luopt::Find(f, luopt::optmode,mode, luopt::optrange,x1min,x1max,x2min,x2max,...,xnmin,xnmax, luopt::opteps,eps):求方程组的全部解
f:自定义2*n元函数句柄,不可缺省。格式如下:
f(x1,x2,...,xn,y1,y2,...,yn)=
{
y1= ...,
y2= ...,
... ...,
yn= ...
};
luopt::optmode,mode:工作模式,取0、1、2、3、... ...。通常,工作模式取值越大,搜到的解越多,但耗时越长。若缺省该参数,工作模式取0。
luopt::optrange,x1min,x1max,x2min,x2max,...,xnmin,xnmax:指定求解区间。若缺省该参数,则所有变量区间均为[-1e50~1e50]。
luopt::opteps,eps:控制精度要求,eps>0。可以缺省该参数,缺省值为1e-6。满足sqrt[(y1*y1+y2*y2+...+yn*yn)/n]<eps。
返回值:解的个数。
说明:该函数的解是不稳定的,需要多次运行该函数以获得需要的解。
例子1:解方程组:
(x-y)^2-3*(x-y) = 10
x^2+2*x*y+y^2 = 9
代码(最后一个值为误差,下同):
f(x,y,y1,y2)=
{
y1=(x-y)^2-3*(x-y)-10,
y2=x^2+2*x*y+y^2-9
};
luopt::Find[@f];
结果:
4. -1. 0.
0.5 2.5 0.
-2.5 -0.5 0.
1.000000000224727 -4.000000000225374 2.227885009675654e-009
4
例子2:解方程组:a取-10~10,t取-7~7
-b*sin(a+6*t)+n-40.4945=0
-b*sin(a+7*t)+n-40.5696=0
-b*sin(a+8*t)+n-41.0443=0
-b*sin(a+9*t)+n-41.4190=0
代码:
!!!using["luopt"];
f(a,b,n,t,y1,y2,y3,y4)=
{
y1=-b*sin(a+6*t)+n-40.4945,
y2=-b*sin(a+7*t)+n-40.5696,
y3=-b*sin(a+8*t)+n-41.0443,
y4=-b*sin(a+9*t)+n-41.4190
};
Find[@f, optmode,5, optrange,-10.,10.,-1e50,1e50,-1e50,1e50,-7.,7.];
结果(没有验证过该方程组有多少组解,下面是其中一次求解结果):
8.423278510740619 0.491530082706187 40.94928398718974 -1.077226214994066 5.024295867788081e-015
5.281685857151022 -0.4915300827061788 40.94928398718975 -1.077226214994089 1.123466709944544e-014
4.143092103608209 -0.4915300827069298 40.94928398718954 1.077226214995628 8.519922959291649e-013
1.001499450021447 0.4915300827000679 40.94928398718732 -5.205959092183973 4.316661155959305e-012
-4.143092103780588 0.4915300827292421 40.94928398721459 -1.077226214971404 2.308770421222309e-011
-5.281685858383328 0.4915300826310666 40.949283987277 1.077226215139597 1.209674914149958e-010
-1.001499513894245 -0.4915300850351296 40.94928398852594 5.205959099724587 3.485087466848284e-009
-2.140093572279421 -0.4915300631949757 40.94928396791384 1.077226272463665 1.373241653986354e-008
-1.001499452411673 -0.491530028262163 40.94928398098966 -1.077226219100778 3.989970963218456e-008
9
(5)luopt::Opt(f, ... ...):求无约束或约束条件下的n维极小值,可用于解方程(组)
有些方程(组)用常规方法求解比较困难,须借助于优化函数Opt求解。
很少有软件可求解以下方程组:
a*exp(-b*7.85)+c*exp(-d*7.85)=28.9
-a/b*exp(-b*7.85)-c/d*exp(-d*7.85)=500
a/b^2*exp(-b*7.85)+c/d^2*exp(-d*7.85)=233
a*exp(-b*1.85)+c*exp(-d*1.85)=20.9
代码:
!!!using["luopt"];
f(a,b,c,d:f1,f2,f3,f4)=
f1=a*exp(-b*7.85)+c*exp(-d*7.85)-28.9,
f2=-a/b*exp(-b*7.85)-c/d*exp(-d*7.85)-500,
f3=a/b^2*exp(-b*7.85)+c/d^2*exp(-d*7.85)-233,
f4=a*exp(-b*1.85)+c*exp(-d*1.85)-20.9,
sqrt[(f1*f1+f2*f2+f3*f3+f4*f4)/4];
Opt[@f, optwaysimdeep, optwayconfra];
多次运行,可得以下2组解(a,b,c,d,误差):
19.08176873960328 -5.36612020972517e-002 -0.1719391618522963 -4.244947571876434e-003 1.70094863414172e-013
-0.1719391618522608 -4.244947571876049e-003 19.08176873960331 -5.366120209725268e-002 7.53644380168212e-015
(1)IMSL::QDAGS(F,A,B,ERRABS,ERRREL,&ERREST):计算一个函数的积分(可能在端点存在奇异)
F:Lu一元函数句柄。该函数由用户定义。
A,B:积分下限及积分上限。
ERRABS:期望的绝对精度。ERRABS>=0。可缺省该参数,缺省值为0.0。
ERRREL:期望的相对精度。ERRREL>=0。可缺省该参数,缺省值为1e-6。
ERREST:返回计算的绝对误差。可缺省该参数。
返回值:积分值。
[例子] 求函数f(x)=ln(x)/sqrt(x)在0~1上的积分值
f(x)=ln(x)/sqrt(x);
IMSL::QDAGS[@f,0.,1.]; //得到积分值
(:err)=IMSL::QDAGS[@f,0.,1.,0.,1e-6,&err],err; //得到积分值的绝对误差
结果:
-4.000000000000085
1.354472090042691e-013
(2)IMSL::QDAG(F,A,B,IRULE,ERRABS,ERRREL,&ERREST):基于Gauss-Kronrod法则使用自适应算法计算一个函数的积分
F:Lu一元函数句柄。该函数由用户定义。
A,B:积分下限及积分上限。
IRULE:积分法则。Gauss-Kronrod法则用于下面的点数(IRULE值:点数):(1:7~15)、(2:11~21)、(3:15~31)、(4:20~41)、(5:25~51)、(6:30~61)。大多数函数推荐取IRULE=2;如果函数有一个奇异的峰值,取IRULE=1;如果函数是摆动的,取IRULE=6。可缺省该参数,缺省值为2。
ERRABS:期望的绝对精度。ERRABS>=0。可缺省该参数,缺省值为0.0。
ERRREL:期望的相对精度。ERRREL>=0。可缺省该参数,缺省值为1e-6。
ERREST:返回计算的绝对误差。可缺省该参数。
返回值:积分值。
[例子] 求函数f(x)=ln(x)/sqrt(x)在0~1上的积分值
f(x)=ln(x)/sqrt(x);
IMSL::QDAG[@f,0.,1.]; //得到积分值
(:err)=IMSL::QDAG[@f,0.,1.,2,0.,1e-6,&err],err; //得到积分值的绝对误差
结果:
-3.999999880644267
2.973612447385764e-006
(3)IMSL::QDAGP(F,A,B,PTS,ERRABS,ERRREL,&ERREST):计算一个给定奇异点的函数的积分
F:Lu一元函数句柄。该函数由用户定义。
A,B:积分下限及积分上限。
PTS:数组,存放积分区间的间断点,至少要有一个间断点。通常这些间断点是被积函数出现奇异值的地方。
ERRABS:期望的绝对精度。ERRABS>=0。可缺省该参数,缺省值为0.0。
ERRREL:期望的相对精度。ERRREL>=0。可缺省该参数,缺省值为1e-6。
ERREST:返回计算的绝对误差。可缺省该参数。
返回值:积分值。
[例子] 求函数f(x)=x^3*ln{abs[(x^2-1)*(x^2-2)]}在0~3上的积分值
f(x)=x^3*ln{abs[(x^2-1)*(x^2-2)]};
IMSL::QDAGP[@f,0.,3.,math::ra1(1,sqrt(2.0))]; //函数math::ra1用于申请一维实数数组并赋初值
结果:
52.74074838347256
(4)IMSL::QDAGI(F,A,B,ERRABS,ERRREL,&ERREST):计算一个函数在无穷区间或半无穷区间的积分
F:Lu一元函数句柄。该函数由用户定义。
A,B:积分下限及积分上限。设x是一个数,则可取的值为 A=-inf,B=x(-∞,x);A=x,B=inf(x,+∞);A=-inf,B=inf(-∞,+∞)。
ERRABS:期望的绝对精度。ERRABS>=0。可缺省该参数,缺省值为0.0。
ERRREL:期望的相对精度。ERRREL>=0。可缺省该参数,缺省值为1e-6。
ERREST:返回计算的绝对误差。可缺省该参数。
返回值:积分值。
[例子] 求函数x*exp[-x]在(0,+∞)上的积分值。
f(x)=x*exp[-x];
IMSL::QDAGI[@f,0.0,inf];
结果:
0.9999999999999998
(5)IMSL::TWODQ(F, A, B, G, H, IRULE, ERRABS, ERRREL, &ERREST):计算二重积分
F:Lu二元被积函数句柄。该函数由用户定义。
A,B:积分下限与积分上限。
G,H:Lu一元函数句柄,由用户定义,用来计算内层积分的下限与上限。
IRULE:选择积分的法则。Gauss-Kronrod法则用于下面的点数(IRULE值:点数):(1:7~15)、(2:11~21)、(3:15~31)、(4:20~41)、(5:25~51)、(6:30~61)。大多数函数推荐取IRULE=2;如果函数有一个奇异的峰值,取IRULE=1;如果函数是摆动的,取IRULE=6。可缺省该参数,缺省值为2。
ERRABS:期望的绝对精度。ERRABS>=0。可缺省该参数,缺省值为0.0。
ERRREL:期望的相对精度。ERRREL>=0。可缺省该参数,缺省值为1e-6。
ERREST:返回计算的绝对误差。可缺省该参数。
返回值:积分值。
[例子] 计算函数f(x,y)=y*cos(x+y*y)的近似积分值。外层区间取[0,1],内层区间取[-2*x,5*x]。
f(x,y)=y*cos(x+y*y);
g(x)=-2*x;
h(x)=5*x;
IMSL::TWODQ[@f,0.,1.,@g,@h,6];
结果:
-8.288981913740839e-002
(6)IMSL::QAND(F, math::ra1[A1, A2, ..., An, B1, B2, ..., Bn], MAXFCN, ERRABS, ERRREL, &ERREST):计算函数在超矩形下的积分
F:Lu多元函数句柄,设有n个自变量。该函数由用户定义。
math::ra1[A1, A2, ..., An, B1, B2, ..., Bn]:数组,长度为n+n。A1, A2, ..., An是积分下限,有n个下限值。B1, B2, ..., Bn是积分上限,有n个上限值。
MAXFCN:可允许的函数估计的最大数目。必须MAXFCN<=256^n。可缺省该参数,缺省值为64^n。
ERRABS:期望的绝对精度。ERRABS>=0。可缺省该参数,缺省值为0.0。
ERRREL:期望的相对精度。ERRREL>=0。可缺省该参数,缺省值为1e-6。
ERREST:返回计算的绝对误差。可缺省该参数。
返回值:积分值。
[例子] 计算函数exp[-(x1^2+x2^2+x3^2)]在扩展的立方体(整个三维空间)上的积分的近似值。积分区间为[-2,-2,-2~2,2,2]。
f(x1,x2,x3)=exp[-(x1^2+x2^2+x3^2)];
IMSL::QAND[@f,math::ra1(-2,-2,-2,2,2,2)];
结果:
5.490551464090155
(7)IMSL::DERIV(FCN,KORDER,X,BGSTEP,TOL):计算一元函数的一阶、二阶或三阶导数
FCN:Lu一元函数句柄,要计算该函数在X的导数。该函数由用户定义。
KORDER:导数的阶(1、2或3)。
X:被求导的点。
BGSTEP:用来计算步长的起始值,这个步长被用于计算导数。BGSTEP>0。可缺省该参数,缺省值为0.1。
TOL:允许的相对误差(0~1)。可缺省该参数,缺省值为1e-6。
返回值:函数的一阶、二阶或三阶导数。
[例子] 求函数f(x)=2*x^4+3*x在0.75处的三阶导数
f(x)=2*x^4+3*x;
IMSL::DERIV[@f,3,0.75];
结果:
36.00000000019881
IMSL::ode(FCN, math::ra1[t0, t1, ..., tk], math::ra1[Y1,Y2,...,Yn], TOL):求解一次常微分方程[y'=f(t,y) ,y(t0)=y0],Runge-Kutta方法
FCN:用户定义的函数,用于计算微分方程组中各方程右端函数值,由用户自编。该表达式有2*n+1个参数,第一个参数为自变量,随后n个参数为函数值,最后n个参数为右端函数值(即微分方程的值)。n为微分方程组中方程的个数,也是未知函数的个数。形式如下:
f(t,y1,y2,y3,d1,d2,d3)=
{
d1=y2,
d2=-y1,
d3=-y3
};
math::ra1[t0, t1, ..., tk]:数组,存放独立变量t。输入初始时间为t0,用户希望得到时间t1, ..., tk时的解。
math::ra1[Y1,Y2,...,Yn]:存放n个未知函数在起始点处的函数值Y(n)=Yn(t)。
TOL:允许误差。可缺省该参数,缺省值为1e-6。
返回值:(1+k)×(1+n)二维数组。存放(1+k)组结果(包含初始值),每一组结果即:t,Y1,Y2,...,Yn。
[例子] 设一阶微分方程组及初值为:
r'=2r-2rf, r(0)=1
f'=-f+rf, f(0)=3
计算t=1,2,...,10时的r、f的值。
程序如下:
!!!using["IMSL","math"];
f(t,r,f,dr,df)={dr=2*r-2*r*f, df=-f+r*f}; //函数定义
ode[@f,ra1[0,1,2,3,4,5,6,7,8,9,10],ra1[1,3]].outa[];
结果:
0. 1. 3.
1. 7.73453e-002 1.46445
2. 8.49774e-002 0.577954
3. 0.290891 0.249253
4. 1.4466 0.187219
5. 4.05146 1.43948
6. 0.175618 2.2586
7. 6.53112e-002 0.9088
8. 0.147227 0.366718
9. 0.650596 0.187575
10. 3.14433 0.348821
luopt::Opt(f, luopt::optrange,x1min,x1max,x2min,x2max,...,xnmin,xnmax, luopt::optwaysimdeep, luopt::optwayconfra, luopt::optnum,num):求无约束或约束条件下的n维极小值
f:自定义n元函数句柄,用于计算目标函数f(x1,x2,...,xn)的值,不可缺省。该函数由用户自编,格式如下:
f(x1,x2,...,xn)=
{
g(x1,x2,...,xn)
};
luopt::optrange,x1min,x1max,x2min,x2max,...,xnmin,xnmax:指定搜索区间。若缺省该参数,则所有变量区间均为[-1e20~1e20]。
luopt::optwaysimdeep:使用单形调优法局部深度搜索。
luopt::optwayconfra:使用连分式局部搜索方法。
luopt::optnum,num:设置最多输出的极小值的个数。可以缺省该参数,默认输出1个极小值。
返回值:极小值个数。
说明1:该函数使用随机算法搜索全局最小值,故解是不稳定的,应多次搜索甚至变换参数搜索,以最小者为最优。
说明2:Opt须注册后才能使用其全部功能。如果没有注册,当优化参数多于2个时,Opt函数仅返回最小值,不能返回优化参数。目前免费注册,用函数luopt::OptPassword获取机器码后,通过E-mail发送到forcal@sina.com获取注册码,同时允许免费用户的E-mail在网上发布。
函数OptPassword的格式(返回值及参数pw1,pw1,... ...,pwn将返回多个机器码):luopt::OptPassword(&pw1,&pw1,... ...,&pwn)
函数OptPassword的用法1:luopt::OptPassword();
函数OptPassword的用法2:(:pw)=luopt::OptPassword(&pw),pw;
函数OptPassword的用法3:(:pw)=luopt::OptPassword(0,&pw),pw;
获取注册码后,打开OpenLu中的工作区文件,修改以下部分(注册码在冒号后):"dll\LuOpt32.dll:604508320"
例子1:计算下列目标函数的极小值点与极小值:J=100*(x1-x0*x0)2+(1-x0)2
程序如下:
f(x0,x1)=100*(x1-x0*x0)^2+(1-x0)^2;
fcopt::Opt[@f];
结果(最后一个数为误差,下同):
1. 1. 0.
例子2:求下列隐函数z的最小值:
z=sin[(z*x-0.5)^2+2*x*y*y-z/10]*exp{-[(x-0.5-exp(-y+z))^2+y*y-z/5+3]}
其中x范围[-1,7],y范围[-2,2]。
程序如下:
!!!using("luopt");
f(x,y,z)=z+1e10*{z-sin[(z*x-0.5)^2+2*x*y*y-z/10]*exp{-[(x-0.5-exp(-y+z))^2+y*y-z/5+3]}}^2; //构造目标函数,注意1e10
Opt[@f, optwaysimdeep, optwayconfra, optrange,-1.,7., -2.,2., -1e10,1e10];
结果(需多次运行):
2.898338314819993 -0.8573299220967106 -2.33540823984039e-002 -2.335408239796544e-002
例子3:拟合公式:z = p0*(1-exp(-p1*(x-p2)))+p3*x^p4+p5*x*y;
参数:p0 - p5
变量:x,y,z
数据(x,y,z):
2 101 172
3 14 210
4 136 241
5 52 265
6 67 280
7 81 289
8 54 294
9 20 302
10 6 299
11 2 306
Lu代码:
!!!using["luopt","math"];
init(::Array,max)=
{
max=10,
Array=new[real_s,max,3].SetArray{
"2 101 172
3 14 210
4 136 241
5 52 265
6 67 280
7 81 289
8 54 294
9 20 302
10 6 299
11 2 306"
}
};
f(p0, p1, p2, p3, p4, p5 :i,s,x,y,z:Array,max)=
{
s=0,i=0,(i<max).while{
x=Array[i,0], y=Array[i,1], z=Array[i,2],
s=s+[ p0*(1-exp(-p1*(x-p2)))+p3*x^p4+p5*x*y - z ]^2,
i++
},
sqrt[s/max]
};
Opt[@f, optwayconfra];
结果(需多次求解):
306.0849059154657 0.4607174830055115 0.9026291308250309 248.1563530146011 -2.274779410407842 -3.621982090081021e-003 1.506033828737254
矩阵运算需要使用FcMath扩展库,该库的函数通过命名空间“math”输出。
例子:
!!!using["math"];
main(:a,b,c)=
a=matrix[5,1 : 1.,2.,3.,4.,5.], //生成6×1矩阵并赋初值
b=matrix[1,6 : 1.,2.,3.,4.,5.,6.], //生成1×5矩阵并赋初值
c=a*b, //矩阵乘
o[a,b,c,c(all:3),c(3:all),c(2,3:3,5)]; //输出5个矩阵,其中c(all:3),c(3:all),c(3,5:2,3)都是矩阵c的子矩阵,all表示取所有的行或列
结果:
1.
2.
3.
4.
5.
1. 2. 3. 4. 5. 6.
1. 2. 3. 4. 5. 6.
2. 4. 6. 8. 10. 12.
3. 6. 9. 12. 15. 18.
4. 8. 12. 16. 20. 24.
5. 10. 15. 20. 25. 30.
4.
8.
12.
16.
20.
4. 8. 12. 16. 20. 24.
12. 15. 18.
16. 20. 24.
如果在工作或学习中经常用到一些常量,可将这些常量放到文件中,让OpenLu自动加载这些常量,提高工作效率。
常量用函数const进行定义。通常,只能将静态数据如整数、实数、复数、三维向量等定义为常量。
以下是一个常量文件,实际上是一个表达式,但只用到了const函数。
//常量文件MyConst.txt
const["my_num",1000], const["my_pi",3.1416], const["my_i",1-2i], //定义一些整数、实数、复数常量
const["my::num",1000], const["my::pi",3.1416], const["my::i",1-2i], //在命名空间“my”中定义整数、实数、复数常量
const["我的整数",1000], const["我的向量",1$5$8]; //可以使用汉字作为常量名
设常量文件MyConst.txt保存在文件夹“command”中,要想让OpenLu自动加载运行该文件,须进行如下操作:
(1)打开工作区文件,添加如下内容
//#COMMAND(必须为大写):自定义命令,定义弹出式菜单“常量”。
#COMMAND (常量)
{
"MyConst*Command\MyConst.txt"
}
(2)在工作区文件中找到自动运行设置项 #AUTOEXE,添加 "MyConst" 自动运行项目。
//#AUTOEXE(必须为大写):程序启动时将自动执行以下命令,这些命令必须在#COMMAND中定义。
#AUTOEXE
{
"加载运行错误描述"
"加载函数说明"
"即时编译计算"
"MyConst"
}
(3)保存工作区文件。
这样,当OpenLu重新启动时,将自动执行文件MyConst.txt的内容,自定义的常量就可以使用了。
将常用的函数放到函数库中,可进行代码重用,提高工作效率。
函数库中的函数,只有定义为全局函数,或者通过命名空间输出,才能被其他程序使用。以下是一个函数库文件:
//函数库文件:面积体积公式.m
//模块名:面积体积公式
//用全局函数输出函数。以:::开头的函数为全局函数,否则为私有函数。
CircleArea(r)=3.1415926*r*r; //圆面积。该公式只能被本模块的表达式所访问。
:::Circle_A(r)=3.1415926*r*r; //圆面积。
:::Triangle_A(a,b,c:s)= {s=(a+b+c)/2, sqrt[s*(s-a)*(s-b)*(s-c)]}; //三角形面积。
:::Cyclinder_V(r,h)=CircleArea(r)*h; //圆柱体体积。
//用命名空间输出函数
#MODULE# //定义一个子模块
!!!Module("Area"); //创建模块命名空间Area
CircleArea(r)=3.1415926*r*r;
圆面积(r)=3.1415926*r*r;
三角形面积(a,b,c:s)= {s=(a+b+c)/2, sqrt[s*(s-a)*(s-b)*(s-c)]};
圆柱体体积(r,h)=CircleArea(r)*h;
!!!OutFun("圆面积","三角形面积","圆柱体体积"); //输出模块命名空间中的函数
#END# //子模块定义结束
设函数库文件“面积体积公式.m”保存在文件夹“module”中,要想在OpenLu中使用该文件,须打开工作区文件,添加如下内容(缺省的工作区文件中已存在该内容):
//#MODULE(必须为大写):设置模块。
#MODULE
{
//定义模块“面积体积公式”,模块文件为“Module\面积体积公式.m”。
"面积体积公式*Module\面积体积公式.m"
}
保存工作区文件并执行菜单“设置->重载模块文件”。
以下代码中使用了模块“面积体积公式”:
#USE# 面积体积公式 ; //编译符#USE#指出要使用模块“面积体积公式”
Circle_A[5];
Triangle_A(3.,4.,5.);
Cyclinder_V(2.,3.);
Area::圆面积[5];
Area::三角形面积[3.,4.,5.];
Area::圆柱体体积[2.,3.];
在“使用常量文件”中已经介绍了如何创建命令菜单,以及如何使OpenLu在启动时自动执行该命令,不再赘述。
OpenLu运行时至少需要MLu32.dll和lu32.dll两个动态库的支持。其他内容使OpenLu的功能更加完善。
文件 readme.htm:帮助文件。
文件夹 dll:存放Lu扩展动态库。注意该文件夹中有一个文件“ImslErr.txt”,如果使用FcIMSL中的函数,要查看该文件中有没有错误输出。
文件夹 command:存放命令文件。
文件夹 err:存放Lu函数的错误提示文件。
文件夹 funhelp:存放Lu函数说明文件。
文件夹 ini:存放OpenLu工作区文件。
文件夹 module:存放模块文件,即自定义函数库文件。
文件夹 help:存放帮助文件。
文件夹 olu:存放用户源程序文件。
OpenLu在初次运行时,会自动加载文件夹“ini”中的默认工作区文件“OpenLu.ini”。通过该文件的配置,OpenLu自动加载了Lu扩展库文件、生成命令菜单并自动执行部分命令(Lu函数错误提示及函数说明)、加载模块文件等等。
可随时将Lu扩展库添加到该系统;可根据需要自己编写函数库、命令菜单、常量文件等等。
参考:OpenLu使用说明
本软件旨在成为“工程计算助手”,若您有好的建议,请与我联系。
版权所有© Lu程序设计 2011-2012,保留所有权利
E-mail: forcal@sina.com QQ:630715621
最近更新: 2011年12月31日