Maple教程

Maple教程 - 第一章 Maple...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: 第一章 Maple 系统简介 本章首先对计算机代数系统进行简要的介绍. 主要内容包括计算机代数系统的发展历史、 计算机代数系统的基本功能及特征以及网络资源. 然后介绍 Maple V 的基本功能, 窗口环境以 及组织结构. 1.1 计算机代数系统的发展历史 什么是计算机代数系统? 从历史的角度来看 “COMPUTE” 的涵义是 “数值的计算”. 数值 计算的涵义不仅仅是数的算术计算, 还包括其它复杂的计算, 例如: 数学函数的计算、求多项式 的根、矩阵的计算、矩阵特征值的计算等等. 数值计算的一个本质的特征是它不能保证绝对的 准确, 原因在于, 在数值计算的过程中我们是用浮点数进行计算的, 对于简单的问题, 我们可以 用纸和笔手工计算, 对于复杂的问题, 就需要用计算器或计算机进行计算. 然而, 对计算机来说, 要想绝对精确的表达一个浮点数几乎是不可能的, 在计算的过程中必然会产生误差. 数学的计算除了数值计算以外还有另一个重要的分枝, 我们称之为符号计算或代数计算. 简 单的讲, 就是对代表数学对象的符号进行计算. 这些符号可以代表整数、有理数、实数、复数或 代数数, 也可以代表其它的数学对象如多项式、有理函数、矩阵、方程组, 或者其它抽象的数学 对象如群、环、域等等. 对于这些抽象的数学符号, 我们通常是手工计算的, 这也是数学家传统 的工作方式. 然而随着计算机技术的发展, 以及对符号算法的深入研究, 用计算机代替人工进行 符号计算已经成为可能. 从二十世纪六十年代以来, 符号计算这个研究领域获得了极大的发展. 一系列符号计算算 法的提出为现代计算机代数系统奠定了理论基础. 比较著名的算法包括: 计算多项式理想的 Gr¨bner 基算法、 o 多项式分解的 Berlekamp 算法、计算有理函数积分的 Risch 算法. 在二十世纪六十年代, 比较流行的计算机程序语言是 FORTRAN 和 ALGOL. 这两种语言 主要是用来作数值计算的, 至今 FORTRAN 依然是数值计算领域的标准语言之一. 然而 FORTRAN 语言和 ALGOL 语言并不适合于编写符号计算软件. 六十年代初出现的 LISP 语言为符 号计算软件提供了合适的语言环境, 因此早期的符号计算软件都是用 LISP 语言编写的. 其中最 著名的符号计算系统是 REDUCE, REDUCE 系统是由 Stanford 大学的 Tony Hearn 开发的基 于 LISP 语言的交互式符号计算系统, 最初的目的是用来进行物理计算. 到了二十世纪七十年代 初, 由麻省理工学院的 Joel Moses, Willian Martin 等人开发的 MACSYMA 系统诞生了, 它是那 个时代功能最强大的符号计算系统. 它的功能除了标准的代数计算以外, 还包括极限的计算、 符 号积分、解方程等. 事实上, 许多符号计算的标准算法都是由麻省理工学院的研究小组提出的. 由 G. Collins 和 R. Loos 开发的 SAC/ALDES 系统是另外一种类型的符号计算系统, 它的 前身是 G. Collins 在 IBM 编写的 PM 系统 (它是一个处理多项式的符号计算系统). SAC 是一 个非交互的系统, 它是由 ALDES(ALgebraic DEScription) 语言编写的模块组成的, 并且带有一 个转换程序, 可以把结果转换成 FORTRAN 语言. 到了 1990 年, H. Hong 用 C 语言重写了 SAC 系统, 形成了新的 SACLIB 系统. 这个系统提供了完整的 C 语言源代码, 可以自由的从国际互 联网上下载. 在二十世纪七十年代的第四个通用的符号计算系统是 muMATH. 它是由 Hawaii 大学的 David Stoutemyer 和 Albert Rich 开发的第一个可以在 IBM 的 PC 机上运行的计算机代数系统. 1 2 第一章 Maple 系统简介 它所使用的开发语言是 LISP 语言的一个子集称为 muSIMP. 进入二十世纪八十年代, 随着个人 PC 机的普及, 计算机代数系统也获得了飞速的发展. 在 这个时代推出的计机代数系统大部分是用 C 语言编写的, 比较著名的系统包括 Maple, Mathematica, DERIVE 等. 有关 Maple 的特点我们将在后面介绍, 这里, 我们简单介绍一下 DERIVE 和 Mathematica. DERIVE 是 muMATH 的后继版本, 它是第一个在 PC 机上运行的符号计算系统.DERIVE 具有友好的菜单驱动界面和图形接口, 可以很方便的显示二维和三维图形. 它唯一的缺陷是没 有编程功能, 直到 1994 年 DERIVE 的第三版问世时, 才提供了有限的编程功能. 现在 DERIVE 的大部分功能都被移植到由 HP 公司和 Texas 公司生产的图形计算器上. Mathematica 是由 Stephen Wolfram 开发的符号计算软件,Mathematica 系统的计算能力非 常强, 它的函数很多, 而且用户自己可以编程. 它的最大优点是, 在带有图形用户接口的计算机 上 Mathematica 支持一个专用的 Notebook 接口. 通过 Notebook 接口, 我们可以向 Mathematica 核心输入命令, 可以显示 Mathematica 的输出结果, 显示图形、 动画、 播放声音. 通过 Notebook, 我们可以书写报告、 论文, 甚至整本书. 事实上, 有关 Mathematica 的论文, 软件, 杂志大部分都 是用 Notebook 写的, 并且在 Internet 网络上广泛传播.Mathematica 的另一个重要特点是它具 有 Mathlink 协议, 通过 Mathlink, 我们可以把 Mathematica 的核心与其它高级语言连接, 我们 可以用其它语言调用 Mathematica, 也可以在 Mathematica 中调用其它语言编写的程序. 到现 在为止, 能够与 Mathlink 连接的语言包括 C 语言,Excel,Word 等. 事实上 Notebook 就是通过 Mathlink 与 Mathematica 核心相连接的. 上面我们介绍的软件都是通用的符号计算系统, 其它通用的符号计算系统还有 IBM 公司 的 Thomas J. Watson 研究中心开发的 AXIOM, 它的前身称为 SCRATCHPAD. 除了上述通用的符号计算系统以外, 还有一些在某个领域专用的符号计算系统. 例如: 用于 高能物理计算的 SCHOONSCHIP, 用于广义相对论计算的 SHEEP 和 STENSOR. 在数学领域 中用于群论的 Cayley 和 GAP, 用于数论的 PARI, SIMATH 和 KANT. 在代数几何和交换代数 领域中常用的系统是 CoCoA 和 Macaulay. 还有专门计算 Lie 群的 Lie 等等. 1.2 计算机代数系统的网络资源 进入二十世纪九十年代以来, 随着国际互联网的迅速发展, 符号计算系统的发展变的更加 迅速和开放. 从国际互联网上可以获取各种符号计算系统, 以及其他数学软件的相关信息. 有些 新的符号计算系统甚至提供源代码. 有些数学软件还有新闻组或讨论组, 通过讨论组, 用户可以 彼此交流信息、解答问题. 厂家也可以及时发现软件的问题, 进行修改. 下面我们介绍一些常用 数学软件的网络资源, 以及主要研究机构的地址. Mathematica 的网络资源: http://www.wolfram.com http://www.mathsource.com http://www.matheverywhere.com http://smc.vnet.net/MathTensor.html ftp://ftp.mathsource.com news://comp.soft-sys.math.mathematica 1.3 Maple 的基本功能 maillist:[email protected] Maple 的网络资源: http://www.maplesoft.com http://daisy.uwaterloo.ca ftp://ftp.maplesoft.com maillist:[email protected] Matlab 的网络资源: http://www.mathworks.com ftp://ftp.mathworks.com news://comp.soft-sys.matlab REDUCE 的网络资源: http://www.rrz.uni-koeln.de/REDUCE http://www.zib.de/Symbolik/reduce ftp://ftp.rand.org/software_and_data/reduce 3 符号计算研究机构及信息中心 http://symbolicnet.mcs.kent.edu http://www.cain.nl/ http://www.risc.uni-linz.ac.at news://sci.math.symbolic 其它符号计算软件的网络地址: Derive Macaulay2 Macsyma Magma Mathcad MuPad Scilab http://www.derive.com http://www.math.uiuc.edu/Macaulay2/ http://www.macsyma.com http://www.maths.usyd.edu.au:8000/u/magma/ http://www.mathsoft.com http://www.mupad.de http://www-rocq.inria.fr/scilab/ 1.3 Maple 的基本功能 计算机代数系统与其它计算机语言的本质区别是: 计机代数系统具有符号计算的能力, 为用户提供交互式的计算环境, 可以进行常规的数学计算, 可以根据给定的数学函数画出函数 的二维或三维图形. 下面我们简要描述 Maple 的基本功能. 数值计算 对于普通的数,Maple 总是进行精确的计算, 这种规则对于有理数和无理数是相同的. 因此 对于无理数 Maple 按照有关的数学规则进行计算, 只有当用户需要计算浮点数近似值时,Maple 才按照用户要求的精度计算. > 1/5+1/4; 9 20 4 > 第一章 Maple 系统简介 5!/21; 40 7 > evalf(%); 5.714285714 > evalf(Pi,40); 3.141592653589793238462643383279502884197 > 2.496745643/2; 1.248372822 > abs(3+5*I); √ 34 > (3+4*I)/(1+I); 71 +I 22 从上面的例子可以看到, 对于复数 Maple 按照复数的规则进行计算. 多项式 符号计算系统的最基本功能是处理符号表达式, 多项式则是最基本的符号表达式. 从下面 的例子中可以看到 Maple 可以用各种方式处理多项式、 三角表达式、 指数与对数等许多数学表 达式. > factor(x^4+2*x^3-12*x^2+40*x-64); (x − 2) (x3 + 4 x2 − 4 x + 32) > expand((x+1)^5); x5 + 5 x4 + 10 x3 + 10 x2 + 5 x + 1 > simplify(exp(x*log(y))); yx > simplify(sin(x)^2+cos(x)^2); 1 > expand((x^2-a)^3*(x+b-1)); x7 + x6 b − x6 − 3 x5 a − 3 x4 a b + 3 x4 a + 3 x3 a2 + 3 x2 a2 b − 3 x2 a2 − a3 x − a3 b + a3 > expand(cos(4*x)+4*cos(2*x)+3,trig); 8 cos(x)4 1.3 Maple 的基本功能 > 5 combine(4*cos(x)^3,trig); cos(3 x) + 3 cos(x) 解方程 用 Maple 来解简单的方程是毫无问题的, 即使是很复杂的方程,Maple 也可以用数值计算的 方法来处理. > solve(x^2-3*x=2,x); 3 1√ 3 1√ + 17, − 17 22 22 > > glsys:={2*x+3*y+z=1,x-y-z=4,3*x+7*z=3}: solve(glsys); −24 97 −43 {z = ,x= ,y= } 41 41 41 fsolve({x^2+y^2=10,x^y=2},{x,y}); {x = 3.102449071, y = .6122170880} > 矩阵计算 Maple 还有许多命令可以处理矩阵和向量, 不过需要调用线性代数软件包 linalg. 还有一 点特别的是, 作矩阵的乘法需要一个特殊的算子 &*. > with(linalg): Warning, new definition for norm Warning, new definition for trace > a:=matrix([[2,3],[1,4]]); 2 3 a := 14 > inverse(a),det(a); −1 5 > 4 5 −3 5 , 5 2 5 x z b:=matrix([[w,x],[y,z]]); w b := y 6 > 第一章 Maple 系统简介 evalm(a+b); 2+w 1+y > 3+x 4+z evalm(a &* b); 2w + 3y w + 4y 2x + 3z x + 4z 极限, 求和与乘积 对于普通的求极限问题, 可以直接用 Maple 来计算, 它还可以符号的计算级数的和与积. 当 符号计算不成功时, 还可以作数值计算. > limit((sqrt(1+x)-1)/x,x=0); 1 2 > limit(x!/x^x,x=infinity); 0 > sum(1/2^n, n=1..infinity); 1 > evalf(product(1+1/x^2, x=1..infinity)); 3.676077910 微分与积分 用 Maple 来求微分是相当容易的, 使用 diff 命令即可以求出数学表达式的微分, 不过求出 的结果可能是相当复杂, 因此通常还要用 simplify 命令进行化简. 求数学表达式的定积分和不 定积分就相对复杂一些, 需要某些特定的算法. 对于复杂的函数, 求出的结果可能是某些特殊函 数. 对于定积分, 还可以用 evalf 求出积分的数值. > simplify(diff((x-1)/(x^2+1),x)); x2 − 1 − 2 x − (x2 + 1)2 diff(sin(x*y),x); cos(x y ) y > > int(1/(1+x+x^2),x); √ 1 2√ 3 arctan( (2 x + 1) 3) 3 3 1.3 Maple 的基本功能 > 7 int(sin(x^2),x=a..b); √ √ 1 b 2√√ 1 a 2√√ FresnelS( √ ) 2 π − FresnelS( √ ) 2 π 2 2 π π > int(sin(x)/x,x=0..5); Si(5) > evalf(%); 1.549931245 微分方程 对于不太复杂的常微分方程,Maple 可以求出它的符号解. 如果你没有给初始条件, 或者给 的初始条件或边界条件不全, 在解的公式中会带有积分常量. > deq:=diff(y(x),x)*y(x)*(1+x^2)=x; ∂ deq := ( y(x)) y(x) (1 + x2 ) = x ∂x dsolve({deq,y(0)=0},{y(x)}); y(x) = ln(1 + x2 ), y(x) = − ln(1 + x2 ) > > dsolve((y(x)^2-x)*D(y)(x)+x^2-y(x)=0,{y(x)} ); 13 1 x − y(x) x + y(x)3 = C1 3 3 级数展开 当数学问题比较复杂时, 求出准确解通常是不可能的, 用 series 作级数展开是有帮助的. > series(sin(x),x=0, 10); 1 15 1 1 x − x3 + x− x7 + x9 + O(x10 ) 6 120 5040 362880 例如在下列微分方程中, 就是用级数方式求出的微分方程级数解. > > Order:=10: deq:=diff(y(x),x$2)+diff(y(x),x)+y(x)=x+sin(x ); deq := ( ∂2 ∂ y(x)) + ( y(x)) + y(x) = x + sin(x) ∂x2 ∂x sln1:=dsolve({deq, y(0)=0, D(y)(0)=0},{y(x)},series); 1 14 15 16 1 1 1 sln1 := y(x) = x3 − x− x+ x− x7 − x8 + x9 + O(x10 ) 3 12 120 240 5040 20160 181440 > 8 Laplace 和 Fourier 变换 第一章 Maple 系统简介 Laplace 变换和 Fourier 变换是常用的数学变换. 在 Maple 中有一个积分变换的程序包 inttrans 提供了各种积分变换和它们的逆变换. > > with(inttrans): laplace(cos(t-a),t,s); s cos(a) + sin(a) s2 + 1 > invlaplace(%,s,t); cos(a) cos(t) + sin(a) sin(t) > combine(%,trig); cos(t − a) > > > alias(sigma=Heaviside): f:=sigma(t+1)-sigma(t-1): g:=simplify(fourier(f,t,w)); I (π Dirac(w) w − I ) sin(w) g := 2 w 插值与函数拟合 interp命令可以由 n 个点出发计算 n − 1 阶的插值多项式. 在下例中,x 的取值是 1 到 10, y 的值是 1 到 10 之间的 10 个随机数.f 是相应的插值多项式. > > > datax:=[seq(i,i=1..10)]: datay:=[seq(rand(10)(),i=1..10)]: dataxy:=zip((x,y)->[x,y], datax, datay); dataxy := [[1, 1], [2, 0], [3, 7], [4, 3], [5, 6], [6, 8], [7, 5], [8, 8], [9, 1], [10, 9]] > f:=interp(datax, datay, x); 17 517 8 11699 7 3719 6 27323 5 176741 4 652577 3 x9 − x+ x− x+ x+ x− x 51840 40320 60480 2880 17280 5760 3240 1816483 2 1669153 x− x + 293 + 3360 2520 f := 使用数值逼近程序包 numapprox 中的 pade 命令可以计算一个给定函数的有理逼近函数, 以及其它类型的逼近函数. > > with(numapprox): x0:=solve(x^2=Pi/2)[1]; x0 := 1√ √ 2π 2 1.3 Maple 的基本功能 > 9 f:=pade(tan(x^2), x=x0, [3,3]); 2 + 10800 %1 π 7 + 43200 %1 π 8 − 7680 %1 π 10 √ √ √ 2 25/2 3 − 3072 %1 π 2 − 32400 π 15/2 2 + 3840 π 23/2 2 + 28800 %1 π 9 + 3072 %1 π 12 √ √ 2 2 + 23040 %1 π 21/2 2 + 14400 %1 π 17/2 2 − 11520 %1 π 11 ) ( 3 f := (−17280 π 19/2 √ 3 3 (−11520 π 11 + 1024 π 13 − 14400 π 9 − 10800 π 7 ) %1 √ √ √ 2 + (7680 π 23/2 2 − 11520 π 19/2 2 + 21600 π 15/2 2) %1 + (−7680 π 12 + 34560 π 10 + 64800 π 8 ) %1) 1√ √ %1 := x − 2π 2 > evalf(normal(f)); 6.(−.4532958122 109 x2 − .1125313130 109 + .1054184360 109 x3 + .5353835473 109 x) ((2. x − 2.506628274) (−.1097168700 109 x2 + .8958248690 109 x − .1356288866 1010 )) 图形 最常用的画图命令是 plot 和 plot3d. 下面的例子说明了使用在两个命令的方法. > plot(sin(x)*exp(1)^(-x/7), x=0..4*Pi); 0.8 0.6 0.4 0.2 0 -0.2 -0.4 2 4 6x 8 10 12 > plot3d(sin(x)*exp(1)^y, x=0..2*Pi, y=0..Pi, axes=boxed); 20 10 0 -10 -20 0 0 0.5 1 1.5 y 2 2.5 3 6 5 4 3 x 2 1 Maple 编程 Maple 不仅可以对数学表达式进行计算, 还可以编程. 他的编程语言和其它的结构化编程 语言很相似. 10 > > > > > > > 第一章 Maple 系统简介 f:=proc(x::nonnegint) option remember; if x=0 then 0 elif x=1 then 1 else f(x-1)+f(x-2) end if end proc: f(40); 102334155 1.4 Maple 系统的交互使用 Maple 的窗口环境提供了先进的工作区界面, 其扩充的数学功能简明易用, 用户可以在其 中展现数学思想, 创建复杂的技术报告, 充分发挥 Maple 的功能.The Maple Window • 1.3 图 1.1: Maple 的窗口环境 3 A B C D E F G H I K L M N J A Maple 的工具条 Figure 1-A Maple window features A toolbar containing shortcut buttons. B Context bar “>”, 显示为红色 D Maple 的输入, toolbar containing context-sensitive shortcut buttons. (This means that the A 提示符为 buttons change based on the cursor location or selection.) It can also contain a field for entering and editing text. C Section heading The name or title of a section. D Maple input A mathematical expression that Maple evaluates. By default, input commands are entered at the prompt, “>”, and are displayed in red type. The resulting output is displayed beneath. C 节的头部及标题 B 内容工具条, Toolbar A 它还包含一个输入和编辑文本的区域 1.4 Maple 系统的交互使用 E Maple 的输出, 既执行 Maple 命令的结果, 通常显示为蓝色 F 一组 Maple 命令及其输出 G Maple 的工作区 H 工作区元素组成的节 I 节的范围: 用一个大的方括号 “[” 表示 J 省缺的 Maple 输入提示符 K 符号模板, 包含了许多常用的数学符号 L 表达式模板 M 矩阵模板 N 向量模板 Maple 工作区界面 11 Maple 的图形界面具有现代应用软件界面的常见功能, 它支持鼠标操作, 包括剪切和粘贴等 功能, 如果你已经习惯了这些用法, 那就具备了使用 Maple 工作区界面的基本知识. 现在你可 以执行一些标准的操作, 例如: 打开文件、保存和打印文件等. 对于 Windows 平台, 只要双击 Maple 图标即可启动 Maple. 在 Unix 系统下, 可在提示符 之后键入 xmaple 命令来启动.Maple 启动后将开启一个新的工作区. 在窗口上端是菜单条, 包括 File 和 Edit 等菜单项, 菜单条之下是工具条, 其中有若干用于 经常性操作的快捷按钮, 如文件打开, 保存和打印等. 工具条之下是内容指示条, 其中有一些控 件规定当前执行的任务. 再向下是较大的工作区区域, 也就是你的工作区. 窗口的最下端是状态 条, 其中显示系统信息. 作为 Maple 用户界面的一个组成部分, 工作区是用户交互的求解问题和把工作写成文档的 集成环境. 所谓交互的求解问题, 简单的说就是输入适当的 Maple 命令, 得到结果. 在工作区中 可以修改命令, 重新执行并获得新的结果. 除了 Maple 命令及其结果以外, 还可以在文档中加入 许多其他类型信息. 主要包括: • 可以加入文本, 用户能够逐个字符地控制文本段落. • 在文本段中, 可以加入数学表达式和 Maple 命令. • 可以加入超连接, 当用鼠标单击某特定文本区域时, 能跳转到工作区的其他位置, 或其它文 本中. • 可以规定文档的结构, 包括超连接, 节与小节的划分. • 在 Windows 平台上, 用户可以嵌入其他对象, 可借助 OLE 2(对象连接与嵌入标准) 嵌入图 形和表格. 添加标题 在 Maple 的工作区中不仅可以作数学计算, 还可以编写文档. 首先我们可以给文档加标题. 具体步骤是: 将光标移到第一行, 在 Insert 菜单的 Execution Group 中选择 Region Before 项, 此时在顶部出现一个新区域. 这个区域包含一个 Maple 输入的提示符, 这意味着此时是输入 Maple 命令的状态. 点击工具条上的 T 按钮或从 Insert 菜单中选择 Text Input 项, 就把这个 区域变成了文本输入状态, 现在就可以输入文本. 此时在工具条下面又出现了一个新的文本选 择工具条, 从中你可以选择文本的字体格式等. 如果你输入的是文章的标题, 就可以在文本格式 12 第一章 Maple 系统简介 的下拉菜单中选择标题格式. 输入标题后回车, 系统会自动要求你输入作者的名字, 输入完作者 名以后就可以输入正文了. 添加小标题 对文档的进一步加工是把文档分解为节. 具体作法是首先用鼠标选定相关的区域, 然后点 击工具条中的 ⇒ 键, 此时就在选定的区域前面出现了一个小方块, 下拉一个大括号, 括住了选 定的区域. 并且在这个区域的第一条命令之前插入一个文本区域, 此时你可以输入节的标题, 回 车后还可以输入其他说明文本. 如果需要开始新的一节, 可以在 Insert 菜单中选择 section. 就可以在这一节之后创建新的一节. 行内数学表达式 在一个文档中有时需要插入数学表达式, 如下面一段文字: Look at the integral on the parameter a. x2 sin(x − a)dx. Notice that its integrand, x2 sin(x − a), depends 在其中插入数学公式的方法是: 首先将光标移到相应的位置, 从 Insert 菜单中选择 Math Input 项, 然后输入对应于 x2 sin(x − a)dx 的 Maple 代码, 即 Int(x^2*sin(x), x), 注意观察内容指 示条中的编码区域, 其中显示输入的代码, 而工作区中则显示使用标准数学符号的积分表达式. 在数学表达式输入完成后, 再将输入状态变成文本输入状态, 就可以继续输入其他文本. 这样就 完成了我们的文档, 它既可以保存也可以打印. 添加超连接 在 Maple 系统中, 用户可以同时打开多个工作区, 在不同的工作区之间可以通过建立超连 接的方式建立联系. 建立超连接的方法是: 在一个工作区中用鼠标选定一个位置, 在 Insert 菜 单中选择 Hyperlink 项. 此时弹出一个对话框, 它要求用户输入联接的文字和另一个工作区的 文件名. 填写完成后单击 OK 键就完成了超连接. 建立书签 在工作区中可以插入书签, 以便迅速的查找内容. 单击指向书签的超连接,Maple 将立即转 至书签位置. 建立书签的方法是: 首先将光标移动到要插入书签的位置, 从 View 菜单中选择 Edit Bookmark 项. 在弹出的对话框中键入一段文字, 例如 “expr command” 作为书签文本, 单 击 OK 按钮插入书签. 当你移动光标到工作区的任何位置时, 从 View 菜单中选择 Bookmark, 再 从弹出的菜单中选择 expr command 项, 就可以跳到你插入书签的位置. 此外超连接的方式也可以使用书签. 具体作法是: 首先按照前面的方法建立书签, 将光标 移动到建立超连接的位置, 在 Insert 菜单中选择 Hyperlink 项. 在弹出的对话框中输入联接的 文字, 然后在 Book Mark 区域添入你已经建立的书签的标记, 例如 “expr command”, 单击 OK 键 就完成了超连接. 帮助系统 前面我们介绍了 Maple 的计算和排版方面的能力, 然而这只能是简介, 在本书中, 我们不可 能详尽的描述 Maple 的所有命令, 因为 Maple 包含了数以千计的命令. 为了了解这些命令的使 用方法, 可以使用 Maple 软件带有的一个自足的参考手册, 即 Maple 的帮助系统. 借助帮助系 1.5 Maple 的组织结构 13 统, 可以按名字或主题查询 Maple 命令及其特点. 此外用户还可以自行选择关键词或术语, 来 迅速打开含有这些文字的帮助页面. 在每个帮助页面中还提供了超连接, 使用户可以阅读相关 的页面. 在帮助系统中,Maple 提供了三种方法定位信息: 按目录、 按主题和按全文查找. 从 Help 菜 单中选择 Contents, 帮助窗口将变为帮助系统的一个简单目录, 用户可以通过超连接的方式浏 览帮助系统. 这就是按目录的查找方法. 通过这种方法我们可以大致了解 Maple V 的基本功 能, 但是要从中找到某个特定的主题还是很困难的. 按主题查找的方法是: 从 Help 菜单中选择 Topic Search, 此时帮助窗口将弹出一个对话框, 在其中添入需要查找的主题, 点击 OK 键, 就可 以阅读相应的帮助文档. 如果已经知道希望阅读的主题词, 也可以直接从工作区访问该页面, 办 法是在 Maple 提示符后键入 ?topic, 回车后就可阅读相应的页面. 在大多数 Maple 版本中 (唯一的例外是 Maple V Realese 4 版本), 进入帮助系统后,Maple 会打开帮助浏览器, 通过帮助浏览器可以方便地找到你需要的帮助. 有的时候, 在解决某个数学问题时不知道应该使用 Maple 的什么命令, 但是由数学问题本 身出发, 有理由推测, 在这些命令的帮助页面应当包含某些特定单词, 此时就要用到全文查找的 方法. 例如我要解一个微分方程, 但是不知道应该用什么命令, 我们可以推测, 在这个命令的帮 助中应该包含 solve, differential 和 equation 等单词, 时可以在 Help 菜单中选择 Full Text Search, 在弹出的对话框中, 输入要查找的关键词, 例如 solve differential equation 等, 然后单击 Search 按钮, 通知 Maple 开始检索.Maple 将列出匹配的主题, 并附带数值, 表明匹配的程度, 用 户可从列表中选择最感兴趣的主题. 此外从 Help 菜单中选定 Balloon Help 项以后, 当鼠标停留在某个按钮或菜单上时,Maple 就显示简短的说明. 这也是一个很有用的功能. 1.5 Maple 的组织结构 Maple 是由加拿大 Waterloo 大学的符号计算组开发的计算机代数系统. 它可以在各种计 算机上运行, 从超级计算机, 例如 Cray Y/MP, 到用于桌面的微型计算机, 例如 IBM PC 兼容 机.Maple 既可以在单用户的操作系统, 例如 MS-Windows 上, 由一个用户使用, 也可以在多用 户操作系统, 例如 Unix 系统上, 由许多用户同时使用.Maple 可以适应这么多种不同的计算机体 系和操作系统, 主要原因是它的模块化设计. Maple 系统由三个主要模块组成. 分别为: 用户界面, 也称为 Iris; 基本代数运算库, 或称为 kernel(核心); 以及外部库函数. 用户界面与核心构成了系统的一小部分, 它们是用 C 语言编写的, 当 Maple 系统启动时就 被调入. 用户界面负责处理数学表达式的输入、 表达式的显示输出、 函数图形的输出等. 对于各 种窗口环境, 它还提供了工作区的界面. Maple 的核心部分解释用户的输入, 进行基本的代数计算, 诸如有理数的计算、 多项式的初 等计算等. 此外核心部分还负责变量存储的管理,Maple 的一个非常重要的功能是: 在会话区中 对每个表达式或子表达式, 内存中仅保留一个拷贝. 这样就节省了许多内存空间, 也提高了运行 速度.Maple 核心的最重要的功能是为 Maple 程序语言提供解释, 换句话说,Maple 核心是 Maple 程序语言的解释器. 作为一种解释型语言,Maple 的执行效率会受到一些影响, 但是它也为系统 的数学知识的扩充和升级提供了便利的条件. 这也是从事符号计算研究的学者喜欢使用 Maple 14 第一章 Maple 系统简介 的原因. Maple 的绝大部分数学知识是用 Maple 的程序语言编写的, 它们以函数或过程的形式驻留 在外部程序库中. 当用户需要一个库函数时, 在大多数情况下,Maple 都能够调用你需要的库函 数, 只有很不常用的函数需要用户手动的调入.Maple 的程序库分为三组, 主库, 杂库和程序包, 这三组库及其中的函数均建立在内核之上. 主库包括最常用的 Maple 命令 (不同于内核). 杂库 由许多不太常用的数学命令组成, 因为不是预定的读入库, 在用之前必须借助 readlib 命令调 入, 命令的用法是: readlib(cmd), 其中 cmd 是要求 Maple 调入的命令. 最后一组库是程序包, Maple 的每一个程序包含有一组彼此关联的计算命令. 例如 linalg 程序包含有矩阵处理的有 关命令. 可以用三种不同方式使用程序包中的命令. 1. 使用程序包和所需命令全名:package[cmd](· · ·). 2. 利用 with 命令加载程序包中所有命令:with(package), 然后使用短命令名. 3. 从程序包中加载单个命令:with(package,cmd), 然后使用短命令名. 例如下面的例子使用了在 student 程序包中的 distance 命令计算两点之间的距离. > with(student); [D, Diff , Doubleint , Int , Limit , Lineint , Product , Sum , Tripleint , changevar , combine , completesquare , distance , equate , extrema , integrand , intercept , intparts , isolate , leftbox , leftsum , makeproc , maximize , middlebox , middlesum , midpoint , minimize , powsubs , rightbox , rightsum , showtangent , simpson , slope , trapezoid , value ] > distance([1,1],[3,4]); √ 13 利用 with(packages) 加载一程序包时, 可以看到程序包中所有命令的短格式名字清单, 此 外, 如果重复定义已存在命令名,Maple 将提出警告. 程序包清单 程序包命的完整清单, 可参阅帮助页 ?index,package. algcurves 研究一维代数簇 (代数曲线) 的工具. codegen 将 Maple 过程转化为其它语言 (C 语言和 Fortran 语言) 的工具 combinat 组合函数, 包括计算列表置换与组合以及整数划分的命令. combstruct 组合结构的生成和计数命令. context 在 Maple 图形界面中建立和改进与内容相关的菜单. CurveFitting 支持曲线拟合的命令. DEtools 微分方程相图和场图的处理和图象工具. diffalg 处理多项式形式的微分方程组的命令 difform 微分几何中微分形式的处理命令. Domains 为数环, 有限域, 多项式环和矩阵环上的多项式和矩阵计算创建支撑整环的命令. ExternalCalling 连接外部函数的命令. finance 金融计算命令. 1.5 Maple 的组织结构 15 GaussInt 处理 Gauss 整数的命令,Gauss 整数即形如 a + bI 的复数, 其中 a, b 是整数, 包括求 GCD, 因子分解, 素性判定等. genfunc 处理有理广义函数的命令. geom3d 三维 Euclid 几何的命令, 包括点, 线, 面, 三角形, 球和多面体的处理. geometry 二维 Euclid 几何的命令, 包括点, 直线, 三角形和圆的处理. Grobner 计算 Gr¨bner 基, 处理和求解较大的多项式表达式集合时特别有用. o group 置换群和有限表示群计算的命令. inttrans 积分变换及其逆的有关命令. liesymm 偏微分方程紧对称系统表示命令. linalg 包括 100 个以上的命令, 用于处理矩阵和向量运算, 从矩阵加法到符号特征根和特征向 量, 几乎无所不包. LinearAlgebra 另一个处理线性代数的程序包, 它特别适用于处理大矩阵. LinearFunctionalSystems 解具有多项式系数的线性泛函方程组的命令. ListTools 管理列表的工具. LREtools 线性递归方程的求解及其图象处理命令. MathML 将 Maple 表达式导入或导出为 MathML 形式的文本. Matlab 连接和使用 Matlab 系统的命令 networks 构造, 绘制和分析组合网络的工具, 具有处理有向图及其点和边的任意加权表达式的 功能. numapprox 计算在指定区间上函数的多项式逼近. numtheory 经典数论的某些命令, 包括素性判定, 求第 n 个素数, 整数因子分解, 生成循环多项 式等, 该程序包还包括处理收敛性的指令. Ore algebra 处理线性算子代数的命令 orthopoly 生成各种类型的正交多项式, 这对微分方程求解非常重要. padic 计算实数的 p– 进逼近的命令. plots 不同类型的专用图象命令, 包括轮廓图, 二维和三维透视图, 图象文本, 以及不同坐标系 下的图象. PDEtools 求解或者画出偏微分方程的工具. plottools 生成和管理图形对象的工具. PolynomialTools 处理多项式对象的工具. powseries 创建和管理一般形式的形式幂级数. process 允许用户在 UNIX 系统下书写多进程 Maple 程序. RandomTools 处理随机对象的命令. RationalNormalForms 将有理函数构造为多项式典范形式和有理范式的命令. RealDomain 提供了一个环境, 使得基本数域是实数域而不是复数域. simplex 利用单纯形算法进行线性优化的命令. Sockets Maple 的网络通讯工具. Slode 求线性常微分方程的形式幂级数解的命令. SolveTools 解代数方程组的命令. Spread 在 Maple 中处理电子数据表的命令. 16 第一章 Maple 系统简介 stats 简单的统计数据管理, 包括平均值, 标准差, 相关系数, 方差和回归分析等. StringTools 优化串操作的命令 student 由浅入深的微积分计算命令, 包括分部积分,Simpson 法则, 极大化函数, 求极值等. sumtools 计算不定的和与确定的和的命令, 包括 Gosper 算法和 Zelberger 算法. tensor 计算张量及张量在广义相对论中的应用的命令. Units 不同计量单位之间的转换命令. XMLTools 将 Maple 表示为 XML 文档的命令. 除了我们上面提到的三组库函数以外, 在某些 Maple 系统的标准发布中还提供共享库. 在 Maple V 的各个版本中, 其标准发布中包含了共享库, 但是在 Maple 6 和 Maple 7 的标准发布中 不包含共享库, 用户可以从 http://www.maplesoft.com 的 Application Center 下载共享库的程 序包. 共享库是由 Maple 的用户用 Maple 程序语言编写的一些额外命令和程序包的汇集. 在 Maple V 中使用共享库的方法是: 首先调用 with(share) 调入共享库, 然后可以使用 ?share,contents 等命令查询一下共享库的内容. 共享库的内容分为下列几个大类, 可以使用相应的查询命令查 询. 范畴 Algebra Analysis Calculus Combinatorics Conversions Courses Engineering Geometry Linear Algebra Modular computations Numerics Number Theory Graphics Programming Science Statistics System Tools 查询命令 ?share,algebra ?share,analysis ?share,calculus ?share,combinat ?share,convert ?share,courses ?share,engineer ?share,geometry ?share,linalg ?share,mod ?share,numerics ?share,numtheor ?share,graphics ?share,program ?share,science ?share,stats ?share,system 当你找到了你需要的共享库时, 可以使用 readshare 命令调入. 例如, 我需要使用计算特 征列的共享库 charsets, 从上表中可以查到 charsets 是在 Algebra 这个范畴中, 那么调用它的 命令就是 readshare(charsets,algebra);. 当调入共享库以后, 你可以使用 ?command 来看这 个共享库中的各个命令的帮助文本. 象标准库一样, 共享库的命令也存储在 maple.lib 文件中, 但是这个文件在 share 目录中. 1.6 输入与输出 17 此外,share 目录树中包含了大量的辅助文件: 包括程序代码的 ASCII 文件 (*.mpl 文件), 帮助 文件 (*.mph 文件), 说明这些函数应用的工作区文件 (*.mws 文件). 以及描述这些程序算法、 数 A X文件等. 通过阅读这些文件, 读者可以对你所使用的命令有更深入 学背景的 TEX文件或 L TE 的了解. 作为 Maple 系统的重要的组成部分,Maple 还提供了两个应用程序:march 和 mint. march 程序是用来管理 Maple 库的命令.mint 是对用户编写的 Maple 程序进行语法检查的程序. 有关 它们的使用细节可以参考相应的帮助文档, 我们将在以后的章节中讨论. 最后, 我们还要说明一点,Maple 是一个开放的系统, 除了核心部分与编译部分的代码以外, 它的绝大部分程序代码都是公开的. 通过阅读 Maple 的程序代码, 你可以更深入的了解 Maple 的工作方式. 要想看到这些代码, 你需要将 interface 变量 verboseroc 设置为 2, 然后对于 Maple 的函数使用 print 命令. 例如 > > interface(verboseproc=2); print(cos); proc(x) local n, t; option ‘Copyright(c) 1992 by the University of Waterloo. All rights reserved.‘; if nargs <> 1 then ERROR(‘expecting 1 argument, got ‘.nargs) elif type(x, ’complex(float)’) then evalf(’cos’(x)) elif type(x, ‘*‘) and member(I, {op(x)}) then cosh( - I * x) elif type(x, ’complex(numeric)’) then if csgn(x) < 0 then cos( - x) else ’cos’(x) fi ... end 这里我们仅列出了程序的很少一部分, 关于程序设计的更多细节将在以后的章节中讨论. 1.6 输入与输出 在 Maple 的工作区内用户可以直接作许多工作. 你可以要求作计算, 作图和把结果作成文 档. 然而, 在某些时候, 为了与其它软件配合, 你可能需要输入数据或输出结果到文件. 这些数 据可能是科学实验的测量值或由其它程序生成的数据. 一旦把这些数据输入 Maple, 你就可以 利用 Maple 的作图功能显示结果, 利用 Maple 的运算功能构造和研究其相应的数学模型. 读入文件 在从文件读入信息时, 两个最常用的操作是从文本文件读入数据和读入存储在文本文件中 的 Maple 命令. 第一种情况适用于读入由实验生成的数据或其它程序产生的数据, 在文本文件中存储的数 据要求是由空格和换行符分割开的数据. 对于这种文本文件使用 Maple 的 readdata 命令就可 以把它们读入 Maple. 例如下面的文本文件 data.txt, 它的格式如下: 010 1 .540302 .841470 18 2 -.416146 .909297 3 -.989992 .141120 4 -.653643 -.756802 5 .283662 -.958924 6 .960170 -.279415 第一章 Maple 系统简介 readdata的用法是:readdate(‘filename‘,n). 这里 filename 是文件名,n 是列数. 如果 n 是 1, 则 readdata 给出一个数字的列表. 不然, readdata 返回一个列表的列表, 每一个子列表对应文 件中的一行. 文件 data.txt 有三列. 读入的方式如下: > L:=readdata(‘data.txt‘,3); L := [[0, 1., 0], [1., .540302, .841470], [2., −.416146, .909297], [3., −.989992, .141120], [4., −.653643, −.756802], [5., .283662, −.958924], [6., .960170, −.279415]] 第二种情形是由文本文件读入命令. 当你编写一个 Maple 程序时, 当然可以直接在 Maple 工作区中工作, 但是当你编写的程序比较复杂时, 最好的工作方式是: 使用你所喜欢的文本编 辑器把它写出来并存为文本文件. 然后你可以用 read 命令读入文件, 或者在 File 菜单选择 Import Text 来读入文件. read 命令的具体用法是:read ‘filename‘. 例如下列文件 ks.mpl 是 用 Maple 语言写的一个函数的定义. S:= n -> sum( binomial(n,beta) * ( (2*beta)!/2^beta-beta!*beta ), beta=1..n); 在一般情况下, 当你读入这个文件时, Maple 只显示命令执行的结果, 而不显示命令本身. > read ‘ks.tst‘; n S := n → β =1 binomial(n, β ) ( (2 β )! − β! β) 2β 如果你设置 interface 变量 echo 为 2, Maple 就把文件中的命令在你的工作区中显示出来. > > > > interface(echo=2); read ‘ks.tst‘; S:= n -> sum( binomial(n,beta) * ( (2*beta)!/2^beta-beta!*beta ), beta=1..n); n (2 β )! S := n → binomial(n, β ) ( β − β ! β ) 2 β =1 read 命令也能读 Maple 的内部格式文件, 这些文件的名字一般为 filename.m, 它们通常是 在 Maple 中用 save 命令保存产生的, 对于 Maple 内部文件格式,Maple 系统可以有效的检索它 们.save 命令的使用方式是: save nameseq, ‘filename.m‘. 这里 nameseq 是一系列名字, 你仅 能保存命名的对象, 这个对象可以是一个变量, 也可以是一个过程. 例如, 下面是几个表达式. > > qbinomial:=(n,k)->product(1-q^i,i=n-k+1..n)/ product(1-q^i,i=1..k); 1.6 输入与输出 n 19 (1 − q i ) qbinomial := (n, k ) → i=n−k+1 k (1 − q i ) i=1 > expr:=qbinomial(10,4); expr := (1 − q 7 ) (1 − q 8 ) (1 − q 9 ) (1 − q 10 ) (1 − q ) (1 − q 2 ) (1 − q 3 ) (1 − q 4 ) > nexpr:=normal(expr); nexpr := (q 6 + q 5 + q 4 + q 3 + q 2 + q + 1) (q 4 + 1) (q 6 + q 3 + 1) (q 8 + q 6 + q 4 + q 2 + 1) 现在你可以把这些表达式存入文件 qbinom.m 中. > save qbinomial,expr,nexpr,‘qbinom.m‘; restart命令将这三个表达式从记忆中清除. 于是下面 expr 所求的值为它自己的名字. > > restart; expr; expr 用 read 命令恢复你存入 qbinom.m 的表达式. > read ‘qbinom.m‘; 现在 expr 又具有了它的值. > expr; (1 − q 7 ) (1 − q 8 ) (1 − q 9 ) (1 − q 10 ) (1 − q ) (1 − q 2 ) (1 − q 3 ) (1 − q 4 ) 把数据写入文件 在用 Maple 完成某个计算后, 你可能想把结果存入文件. 使得你以后可以再用 Maple 或其 它程序处理这个结果. 如果 Maple 的运算结果是数字的长列表或大的阵列, 你可能想把这些数字结构化地写入文 件. 命令 writedata 用于写入数值列数据, 使得你可以把这些数字输入其它程序. 使用 writedata 命令的方式为 writedate(‘filename‘,data). 这里,filename 是 writedata 放数据的文件, 而 data 是一个列表、 向量、 列表的列表或矩阵. 如果 filename 是专用名称 terminal, 则 writedata 把数据写在你的屏幕上. 注意 writedata 覆盖已经存在的 filename. 如果你想把数据添加到已 经存在的文件中, 可以使用 writedata[APPEND](‘filename‘,data) 命令. 如果数据是数字的向量或列表, 则 writedata 一个数字写一行. > L:=[3,3.1415,-65,0]; 20 L := [3, 3.1415, −65, 0] > 第一章 Maple 系统简介 writedata(‘terminal‘,L); 3 3.1415 -65 0 如果数据是数字的矩阵或列表的列表, 则 writedata 写数据列并用表格键分隔列. > A:=[[1,2,3],[-1.45,0,3/2]]; A := [[1, 2, 3], [−1.45, 0, 3 ]] 2 > writedata(‘terminal‘, A); 3 0 1.5 1 2 -1.45 writedata期望数据是数值的. 你必须在调入 writedata 之前, 把诸如 π 和 e9 等常数值化成浮 点小数. > L:=[Pi,exp(9)]; L := [π, e9 ] > Lf:=evalf(L); Lf := [3.141592654, 8103.083928] > writedata(‘terminal‘,Lf); 3.141592 8103.083928 有关 writedata 命令更详细的用法请参见帮助文档. A 转换成 L TEX格式 A TEX是一个数学排版的程序,L TEX是 TEX的一个宏软件包. latex 命令把 Maple 表达式转 A A 换为 L TEX格式. 因此, 你可以用 Maple 求解问题, 然后把结果转换为 L TEX代码, 你可以将它 A 并入 L TEX文件中. latex 命令的使用方式如下. latex(expr,‘filename‘) A latex 命令把对应 Maple 表达式 expr 的 L TEX代码写入文件 filename. 如果 filename 已 A 经存在,latex 就覆盖它. 你可以省略 filename, 在这种情形 latex 将 L TEX代码打印在屏幕上 A 然后你可以把它剪切下来粘贴进你的 L TEX文件. A 此外 Maple 还可以把整个工作区以 L TEX的格式输出. 通过在菜单 File 中选 Export as A LaTeX, 就可以把一个 Maple 工作区以 L TEX的格式输出. 当你选择 Export as LaTeX 时, 一个对话框将询问排版文件的宽度. Maple 使用这个规格 调整多行显示的表达式. 如果你的工作区含有图像,Maple 生成与图像对应的 PostScript 文件以 A A 及为了包含这些 PostScript 文件于你的 L TEX文件中的 L TEX代码. 此外在 Maple 6 以后的版 本中, 还可以把工作区输出为 html 文件, RTF 文件等. 1.7 Maple 版本的变迁 21 打印图像 在通常情况下, 我们是在屏幕上观察 Maple 产生的图形, 但有的时候, 我们需要将 Maple 产 生的图形保存为文件, 以便其它程序使用. 此时, 你可以用 plotsetup 命令把图像以你选择的格 式输出到文件中. 命令的具体用法是 plotsetup(DeviceType,plotoutput=‘filename‘,plotoption=‘options‘ 这里,DeviceType 是 Maple 使用的图像设备,filename 是输出文件的名字,options 是一些图像驱 动程序所能识别的选择项. 例如下面的例子就以 PostScript 的格式把图像输出到文件 myplot.ps. > plotsetup( postscript, plotoutput=‘myplot.ps‘ ); 由 plot 命令生成的图像不出现在屏幕上, 而是输出到文件 myplot.ps. 如果你想要打印一个以上的图像, 你必须在每一个图像之间改变选择项 plotoutput, 否则 新的图像将覆盖旧的. 在你完成了输出图像之后, 你必须告诉 Maple 把以后的图像重新送到屏 幕. > plotsetup( default ); 参看 ?plot,device 可了解 Maple 对作图设备的描述. 在 Maple 6 以后的版本中, 提供了更方便的输出图形文件的方法. 具体方法是用鼠标右键 单击 Maple 图形, 会弹出一个菜单, 选择其中的 Export As 项就可以把图形存为你需要的各种 图形格式. 1.7 Maple 版本的变迁 Maple 的版本变化很快, 通常情况下一到两年就会有一次版本升级. 本书刚开始写作的时 候, 流行的 Maple 版本还是 Maple Release 4, 可是到 2002 年 5 月 Maple 公司已经开始发布最 新的版本 Maple 8. 虽然 Maple 的版本变化非常快, 但是其基本用法始终没有大的变化. 主要的 变化是 Maple 的程序库进行了更新,Maple 的用户界面做了改进. 在 Maple 版本变迁的过程中, 从 Maple V 到 Maple 6 之间,Maple 的编程语言变化很大. 在 Maple 6 中提供了一种新的编程方法: 模块编程. 模块编程有些类似于 C++ 等面向对象的编程 语言. 模块编程方法极大的丰富了 Maple 语言编程的能力, 使 Maple 能解决更复杂的数学问题. Maple 7 与 Maple 6 相比, 我认为最主要的变化是 Maple 7 的网络功能. 在 Maple 7 中提供了一 个 Sockets 程序包, 应用这个程序包, 用户的程序可以通过 TCP/IP 协议与其他主机进行通讯, 应用这个程序包, 用户也可以编写自己的客户端程序或服务器程序. 从 Maple 公司所发布的 Maple 8 的新功能可以看出:Maple 8 的主要变化在于其教学功能. 例如它的新微积分程序包可以提示用户一步一步的解决微积分的问题, 此外它的 Maplets 程序 包允许用户自己设计用户图形界面. 这些新功能极大的丰富了 Maple 的教学能力. 第二章 2.1 整数和有理数 数值计算 Maple 的最基本计算是数值计算, 与其他计算器不同的是,Maple 自己能区分整数、有理数 计算和无理数、 浮点数的区别. 进入 Maple 以后, 在工作区中按照自然语法输入表达式, 以分号 结束, 即可进行计算. > 1023+43251; 44274 > 21-4/3; 59 3 > 32*(2-1/2)*(3+1/3)/21; 160 21 在输入表达式的过程中需要注意的是: 每个表达式都要以分号结束, 按回车键以后 Maple 就开 始计算. 如果你忘记了分号, 回车以后 Maple 会给出错误提示, 它认为你的表达还没有输入完 全, 只有当你输入了分号 ‘;’ 以后,Maple 才开始计算. > (12+34-4)*12 Warning, incomplete statement or missing semicolon 还有一点需要注意的是:Maple 用 * 代表乘法, 在表达式中如果忘记输入乘法符号, 会得到奇怪 的结果, 例如: > (1+2)(3+4); 3 此时计算的结果是第一个括号内的表达式. 在两个括号之间加上 * 以后才能得到正确的结果. > (1+2)*(3+4); 21 Maple 能计算几乎任意大的整数, 实际上整数计算的极限差不多达到 500000 位十进制数, 准确的数字是 21 9 − 9 = 524279 位十进制数. 这主要取决于 Maple 表示整数的方式. 在系统内 部,Maple 用联贯的字来表示整数, 它们构成的线性列表称为动态数据向量. 其结构如下: intpos integer i0 integer i1 ········· integer in 这个向量的第一个字记录了这个数据结构的所有信息: 它指明这个向量表示一个整数, 向量的 长度是 n + 2 等. 后面的 n + 1 个字包含了一组非负整数 i0 , i1 , · · · , in . 设 B 是基, 则上述的向 量表示整数 i0 + i1 B + i2 B 2 + i3 B 3 + · · · + in B n 22 2.1 整数和有理数 23 Maple 选择基 B 的原则是:B 是 10 的幂, 并且 B 2 可以用一个计算机的字表示, 对于 32 位的计 算机,B = 104 . 由于向量的长度可以动态的选择, 因此 Maple 可以表示很大的整数, 唯一的限制 是这些字的个数必须在第一个字中指定.Maple 用 17 个位来表示向量的长度, 因此整个向量的 长度最大是 217 − 1, 它可以表达的最大整数的位数为 4((217 − 1) − 1) − 1 = 219 − 9. Maple 在计 算时自己可以确定整数是否超出了范围, 如果超出, 它给出错误信息. 例如 > 123456789^987654321; Error, object too large 除了一般的整数计算以外,Maple 还有许多函数可以处理整数的问题. 例如求整数的位数, 分解因数等. > 123456789^987654321; Error, object too large > number:=10^31-10^16-1; number := 9999999999999989999999999999999 > isprime(number); false > > settime:=time(): ifactor(number); ( 27615280443656567) ( 3246769) ( 111531913) > cpu_time:=(time()-settime)*seconds; cpu time := 7.012 seconds > nextprime(number); 9999999999999990000000000000049 > isqrt(number); 3162277660168378 当整数特别大时, 分解因数将耗费很多时间. 使用 Maple 提供的 time 过程可以大致的求出所 用的时间. 具体作法见上例. 在下表中我们给出了 Maple 提供的与整数计算有关的命令. 与整数计算有关的命令 abs irem isqrt igcd sign iquo iroot ilcm max modp isprime igcdex min mods ifactor iratrecon factorial mod ifactors rand 24 第二章 数值计算 在整数的计算过程中, 整数的带余除法, 求两个整数的最大公因数和最小公倍数起了关键 的作用. > > a:=1234: b:=56: q:=iquo(a,b); q := 22 > r:=irem(a,b); r := 2 > a=q*b+r; 1234 = 1234 > igcd(a,b); 2 > igcdex(a,b,’s,’t’); 2 > s; 1 > t; −22 > a*s+b*t; 2 使用 igcdex 过程,Maple 可以计算整数的扩充最大公因数; 也就是对任意两个整数 a, b, 求出两 个整数 s, t 满足 as + bt = gcd(a, b). 在使用 igcdex 命令时, 我们给 s,t 加了单引号, 它的含义 是抑制对变量求值, 我们将在下一章中详细解释这个概念, 在这里的作用是记录 igcdex 的另外 两个返回值. 在整数分解和整数的素性检验过程中, 模算术起了非常重要的作用.Maple 提供了三种形式 的模函数 mod, modp, mods. 模函数 mod 有下列两种调用方式 > 11 mod 7; 4 > ‘mod‘(11,7); 4 2.1 整数和有理数 25 第一种方式是把 mod 作为中置算子来使用, 这符合我们通常的使用习惯. 第二种方式是把 mod 作为函数来调用, 此时必须用左单引号将 mod 括起来. 有关单引号的具体用法和含义将在下一 章介绍. 模运算的结果取决于环境变量 ‘mod‘ 的值, 省确情况下 ‘mod‘ 的值为 modp. > modp(11,7); 4 此时得到的结果与直接调用 modp 的结果是一样的. 如果是模 n 的算术, 那么它返回的结果是 [0, |n| − 1] 区间中的正整数. 如果你把 ‘mod‘ 的值设置为 mods, 或者直接调用 mods, 那么得到 的结果在下列关于 0 对称的序列中 − |n| − 1 |n| |n| , . . . , −1, 0, 1, . . . , −1 , 2 2 2 例如 > > ‘mod‘:=mods: ‘mod‘(11,7); −3 在与整数计算有关的 Maple 命令中, 有一个比较特殊的命令 iratrecon, 它的作用是从一 个有理数模 m 的象 u 重新构造出原有理数. 具体用法是 iratrecon(u,m,N,D,’n’,’d’) 其中 N, D 是有理数分子和分母的上界. 如果 iratrecon 返回的值为真, 则它返回的 n, d 将满足: n/d ≡ u (mod m) 这里 |n| ≤ N, |d| ≤ D. 使用方法如下: > readlib(iratrecon): m := 11; m := 11 > u := 1/2 mod m; u := 6 iratrecon(u,m,2,2,’n’,’d’); true n/d; 1 2 > > 对于这种不很常用的函数,Maple 启动时并不把它调入系统中, 你需要用 readlib 命令手动的调 入.readlib(iratrecon) 就是完成这项工作的命令. 26 第二章 数值计算 2.2 无理数和浮点数 在上一节中, 我们看到对于有理数,Maple 可以自动化简. 一般情况下,Maple 并不这样做. 它总是按照你的命令来工作. 例如: > 25^(1/6); 251/6 > simplify(%); 51/3 > evalf(%%); 1.709975947 > convert(%%%,‘float‘); 1.709975947 在这个例子中, 我们看到对于第一个输入 25^(1/6),Maple 并不作化简的工作 (主要的原因 是, 直接化简有可能犯错误), 你必须用 simplify 命令强迫它化简. 但是由于 25 是整数, 因此 Maple 也不会自动计算 25^(1/6) 的值, 你需要用 evalf 命令来求出它的浮点值.convert 命令 是一个用途广泛的函数, 它主要用来在 Maple 的不同数据结构之间进行转换, 在上面的例子中, 我们用 convert 把一个整数表达式转换为浮点数. 在上面的计算过程中, 出现了 %, 它的含义是上一次计算的结果. 在不同的 Maple 版本中, 代表上一次运算结果的符号是不同的. 在 Maple V Release 4 以前的版本中是用 " 来代表上一 次的运算结果, 而在 Maple V Release 5 以后的版本中, 都是用 % 来表示上一次计算的结果. 如果你输入的数据包含一个小数点, 那么 Maple 的解释器就认为这个数是浮点数, 上述的 计算就可以直接进行. 在这种情况下,Maple 会自动的进行整数类型到浮点数类型的转换. 例如: > 25.0^(1/6); 1.709975947 > %^6; 25.00000003 > 100045*0.15; 15006.75 浮点算术的位数由 Maple 变量 Digits 控制, 省缺情况下,Digits 的值是 10. 从前面的计算 可以看出浮点数在小数点后的位数不超过 10. 改变 Digits 的值, 就可以得到不同精度的浮点 值.Maple 在进行浮点数计算时经常使用的函数是 evalf, 它的作用是计算一个表达式的浮点值. 例如: > evalf(sqrt(2)); 1.414213562 2.2 无理数和浮点数 > 27 Digits; 10 > Digits:=20: evalf(sqrt(2)); 1.4142135623730950488 > evalf(Pi,150); 3.1415926535897932384626433832795028841971693993751058209749445923\ 07816406286208998628034825342117067982148086513282306647093844609\ 55058223172535940813 evalf过程用第二个参数来指定浮点数的精度, 如果没有第二个参数, 浮点数的位数由 Digits 决定. Maple 知道许多数学常数, 例如圆周率 π 等. 它们存储在序列 constants 中. 当然, 你也可 以定义自己的符号常数, 定义的方法就是附加在 constants 之后. 例如 > constants; false , γ, ∞, true , Catalan , FAIL, π > constants:=constants,A,B,C; constants := false , γ, ∞, true , Catalan , FAIL, π, A, B, C 在上面的常量中,f alse, true, F AIL 是布尔常量, 常量 γ 是欧拉常数, 定义是: n γ = lim (−1)n . (2n + 1)2 n=0 ∞ n→∞ k=1 1 − ln n k Catalan 数的定义是: 除了基本的数学常量以外,Maple 也提供了大量的常用数学函数, 例如: 指数函数、对数函 数、三角函数、反三角函数、双曲函数、反双曲函数等. 使用 ?inifcns 命令可以得到一个完整 的数学函数列表. 对于常见的数学函数, 我们不再进行说明, 对于部分特殊的数学函数, 我们给 出它们的定义及说明: binomial(n,m) 如果 0 ≤ m ≤ n, 则二项式系数 m= n n! , 更一般的定义由 Γ m!(n−m)! GAMMA(z) GAMMA(z,a) Psi(z) Psi(n,z) Beta(x,y) Zeta(x) Zeta(n,x) Γ(n+1) 函数给出:binomial(n, m) = Γ(m+1)Γ(n−m+1) ∞ Γ 函数, 定义为:Γ(z ) = 0 e−t tz−1 dt. ∞ 不完备的 Γ 函数, 定义为:Γ(z, a) = 0 e−t ta−1 dt. d Γ(x) 二次 Γ 函数, 定义为:Ψ(x) = dx . Γ(x) n 次 Γ 函数 (也就是二次 Γ 函数的 n 次导数), 定义为:Ψ(n, x) = dn dxn Ψ(x). β 函数, 定义为:β (x, y ) = Γ(x)Γ(y) Γ(x+y ) 黎曼 ζ 函数和它的 n 次导数, 定义为:ζ (x) = ∞ 1/ix , ζ (n, x) = i=1 dn ζ (x) n. dx 28 BesselJ(n,z) BesselI(n,z) BesselY(n,z) BesselK(n,z) LegendreF(x,k) LegendreE(x,k) LegendreKc(k) LegendreEc(k) Si(z),Ci(z) Ei(z),Li(z) FresnelS(z) FresnelC(z) with(orthopoly) P(n,x) erf(x) 第二章 数值计算 BesselJ 是第一类贝塞尔函数;BesselI 是改进的第一类贝塞尔函 数;BesselY 是第二类贝塞尔函数 (Weber 函数);BesselK 是改进的 第二类贝塞尔函数 (Macdonald 函数). 和 LegendreE 分 别 表 示 第 一 和 第 二 类 椭 圆 积 分;LegendreKc 和 LegendreEc 分别表示完备的第一和第二类 椭圆积分. LegendreF Si(z) 是正弦积分: z cos(t)−1 dt; t 0 是余弦积分:γ + ln(Iz ) − Iπ + 2 t z Ei(z) 是指数积分 −∞ et dt;Li(z) 是对数积分 z 0 z sin(t) t dt;Ci(z) 0 Ei(ln(z )). Fresnel 的正弦积分函数和余弦积分函数, 分别定义为: sin 和 cos dt. 关于变量 x 的 n 阶拉格朗日多项式. 误差函数, 定义为:erf(x) = 2 √ π x −t2 e dt. 0 z 0 πt 2 2 π t2 2 dt 所有这些特殊函数可以直接使用, 或者从 orthopoly, numtheory, combinat, stats 等程 序包中调用. 对于某些函数,Maple 知道它们在某些特殊点的准确值, 例如对于三角函数,Maple π 能求出在 π , π , π , 10 等点的准确值. 对于黎曼 ζ 函数,Maple 可以直接求出在小于 50 的偶数点 248 的值. 当偶数大于等于 50 时, Maple 不直接给出值, 你需要用 expand 展开. 对于奇数点, 则需 要使用 evalf 来求出它的值. > sin(Pi/10),cos(Pi/10); 1√ 1 1√ 5− , 2 4 44 5+ √ 5 > Zeta(10); 1 π 10 93555 > Zeta(50); ζ (50) > expand(%); 39604576419286371856998202 π 50 285258771457546764463363635252374414183254365234375 > Zeta(3); ζ (3) > evalf(%); 1.202056903 2.3 代数数 29 下面的例子更准确的说明了符号计算与浮点计算的差别. > sin(4)-2*sin(2)*cos(2); sin(4) − 2 sin(2) cos(2) > combine(%,’trig’); 0 > evalf(%%); −.1 10−9 > (1+sqrt(2))^2-2*(1+sqrt(2))-1; √ √ ( 2 + 1)2 − 3 − 2 2 simplify(%); 0 > > evalf(%%); −.1 10−8 2.3 代数数 在前面我们已经看到了根式计算的例子, 例如整数的平方根和三次方根. 在计算根式的过 程中,Maple 一般不直接进行计算, 而是等待用户发出化简或展开的命令. 这是为了保证计算的 准确性. 例如 > (1/2+1/2*sqrt(5))^2; 1 1√ 2 (+ 5) 22 3 1√ + 5 22 > expand(%); > (-8)^(1/3); (−8)1/3 > simplify(%); 1+I √ 3 > %^3; (1 + I √ 3)3 > expand(%); −8 30 1 第二章 数值计算 在上面的计算中, 当我们求 (−8) 3 时, 并没有得到我们所期待的结果, 这与 Maple 处理有理根 式的方式有关. 对于有理根式, 也称为代数数, 一般可以看成是一个有理数域上的不可约多项式的根. 例如 √ √ √ √ √ 2 是多项式 x2 − 2 的根, 2 + 3 + 5 是多项式 x8 − 40x6 + 353x4 − 960x2 + 576 的根, 而 3 −8 则是 x3 + 8 的根. 有些不可约多项式的根不能写成根式的形式, 例如 x5 + x + 1 的根 α, 但是 Maple 同样可以对 α 进行有关的计算. 当然对代数数进行计算是比较费时的,Maple 对代数数的计算有内建的方法. 每个代数数都 √ 可用 RootOf 过程表示, 例如 2 作为代数数就表示为: > alpha:=RootOf(z^2-2,z); α := RootOf( Z 2 − 2) > alpha^2; RootOf( Z 2 − 2)2 > simplify(%); 2 > simplify(1/(1+alpha)); RootOf( Z 2 − 2) − 1 这里 Maple 用以下划线开始的内部变量名 Z 来表示. 而 simplify 命令则用 α2 = 2 这一公式 来化简一切包含 α 的表达式. 为了使计算看起来更清楚, 我们可以使用 alias. > > alias(beta=RootOf(z^2-2,z)): 1/(beta+1)+1/(beta-1); 1 1 + 1+β β−1 > simplify(%); 2β 你也可以用 convert 命令把根式形式与 RootOf 的形式互相转变. > convert((-8)^(1/3),‘RootOf‘); RootOf( Z 3 + 8) > convert(%,‘radical‘); (−8)1/3 实际上 α, β 可以是 x2 − 2 的任何一个根, 使用 allvalues 命令可以显示它的所有根. > allvalues(alpha); √ √ 2, − 2 2.4 复数 31 2.4 复数 √ 与代数数不同, 复数是一个基本的数据类型. 复数 i ( −1) 在 Maple 中表示为 I. 关于复数 的算术计算是自动进行的. 例如 > complex_num:=(2+3*I)+(4-5*I)*(1-I); complex num := 1 − 6 I > Re(%); 1 > Im(%%); −6 > conjugate(%%%); 1 + 6I > argument(complex_num); −arctan(6) > 1/complex_num; 1 6 + I 37 37 在 Maple 中许多数学函数都可以识别复数, 并按照复数的规则进行计算. > cos(I),ln(I),arccoth(0); cosh(1), 1 1 I π, I π 2 2 > GAMMA(1+2*I); Γ(1 + 2 I ) > evalf(%); .1519040027 + .01980488016 I 32 > > 第二章 数值计算 plot3d(abs(GAMMA(x+y*I)),x=-Pi..Pi,y=-Pi..Pi, view=0..5,grid=[30,30], orientation=[-120,45],axes=framed,style=patch contour); 5 4 3 2 1 0 3 2 1 y0 -1 -2 -3 -3 -2 -1 0 x 1 2 3 在上述 plot3d 命令中, 我们指定了许多的选项以生成上述图形, 实际上, 这些选项都可以通过 窗口环境的设置来完成. 如果要对复数进行符号计算, 我们经常要使用 evalc 命令.evalc 命令假设在表达式中的变 量都是实数并将复数用标准的 a + bi 的形式表示. > 1/(3+a-b*I); 1 3+a−Ib > evalc(%); 3+a Ib + (3 + a)2 + b2 (3 + a)2 + b2 > abs(%%); 1 |3 + a − I b| > evalc(%); √ 1 9 + 6 a + a2 + b2 √ > sqrt(a+b*I); a+Ib > evalc(%); 1 2 2 a2 + b2 + 2 a + 1 I csgn(b − I a) 2 2 a2 + b2 − 2 a 2.4 复数 33 这里复符号函数 csgn 定义如下: 1 如果 (z ) > 0或( (z ) = 0, (z ) > 0); csgn(z ) = 1 如果z = 0; −1 其他情况. 当我们对 a, b 加一些假设以后,evalc 就可以直接工作了. > > assume(a>0):assume(b>0): evalc(sqrt(a+b*I)); 1 2 2 a ˜2 + b ˜2 + 2 a ˜ + 1 I 2 2 a ˜2 + b ˜2 − 2 a ˜ 第三章 3*x^2+a+b; 变量管理 在 Maple 中, 一个变量名可以有值, 也可以没有值. 例如: > 3 x2 + a + b 其中, 变量 x, a, b 就没有任何值. 另一方面,Maple 的任一变量名都可以指给另一个 Maple 对象, 如, > a:=100; a := 100 我们说:“数 100 已经 赋值 给名称 a”. 从此以后,Maple 每当遇到 a 时都把它作为 100 看待. 我 们说:“变量名 a 就指的是数 100. 例如 > a^2*t-2*a-1; 10000 t − 201 现在我们可以说 Maple 已经求出了 a^2*t-2*a-1 的值, 术语 求值(evaluation) 在计算机语言 中有不同的含意. 严格讲,Maple 里的求值是求变量的值 (即通过对名称所指向的内存的搜寻过 程), 并不包含任何计算的意义. 在 Maple 的术语里, 计算 (calculation) 叫做 化简(simplification). 一般地, 化简必须由用户提出要求, 但某些基本的化简是可以自动执行的, 例如像计算 100 的平 方, 合并 −200 与 −1 等. 实际上 Maple 在上例中是通过以下几步计算求出结果的: • 将 a 的值计为 100 • 自动化简(autosimplification) 所得的表达式 • 根据 Maple 内存的内部序对结果表达式的子式进行分类 在这一章中, 我们将讨论变量的赋值、管理和求值等问题. 此外我们还要介绍 Maple 中可 以使用的各种基本数据类型. 3.1 变量的赋值 在前面的计算中,我们经常使用双引号来代表前一次的计算结果, 然而每次都这样作或重 新输入 Maple 表达式毕竟不太方便, 因此 Maple 允许为表达式命名, 其语法如下: name:= expression; 含义是将表达式 expression 赋值给变量 name. 以后就可以用变量名 name 来代替表达式 expression 进行各种运算. 我们把这种赋值也称为给表达式命名. 任何 Maple 表达式均可以被 命名. 例如: > var:=x; var := x > term:=x*y; term := x y 34 3.1 变量的赋值 35 方程也可以命名, > eqs:=x=y+z; eqs := x = y + z Maple 的名字可以由任何字母, 数字和下划线构成, 但不能以数字开头, 应避免以下划线开 头,Maple 中以下划线开头的名字用于内部变量. 合法的 Maple 名字如 polynomial, test data, RoOt 10cUs, pLoT 和 value2, 不合法的 Maple 名字如 2ndphase (以数字开头), x&y (因 & 不是 字母, 数字). 然而, 并非所有名字均可用于自定义变量, Maple 有一些预定义的名字和保留字, 试图给这 些预定义名字或保留字赋值, 将会受到警告. > Pi:=3.14; Error, attempting to assign to ‘Pi‘ which is protected > set:={1,2,3}; Error, attempting to assign to ‘set‘ which is protected 有时用户希望对自己定义的变量名进行保护, 这时可以使用 protect 命令. 对变量进行保 护以后, 就不能再给这个变量赋值, 如果赋值就会出现保护错误. 如果你一定要改变被保护的变 量的值, 可以使用 unprotect 命令取消对变量的保护. 例如 > abc:=12345; abc := 12345 > > protect(’abc’); abc:=32434; Error, attempting to assign to ‘abc‘ which is protected > > unprotect(’abc’); abc:=134452; abc := 134452 可以利用 Maple 的箭头记号 (−>) 定义自己的函数, 使 Maple 知道当这些函数出现在表达 式中时, 如何求它们的值, 下面的例子利用 plot 命令为自定义函数画图. > f:=x->2*x^2-3*x+4; f := x → 2 x2 − 3 x + 4 36 > 第三章 变量管理 plot(f(x),x=-5..5); 70 60 50 40 30 20 10 -4 -2 0 2x 4 赋值操作 (:=) 将函数名与函数定义相关联, 函数名出现在 := 的左侧, 而函数定义 (利用箭 头记号) 出现在右侧, 下列语句将 f 定义为平方函数, > f:=x->x^2; f := x → x2 于是, 给 f 的参数指派一个值将得到该值的平方, > f(5); 25 > f(y+1); (y + 1)2 变量的内部管理 对于所有的变量,Maple 提供了用于内部管理的命令. 你可以用 assigned 命令来检查一个 变量是否已被赋值. 例如: > x:=4; x := 4 > assigned(x); true > assigned(a); false 3.1 变量的赋值 37 当一个变量已经被赋值后,assigned 命令的结果是 ture. 此外也可以用 unassign 命令取消对 某个变量的赋值, 例如在下例中, 我们先给变量 a,b,c 赋值, 然后用 unassign 命令取消对它们 的赋值, 当我们再次求它们的值时, 得到的结果是它们的名字.(一般情况下当一个变量没有被赋 值时, 对它求值得到的结果就是变量名). > > > a:=1:b:=2:c:=3: unassign(’a,b,c’); a,b,c; a, b, c 前面我们提到, 变量可以存储任意的数据类型, 那么我们如何知道一个变量所存储的数据 类型呢? 最简单的方法是用 whattype 命令来了解变量的类型. > > > w:=1:x:=a^2+b^2:y:={a+b=5,a-b=1}: z:=array([[1,2],[3,4]]): whattype(w); integer > whattype(x); + > whattype(y); set > whattype(z); string 不过 whattype 命令只能知道基本的数据类型, 对于它所不了解的数据类型, 它就简单的返回一 个 string 类型, 此时你可以用 type 命令来检查变量是否是某种数据类型. 当然这需要用户对 Maple 所支持的数据类型有所了解. > type(y,set); true > type(z,array),type(z,matrix); true , true 在 Maple 系统中, 对变量的使用比较自由, 不象其它程序语言需要对使用的变量进行说明, 因此, 在计算过程中经常会出现误用已经赋值的变量的情况. 例如: > a:=1/2; a := 1 2 38 > 第三章 变量管理 solve({a*x^2+b*x+c=0},{x}); {x = −b + b2 − 2 c}, {x = −b − b2 − 2 c} 在这个例子中, 第二个命令原意是要求出一元二次方程 ax2 + bx + c = 0 的符号解, 但是由于我 1 们前面给 a 赋了值 2 , 因此第二个命令就变成了求 1 x2 + bx + c = 0 的根. 因此在做某些计算之 2 前, 我们有必要了解一下那些变量已经被赋了值, 那些变量虽然被使用, 但是并没有赋值. 为此 Maple 提供了两个函数, 分别为 anames 和 unames. 当我们在系统中无参数的调用这两个函数 时, 它们会返回当前状态下已经赋值的变量和使用过但是没有被赋值的变量. 3.2 数据类型与结构 Maple 系统提供多种数据类型和结构, 最基本的数据类型有我们前面见过的整数类型 (in- teger), 浮点数类型 (floating-point number) 和字符串 (string). 复杂的数据类型结构则由基本的 数据类型所组成, 主要包括: 表达式序列, 列表, 向量, 表, 和阵列. 表达式序列 Maple 的基本数据结构是表达式序列, 表达式序列是一组用逗号分隔的表达式. > 1,2,3,4; 1, 2, 3, 4 > x,y,z,w; x, y, z, w 表达式序列既不是列表也不是集合. 在 Maple 中, 这些类型代表不同的数据结构, 各有其不同的 性质, 例如表达式序列保持其中的表达式顺序, 允许一个表达式重复出现, 即表达式顺序与键入 顺序一致, 允许重复键入相同的表达式. 利用连接操作构造更复杂的对象时, 常常用到表达式序 列. 表达式序列扩展了许多 Maple 基本操作的功能, 例如串连接是构造名字的基本操作, 在 Maple V 中, 连接操作符是 “.”. 在 Maple 6 以后的版本中连接操作符改为 “||”. 连接操作符的 用法如下: > a||b; ab 如果连接操作用于表达式序列, 则连接作用于序列的每一元素, 例如, 若 S 为一序列, 则连接 a 和 S, 可以把 a 缀于 S 的每一元素之前, 构成新的名字序列. > S:=1,2,3,4; S := 1, 2, 3, 4 > a||S; a1, a2, a3, a4 3.2 数据类型与结构 39 列表 把若干以逗号分隔的 Maple 对象用一对方括号括起来, 就构成一个列表, > data_list:=[1,2,3,4,5]; data list := [1, 2, 3, 4, 5] > polynormials:=[x^2+3,x^2+3*x-1,2*x]; polynormials := [x2 + 3, x2 + 3 x − 1, 2 x] > participants:=[Kathy, Frank, Rene, Niklaus, Liz]; participants := [Kathy , Frank , Rene , Niklaus , Liz ] 换句话说, 列表是将表达式序列用方括号括起来. Maple 保持列表中表达式顺序和重复次数, 因此 [a, b, c], [b, c, a], [a, a, b, c, b] 是彼此不同 的列表. > [a,b,c],[b,c,a],[a,a,b,c,b]; [a, b, c], [b, c, a], [a, a, b, c, b] 由于保持顺序, 用户可以抽取列表中的指定元素, > letters:=[a,b,c]; letters := [a, b, c] > letters[2]; b 使用 nops 命令可以求出列表中元素个数, > nops(letters); 3 使用 op 命令可以把一个列表转换为一个表达式序列, > op(letters); a, b, c 这两条命令还可以用来把表达式拆分成若干部分, 并从中抽取子表达式. 将 nops 作用于表达式 时, 可以告诉我们表达式有多少部分. 例如 > nops(x^2); 2 > nops(x+2); 2 40 op命令允许用户访问表达式的各个部分, 并按顺序返回部分表达式. > 第三章 变量管理 op(x^2); x, 2 也可以指定序号或范围, 以抽取表达式的指定部分. > op(1,x^2); x > op(2,x^2); 2 > op(1..2,x+y+z+w); x, y 集合 Maple 支持数学意义上的集合, 如同序列和列表一样, 集合是用一对花括号括起的以逗号 分隔的一组 Maple 对象. > data_set:={1,-1,0,10,2}; data set := {0, −1, 1, 2, 10} > unkown:={x,y,z}; unkown := {x, z, y } Maple 不保持集合元素的顺序和重复次数, 因此 Maple 的集合与数学的集合具有相同的性质, 下面的三个集合是相同的, > {a,b,c},{c,b,a},{a,a,b,c,a}; {a, c, b}, {a, c, b}, {a, c, b} 注意在 Maple 中, 整数 2 不同于浮点近似值 2.0, 因此下面的集合有三个元素, 而不是两个元素. > {1,2,2.0}; {1, 2, 2.0} 集合的性质使之成为 Maple 中十分有用的概念, 正如数学中的集合概念一样, Maple 提供多种 集合运算. 包括通常的并和交, 操作符为 union 和 intersect. > {a,b,c}union{c,d,e}; {a, c, d, b, e} > {1,2,3,a,b,c}intersect{0,1,y,a}; {1, a} 3.2 数据类型与结构 41 与列表情形一样, 命令 nops 也求出集合中元素的个数, > nops(%); 2 另一个表达式处理命令 op, 同样可以将集合转化为表达式序列, > op({1,2,3,a,b}); 1, 2, 3, a, b 集合和列表上的运算 命令 member 可以判定一个表达式是否是集合或列表的成员, > participants:=[Kate,Tom,Steve]; participants := [Kate , Tom , Steve ] > member(Tom,participants); true > data_set:={5,6,3,7}; data set := {3, 5, 6, 7} > member(2,data_set); false 从列表中选择一项可使用下标记号 [n], 其中 n 表示所求元素在列表中的位置. > participants:=[Kate,Tom,Steve]; participants := [Kate , Tom , Steve ] > participants[2]; Tom Maple 理解空集和空列表, 即没有元素的集合和列表. > empty_set:={}; empty set := {} > empty_list:=; empty list := 通过集合运算可以从已有集合出发构造新集合, 例如用 union 命令得到两个集合的并, 用 minus 命令从集合中去除某些元素等. > old_set:={2,3,4} union {}; old set := {2, 3, 4} 42 > 第三章 变量管理 new_set:=old_set union {2,5}; new set := {2, 3, 4, 5} > third_set:=old_set minus {2,5}; third set := {3, 4} 阵列 阵列是列表数据结构的扩展, 列表是由若干项构成的组, 每个项对应一个正整数作为它的 指标, 表示列表中项的位置.Maple 中阵列数据结构是这种思想的推广, 每一元素仍关联于一个 指标, 但不再限于一维, 此外允许指标为任意整数, 还可以修改个别元素,而不必重新定义整个 阵列. 阵列需要声明, 以通知 Maple 阵列的维数. > squares:=array(1..3); squares := array(1..3, ) 为阵列元素赋值 > squares[1]:=1;squares[2]:=2^2;squares[3]:=3^2 ; squares 1 := 1 squares 2 := 4 squares 3 := 9 也可以在定义阵列的同时为元素赋值, > cubes:=array(1..3,[1,8,27]); cubes := [1, 8, 27] 与列表情形相同, 可以选取阵列的单个元素, > squares[2]; 4 阵列必须事先声明, 要看到阵列的内容, 须使用类似于 print 的命令. > squares; squares > print(squares); [1, 4, 9] 使用 print 命令, 起初可能使用户感到麻烦, 然而这一特点不仅使 Maple 能更加有效地工作, 而 且在处理较大规模的阵列时, 用户就可以发现这种简捷安排的好处. 前述例子中的阵列都是一维的, 一般情形, 阵列维数可以大于 1 维, 定义一个 3 × 3 阵列. > pwrs:=array(1..3,1..3); pwrs := array(1..3, 1..3, ) 3.2 数据类型与结构 43 此阵列是二维的 (有两个指标集). 下面我们首先为阵列的第一行元素赋值. > pwrs[1,1]:=1;pwrs[1,2]:=1;pwrs[1,3]:=1; pwrs 1, 1 := 1 pwrs 1, 2 := 1 pwrs 1, 3 := 1 然后再为阵列的其他元素赋值, 可以用 (:) 代替 (;) 结束命令, 以减少大量不必要的输出. > > > pwrs[2,1]:=2:pwrs[2,2]:=4:pwrs[2,3]:=8: pwrs[3,1]:=3:pwrs[3,2]:=9:pwrs[3,3]:=27: print(pwrs); 1 1 1 2 4 9 3 9 27 可以选取阵列中指定行列的元素. > pwrs[2,3]; 9 与一维情形类似, 可以在定义二维阵列的同时为元素赋值. 此时, 应在列表中使用列表, 即构造 一个列表, 其每一元素为包含阵列一行元素的列表, 定义阵列 pwrs 的方式如下: > pwrs2:=array(1..3,1..3,[[1,1,1],[2,4,8],[3,9, 27]]); 1 1 1 pwrs2 := 2 4 8 3 9 27 阵列不限于二维, 但高维阵列的显示比较困难, 对于高维阵列也可以在定义阵列的同时声明所 有元素. > > array3:=array(1..2,1..2,1..2, [[[1,2],[3,4]],[[5,6],[7,8]]]); array3 := array(1..2, 1..2, 1..2, [ (1, 1, 1) = 1 (1, 1, 2) = 2 (1, 2, 1) = 3 (1, 2, 2) = 4 (2, 1, 1) = 5 (2, 1, 2) = 6 (2, 2, 1) = 7 44 (2, 2, 2) = 8 ]) 第三章 变量管理 表 表是阵列数据结构的扩展, 两者的区别在于表的指标任意, 不仅限于整数. > translate:=table([one=un,two=deux,three=trois ]); translate := table([ two = deux three = trois one = un ]) > translate[two]; deux 表似乎只是在阵列的基础上前进了一小步, 但事实上表是非常强有力的结构, 表允许对数 据结构使用自然的记号, 因此可以表达更复杂的对象. 例如可以使用 Maple 的表来描述地球的 物理特征. > > > earth_data:=table([mass=[5.976*10^24,kg], radius=[6.378164*10^6,m], circumference=[4.00752*10^7,m]]); earth data := table([ circumference = [.4007520000 108 , m] radius = [.6378164000 107 , m] mass = [.5976000000 1025 , kg ] ]) > earth_data[mass]; [.5976000000 1025 , kg ] 本例中, 每一个指标是一个名字, 而每一个元素是一个列表, 这还是相当简单的情形. 在更一般 的情况下, 指标常常是很有用的, 例如我们可以构造一个表, 它以代数公式为指标, 而对应的元 素为其导数. 3.3 求值 一般情况下当 Maple 遇到一个变量时, 它总是搜索这个变量所指向的对象. 我们把这个过 程称为 Maple 对变量求值. 如果你给变量名 x 指定的值为 y, 给 y 指定的值为 z, 给 z 指定的值 为 5, 那么 x 应该是什么值呢? 3.3 求值 45 求值的层次 在大多数情形, Maple 对名字作完全的求值. 也就是说, 当你使用一个名字或符号时,Maple 就检查这个名字或符号是否被指定了什么值. 如果它有一个值, Maple 就把这个名字代换成那个 值. 如果那个值本身有一个指定的值,Maple 就再一次执行代换, 如此递归下去直至不再有可能 的代换. > x:=y; x := y > y:=z; y := z > z:=5; z := 5 现在 Maple 对 x 完全地求值. 也就是,Maple 把 x 代换成 y, 把 y 代换成 z, 最后, 把 z 代换成 5. > x; 5 你可以使用 eval 命令来控制一个表达式的求值层次. 如果你调入 eval 时只有一个参数, 那么 eval 对那个参数进行完全求值. > eval(x); 5 eval的第二个的参数指明你想要对第一个参数作多少层的求值. > eval(x,1); y > eval(x,2); z > eval(x,3); 5 完全求值规则的主要例外是诸如表, 阵列和过程之类的特殊数据结构以及在一个过程内的 局部变量的求值. 最后名字的求值 数据结构 array, table 和 proc 有一个称作最后名字求值的特殊求值方式. > x:=y; x := y 46 > 第三章 变量管理 y:=z; y := z z:=array([[1,2],[3,4]]); > 1 2 z := 34 Maple 把 x 代换成 y 和把 y 代换成 z, 然后打住, 这是由于最后的名称 z 将产生一种特殊的结 构: 阵列. > x; z Maple 之所以对阵列, 表, 过程使用最后名字求值是因为这些结构常常是非常大的, 而按照 缺省的方式进行完全求值会在 Maple 对话区中产生多余的输出结果. 不过你可以通过明确调用 eval 命令来进行强迫的完全求值. > eval(x); 1 2 34 > add2:=proc(x,y) x+y; end; add2 := proc(x, y ) x + y end add2; add2 > 你以轻而易举的使用 eval 或 print 来强迫完全求值. > eval(add2); proc(x, y ) x + y end 注意在缺省状态下, 对 Maple 的内部程序进行完全求值是受到抑止的. 为说明这一点, 从 软件包 finance 中装入 effectiverate 命令. > > with(finance,effectiverate): effectiverate; effectiverate 3.3 求值 > 47 eval(effectiverate); proc(Rate , N ) description‘effective rate when stated Rate is calculated ‘ ‘N times in a period , N can be infinity ‘ end 此时我们只能看到对这个程序的简单描述, 得不到更多的信息. 但是当我们把 interface 变量 verboseproc 设置为 2 后, 再试一下刚才的命令, 就会得到有趣的结果. > > interface(verboseproc=2); eval(effectiverate); proc(Rate , N ) option‘Copyright (c ) 1994 by Je ro ˆme M . Lang . All rights reserved .‘; description‘effective rate when stated Rate is ‘ ‘calculated N times in a period , N can be infinity ‘ if N = ∞ then exp(Rate ) − 1 else (1 + Rate /N )N − 1 fi end 我们现在所看到的是 effectiverate 这个命令的 Maple 语言的源程序. verboseproc 的缺省值为 1. > interface(verboseproc=1); ?interface的帮助页提供了 verboseproc 的可能设置和其它 interface 变量的解释. 在下 一节 Maple 的内部变量中, 我们将讨论 interface 变量的作用. 此外在一个过程中, 我们可以使用局部变量,Maple 对局部变量使用一层求值. 也就是说, 如 果你给一个局部变量赋值, 那么求值的结果就是最近一次直接赋给这个变量的值. 关于局部变 量的求值问题, 我们将在第 10 章 Maple 编程中讨论. 具有特殊求值规则的命令 assigned和 evaln 命令: 函数 assigned 和 evaln 对其参数求值仅到它们的名字之处为止. > x:=y; x := y > y:=z; y := z > evaln(x); x 48 assigned命令检查一个名字是否指定了一个值. > 第三章 变量管理 assigned(x); true seq命令: 用于产生表达式序列的 seq 命令对其参数不求值, 以至于即使一个变量具有一 个指定的值,seq 仍能够把它用作计数变量. 事实上, 对于 seq 的计数变量,Maple 是作为局部变 量来处理的, 它不同于在 Maple 会话区中已经赋值的同名变量. > i:=2; i := 2 > seq(i^2,i=1..5); 1, 4, 9, 16, 25 > i; 2 与此形成对比的是 sum 的行为. > sum(i^2,i=1..5); Error, (in sum) summation variable previously assigned, second argument evaluates to, 2 = 1 .. 5 你可以使用 向前单引号 来解决这个问题. 延迟求值和求值消去 Maple 语言使用 向前单引号 以延迟一个层次的求值. 用向前单引号 (’) 括起的一个名字 阻止 Maple 对其求值. 它能禁止 Maple 再去查看内存中这些向前引号间名称所代表的值; 而 Maple 所做的只不过是剥去这对向前引号. > i:=4; i := 4 > i; 4 > ’i’; i 用这一方法可以避免上面关于 sum 的问题. > i; 4 3.3 求值 > 49 sum(i^2,i=1..5); Error, (in sum) summation variable previously assigned, second argument evaluates to, 4 = 1 .. 5 > sum(’i^2’,’i’=1..5); 55 > i; 4 对一个带单引号的表达式进行完全求值可以剥去一层引号. > x:=0; x := 0 > ’’’x’+1’’; ’’x’ + 1’ > %; ’x’ + 1 > %; x+1 > %; 1 对一个表达式加单引号可以延迟求值, 但是不阻止表达式自动的化简和算术运算. > ’1-1’; 0 > ’p+q-i-p+3*q’; 4q − i 如果对一个简单的变量附加单引号, 结果是这个变量的名字. 你可以使用这一方法来消去 对某个变量的指定. > x:=1; x := 1 > x; 1 50 > 第三章 变量管理 x:=’x’; x := x > x; x 然而, 要消去一个变量的值, 最好是使用 evaln. 例如, 在下例中, 我们要消去对 a4 的值 > i:=4; i := 4 > a[i]:=9; a4 := 9 此时就不能使用 ’a[i]’, 这是因为 ’a[i]’ 现在是 a[i] 而不是 a[4]. > ’a[i]’; ai 使用 evaln 命令可以消去对 a[i] 的指定. > evaln(a[i]); a4 > a[i]:=evaln(a[i]); a4 := a4 使用单引号变量作为数参数 一些 Maple 命令使用名字作为一种除了标准的返回值以外的返回信息. 例如 > divide(x^2-1,x-1,’q’); true 在 divide 命令中, 把商指定为全局名字,q. > q; x+1 使用一个带单引号的名字是为了确保你不会把已经被赋了值变量引入这个过程. 如果能确保所 使用的名字先前没有被赋值, 你也可以不必使用单引号. > q:=2; q := 2 > divide(x^2-y^2,x-y,q); Error, wrong number (or type) of parameters in function divide 3.3 求值 > 51 q:=evaln(q); q := q > divide(x^2-y^2,x-y,q); true > q; x+y rem, quo, irem和 iquo 命令有相似的特征. 名字的并置 并置是一种用其它变量名来生成新变量名的方法. > a||b; ab 在一个名字中的并置算子 “||” 导致对算子右边而不是左边的求值. > a:=x; a := x > b:=2; b := 2 > a||b; a2 > c:=3; c := 3 > a||b||c; a23 在并置运算中, 如果对一个名字求值的结果不是单一的符号, Maple 就不会对这个并置求 值. 例如 > a:=x; a := x > b:=y+1; b := y + 1 > new_name:=a||b; new name := a||(y + 1) 52 > 第三章 变量管理 y:=3; y := 3 > new_name; a4 你可以使用并置名字来创造表达式. > i:=1; i := 1 > a||i:=0; a1 := 0 这里需要单引号. > sum(’a||k’*x^k,k=0..8); a0 + a2 x2 + a3 x3 + a4 x4 + a5 x5 + a6 x6 + a7 x7 + a8 x8 如果丢掉单引号, Maple 对 a||k 求值的结果为 ak. > sum(a||k*x^k,k=0..8); ak + ak x + ak x2 + ak x3 + ak x4 + ak x5 + ak x6 + ak x7 + ak x8 你还可以使用并置来作图的标题. 3.4 系统变量 Maple 有许多系统变量在不同的情况下起作用. 系统变量大致可分为两类: 环境变量 (en- vironment) 和界面变量 (interface). 环境变量有 Digits、 Order、 Normalizer、 Testzero、 mod 和 printlevel 等. 此外前面用过 的 %, %% 和 %%% 也是环境变量.Maple 认为凡是以 Env 开头的变量都是环境变量. 环境变量有其省缺值, 用户可以用赋值语句改变环境变量的值, 以得到用户所需要的结果. 例如在作浮点计算是,Maple 在省缺情况下输出的浮点值精度只有 10 位, 改变 Digits 的 值, 就可以得到更高精度的输出. 例如 > evalf(1/3); .3333333333 > Digits:=20; Digits := 20 > evalf(1/3); .33333333333333333333 3.4 系统变量 53 环境变量 Order 是用来控制级数展开的次数的, 其省缺值为 6, 改变它的值就可以得到更 高阶的级数展开, 例如: > series(sin(x)/(x+1),x); x − x2 + 5 3 5 4 101 5 x− x+ x + O(x6 ) 6 6 120 > Order:=10; Order := 10 > series(sin(x)/(x+1),x); 5 5 101 5 101 6 4241 7 4241 8 305353 9 x − x2 + x3 − x4 + x− x+ x− x+ x + O(x10 ) 6 6 120 120 5040 5040 362880 环境变量 mod 涉及整数的模运算, 它的初始值为 modp, 也可以把它设置为 mods, 有关它的 用法在第二章有相关说明, 这里不再详细讨论. 环境变量 Normalizer 和 Testzero 都与级数的展开有关, 它们的用法我们将在第八章级数 中说明. 环境变量 printlevel 控制 Maple 的显示层次, 它的省缺值是 1, 如果你把它的值设为负 整数, 则 Maple 对你输入的任何命令, 都不显示计算结果. 另一方面,printlevel 的值设置的越 大,Maple 显示的信息就越多, 当我们对程序进行调试时, 甚至可以把 printlevel 的值设置为 1000. interface 变量控制着 Maple 核心与它的用户界面的连接, 通过改变 interface 变量的值, 我们可以控制 Maple 核心提供给用户界面的信息量.interface 的变量有: ansi echo errorbreak indentamount labelling labelwidth patchlevel plotdevice plotoptions plotoutput postplot preplot prettyprint prompt quiet screenheight screenwidth showassumed terminal verboseproc version warnlevel 设置 interface 变量的方法是:interface(var=value). 常用的 interface 变量有 verboseproc, showassumed, plotdevice, plotoutput 等. 在前面的例子中, 我们已经看到, 当把 verboseproc 的值设置为 2 时, 使用 eval 或 print 命令可以显示出 Maple 程序的原代码. plotdevice 和 plotoutput 是用来控制图形输出的, 具体用法是: > > > interface(plotdevice=pcx); interface(plotoutput=‘myfile.pcx‘); plot(cos(x),x=0..2*Pi); 第一个命令的含义是将图形输出格式设置为 pcx 图形,Maple 所支持的图形格式还有很多, 具体可以参见 plot[device] 的帮助. 第二个命令的含义是将产生的图形输出到文件 myfile.pcx, 通常这个文件所在的目录是 Maple 的工作目录, 一般为 C:\MAPLEV4\EXAMPLES. 此时画图命令 所产生的图形将不在窗口上显示, 而直接输出到文件中. 如果你要画许多图形, 就需要不断改变 plotoutput 的值, 以避免新的图形覆盖就的图形. 54 第三章 变量管理 showassumed 的作用是显示具有假设特征的变量, 通常当一个变量具有假设特征时,Maple 在变量名后面加一个 ∼ 来表示. 如果我们把 showassumed 的值设置为 2, Maple 就会去掉变量 名后面的 ∼, 而用一句话来说明变量具有假设. 例如 > > > interface(showassumed=2); assume(n,odd); sin(n*x); sin(n x) with assumptions on n 3.5 变量的假设特征 当你对一个变量没有赋值时,Maple 对这个变量的特征一无所知. 但是在你处理数学问题 时, 实际上对问题中的某些符号有内在的假设. 比如说考察一个一元二次方程 ax2 + bx + c = 0, 对于系数 a, b, c 你通常有自己的假设, 如 a = 0, a, b, c 都是实数等. 当然这些假设都不是必需的. 不过对于 Maple 而言, 如果一个符号没有被赋值,Maple 认为它就是一个普通的符号, 没有任何 内在的假设存在. 因此在处理一些多值函数或进行表达式化简的过程中就出现了问题. 为了解 决这类问题,Maple 提供了对变量进行假设设置的处理机制.assume 可以帮助 Maple 较好地处理 符号表达式的化简, 特别是多值函数的问题. 例如: 平方根: > sqrt(a^2); √ a2 由于结果对于正的和负的 a 是不同的,Maple 不能化简这个表达式. 给出关于 a 的假设就使 Maple 能够化简这个表达式. > > assume(a>0); sqrt(a^2); a˜ 具有代字号 (˜ ) 的变量表明该变量作了某种假设. 用新的假设代替旧的假设, 会得到新的结果 > > assume(a<0); sqrt(a^2); −a ˜ 用 about 命令可以得到对未知量的假设信息. > about(a); Originally a, renamed a~: is assumed to be: RealRange(-infinity,Open(0)) 3.5 变量的假设特征 55 利用 additionally 命令可以对未知量作进一步的假设. > > > assume(m,nonneg); additionally(m<=0); about(m); Originally m, renamed m~: is assumed to be: 0 许多函数使用对未知量的假设. 例如 frac 命令返回一个数的分数部分. 如果对变量 n 没 有任何假设,Maple 无法知道 n 有没有分数部分, 因此它就直接返回未求值的 frac(n). 当你对 n 加了适当的假设后,Maple 才能得到真正的结果. > frac(n); frac(n) > > assume(n,integer); frac(n); 0 下面的极限与 b 有关. > limit(b*x,x=infinity); signum(b) ∞ > > assume(b>0); limit(b*x,x=infinity); ∞ 使用 infolevel 命令可以得到 Maple 关于一个命令所执行时的细节. > infolevel[int]:=2; infolevel int := 2 > int(exp(c*x),x=0..infinity); Definite integration: Can’t determine if the integral \ is convergent. Need to know the sign of --> -c Will now try indefinite integration and then take limits. int/indef: first-stage indefinite integration int/indef2: second-stage indefinite integration int/indef2: applying derivative-divides int/indef: first-stage indefinite integration 1 e(c x) − x→∞ c c lim 56 int命令需要知道 c 的符号 (或更确切地是 −c 的符号). > > 第三章 变量管理 assume(c>0); int(exp(c*x),x=0..infinity); int/cook/nogo1: Given Integral Int(exp(x),x = 0 .. infinity) Fits into this pattern: Int(exp(-Ucplex*x^S1-U2*x^S2)*x^N*ln(B*x^DL)^M*cos(C1* x^R)/((A0+A1*x^D)^P),x = t1 .. t2) int/cook/IIntd1: --> U must be <= 0 for converging integral --> will use limit to find if integral is +infinity --> or - infinity or undefined ∞ 对数函数 (Logarithms) 是多枝的; 对于一般的复值 x, ln(ex ) 与 x 是不同的. > ln(exp(3*Pi*I)); Iπ 因此,Maple 不化简下式, 除非假定 x 是实的. > ln(exp(x)); ln(ex ) > > assume(x,real); ln(exp(x)); x˜ 你可以用 is 命令检查未知量的性质. > is(c>0); true > is(x,complex); true > is(x,real); true 在下面的例子中,Maple 仍然假设变量 a 是负的. > eq:=xi^2=a; eq := ξ 2 = a ˜ > solve(eq,{xi}); {ξ = I √ −a ˜}, {ξ = −I √ −a ˜} 3.6 宏与别名 57 为了除去你对某个名字的假设, 只要除去对这个名字的指定就行了. 然而, 符号 eq 依旧指 的是 a∼ . > eq; ξ2 = a ˜ 因此, 在除去关于 a 的假设之前, 你必须先除去在 eq 中的对 a 的假设. 首先, 除去在 eq 中的对 a 的假设. > eq:=subs(a=’a’,eq); eq := ξ 2 = a 然后, 除去对 a 的指定. > a:=’a’; a := a 关于假设设置的进一步信息, 参看 assume 的帮助. 3.6 宏与别名 别名 alias 与宏 macro 都可以用来定义缩写, 这两者的主要差别在于宏的缩写仅应用于 Maple 的输入, 对于 Maple 的输出没有影响, 而别名定义的缩写不仅作用于 Maple 的输入, 也作 用于 Maple 的输出. 下面我们通过例子来说明宏与别名的用法及其差别. 宏的用法是:macro(x=expression). 它的含义是用符号 x 代表表达式 expression. 这里 Maple 对于符号 x 和表达式 expression 都不求值. 符号 x 仅仅是代表一个表达式 expression, 而没有别的含义. 例如 > > macro(s=solve); s(x^2-2); √ √ 2, − 2 > > macro(s=sin); s(x^2-2); sin(x2 − 2) 宏也可以用来定义常量, 使用宏定义的常量将受到保护, 任何改变这个常量或删除它的企 图都会失败, 如果你真的要删除这个常量, 还得使用宏来解决. > > macro(s=1.234567); s; 1.234567 > s:=3; Error, Illegal use of an object as a name 58 > > 第三章 变量管理 macro(s=s); #delete the macro for s s:=3; s := 3 此外不仅可以用变量, 也可以用数学表达式来作为宏的名字, 例如 macro(sin(x)=0) 是合 法的. sin(x) 的新含义只当作为 Maple 的输入起作用, 例如 > > macro(sin(x)=0); sin(x)+1; 1 > diff(cos(x),x); −sin(x) sin(x)的宏定义对于 Maple 的输出没有影响. 这也是宏定义的特点. 需要说明的是: 命令 macro(a=b) 和赋值语句 a:=b 在三个方面有区别: 首先宏的缩写仅应 用于 Maple 的输入, 其次 a 可以是一般的数学表达式 (例如 x2 + y 2 ), 第三点是宏定义的名字 a 只能用 macro 命令来删除或改变, 而不能用赋值语句改变它的值. 别名 alias 的用法和宏 macro 的用法类似, 用法是:alias(x=expression), 不过它产生的 输出与 macro 不同, 它是目前已经定义的别名的列表. 例如 > alias(s=sin); I, s > sin(x); s(x) √ 这里 I 是 −1 的别名. 与宏不同的是别名的定义不仅影响输入, 也影响输出, 上面我们输入了 sin(x), 由于定义 了 sin 的别名为 s, 因此它的输出为 s(x). 别名经常用来隐藏一个函数的自变量, 例如我们在求函数 f (x) 的微分时, 经常省略掉变量 x, 不过在 Maple 中这样作就会得到错误的结果. 使用别名可以解决这种问题. 例如 > > alias(f=f(x)): diff(f^2,x); 2f ( ∂ f) ∂x 别名与宏的区别主要表现在以下几点: (1) 别名不仅影响 Maple 的输入, 也影响 Maple 的输出. (2) 在别名定义中的表达式在以缩写方式存储之前将被求值. (3) 不能用别名来定义常量. , simplify. . subs. Maple , Maple , . , 4.1 Maple , , , ( x +3x y + y , ), , . Maple , Maple Maple , . , , , , . . Maple 3 2 2 , , x ,x =x 3 l 3 . 0 x = 1. . x −2x+1 , , an xn + an−1 xn−1 + · · · + a1 x + a0 Maple , > . , , . , ,Maple x^5-4*x^7+3*x^3-12*x+4; x5 − 4 x7 + 3 x3 − 12 x + 4 sort , . , , ,Maple . > sort_poly:=x+x^2-x^3+1-x^4; sort poly := x + x2 − x3 + 1 − x4 > sort(sort_poly); −x4 − x3 + x2 + x + 1 > sort_poly; −x4 − x3 + x2 + x + 1 59 60 , T (x1 , x2 , · · · , xn ) ≤. {x1 , · · · , xn } , t1 · s ≤ t2 · s. , , ) (plex), , . . , , : . T (x1 , x2 , · · · , xn ) (2) , (1) ∀s ∈ T (x1 , x2 , · · · , xn ), 1 ≤ s; , s, t1 , t2 ∈ T (x1 , x2 , · · · , xn ), x . 3 Maple t1 ≤ t 2 , xy , , 2 2 y 3 . (sort > poly1:=x^3*y-y^3*x; poly1 := x3 y − y 3 x > sort(poly1); x3 y − x y 3 > sort(poly1,[y,x]); −y 3 x + y x3 > poly2:=y^3+x^2*y^2+x^3; poly2 := y 3 + x2 y 2 + x3 > sort(poly2,[x,y]); x2 y 2 + x 3 + y 3 > sort(poly2,[x,y],’plex’); x3 + x 2 y 2 + y 3 ,Maple . > > > , ,Maple p1:=x^3-3*x^2+4*x-2: p2:=x^4-x^3+2*x^2-5*x+3: p1+p2; −x2 − x + 1 + x4 ,Maple . , (a + b)x. . , ax bx , , > collect Maple poly3:=x*y+z*x*y+y*x^3-z*y*x^3+x+z*x; poly3 := x y + z x y + y x3 − z y x3 + x + z x 4.1 > 61 collect(poly3,x); (y − z y ) x3 + (y + z y + 1 + z ) x > collect(poly3,z); (x y − y x3 + x) z + x y + y x3 + x , , , . : , . . > poly:=x*y+z*x*y+y*x^2-z*y*x^2+x+z*x; poly := x y + z x y + y x2 − z y x2 + x + z x > collect(poly,[x,y]); (1 − z ) y x2 + ((1 + z ) y + 1 + z ) x . > collect(poly,[x,y],distributed); (1 + z ) x + (1 + z ) x y + (1 − z ) y x2 , , . ,Maple > rem quo r:=rem(x^3+x+1,x^2+x+1,x); r := 2 + x > q:=quo(x^3+x+1,x^2+x+1,x); q := x − 1 > collect((x^2+x+1)*q+r,x); x3 + x + 1 , divide . > divide(x^3-y^3,x-y); true > rem(x^3-y^3,x-y,x); 0 ,Maple . , expand Maple ,Maple > poly:=(x+1)*(x+2); poly := (x + 1) (x + 2) 62 > expand(poly); x2 + 3 x + 2 expand . > expand((x+1)*(y+z)); xy +xz +y +z > expand((x+1)*(y+z),x+1); (x + 1) y + (x + 1) z . > poly:=(x+2)^2*(x-2); poly := (x + 2)2 (x − 2) > expand(poly); x3 + 2 x 2 − 4 x − 8 > " mod 3; x3 + 2 x 2 + 2 x + 1 , Expand . > Expand(poly) mod 3; x3 + 2 x 2 + 2 x + 1 Expand expand expand mod , ,Maple ?expand. . . > expand(sin(2*x)); 2 sin(x) cos(x) > ln(abs(x^2)/(1+abs(x))); ln |x| 1 + |x| 2 > expand("); 2 ln(|x|) − ln(1 + |x|) degree > coeff . poly4:=3*z^4-z^2+2*z-3*z+1; poly4 := 3 z 4 − z 2 − z + 1 4.1 > 63 coeff(poly4,z^2); −1 > degree(poly4); 4 ldegree > ldegree(poly4,x); 0 lcoeff tcoeff , . , . coeffs > lcoeff(poly4); 3 > tcoeff(poly4); 1 > coeffs(poly4,z,‘power‘); 1, −1, 3, −1 > power; 1, z, z 4 , z 2 , Maple V . . , Maple 6 , ,coeff , coeff > poly5:=(x+2)*x^2-x^2*(1+n); poly5 := (x + 2) x2 − x2 (1 + n) > coeff(poly5,x^2); −1 − n > new_poly:=collect(poly5,x); new poly := x3 + (1 − n) x2 > coeff(new_poly,x^2); 1−n 64 Euclid . Euclid Euclid > . Euclid , . . Maple gcd. p3:=-3*x+7*x^2-3*x^3+7*x^4; p3 := −3 x + 7 x2 − 3 x3 + 7 x4 > p4:=5*x^5+3*x^3+x^2-2*x+1; p4 := 5 x5 + 3 x3 + x2 − 2 x + 1 > gcd(p3,p4); x2 + 1 ,factor . > poly5:=expand(p3*p4); poly5 := −17 x6 + 11 x4 − 20 x3 + 13 x2 − 3 x + 56 x7 + 4 x5 − 15 x8 + 35 x9 > factor(poly5); x (7 x − 3) (5 x3 − 2 x + 1) (x2 + 1)2 factor > . factor(x^3+y^3+z^3-3*x*y*z); (y + x + z ) (y 2 − x y − y z − x z + x2 + z 2 ) , . . ; , • > Z[i] factor(poly5,I); (5 x3 − 2 x + 1) (7 x − 3) x (x + I )2 (x − I )2 • > Z2 Factor(poly5)mod 2; x (x + 1)6 (x2 + x + 1) > expand(%)mod 2; x6 + x 4 + x 2 + x + x 8 + x 9 4.2 Z2 , Factor(poly5) poly5. , > 65 factor , poly5 factor Z2 2 poly5 . , ,Factor , 2 Factor. . Z2 , factor(poly5)mod 2; x (x + 1) (x3 + 1) (x2 + 1)2 • Galois x + x + 1. > > 2 GF (4) . GF (4) Z2 , alias(alpha=RootOf(x^2+x+1,x)): Factor(poly5,alpha)mod 2; x (x + α) (x + α + 1) (x + 1)6 • √ Q( 6) > . factor(8*x^3-12*x,sqrt(6)); √ √ 2 (2 x + 6) (2 x − 6) x √ Q( 1 + y ) • > > alias(beta=RootOf(z^2-1-y,z)): x^2+2*beta*x+1+y; x2 + 2 β x + 1 + y > factor(x^2+2*beta*x+1+y,beta); (x + β )2 , Maple . 4.2 f /g , > > g . f:=x^2+3*x+2: g:=x^2+5*x+6: f/g; x2 + 3 x + 2 x2 + 5 x + 6 , numer denom . > numer(%); x2 + 3 x + 2 > denom(%%); x2 + 5 x + 6 66 4.1: content compoly discrim gcd gcdex interp lcm norm prem primpart randpoly recipoly resultant roots sqrfree Euclidean , Maple , > . Maple . ff:=(x-1)*f; ff := (x 1) (x2 + 3 x + 2) > gg:=(x-1)^2*g; gg := (x − 1)2 (x2 + 5 x + 6) > ff/gg; x2 + 3 x + 2 (x − 1) (x2 + 5 x + 6) , , Maple factor, . > factor(f/g); x+1 x+3 > factor(ff/gg); x+1 (x + 3) (x − 1) 4.2 normal : , > 67 , , . x+1 x+3 > . , normal(f/g); normal(ff/gg); x+1 (x + 3) (x − 1) > expr:=x/(x+1)+1/x+1/(x+1); expr := x 1 1 ++ x+1 x x+1 > normal(expr); x+1 x normal expanded. > , . normal , expr:=(x-1/x)/(x-2); 1 x expr := x−2 x− > normal(expr); x2 − 1 x (x − 2) > normal(expr,expanded); x2 − 1 x2 − 2 x normal > , x2 +1 x2 − 1 , e( x ) ] x (x − 2) . normal([expr,exp(x+1/x)]); [ > > big_expr:=sin((x*(x+1)-x)/(x+2))^2 +cos((x^2)/(-x-2)^2); x (x + 1) − x 2 x2 big expr := sin( ) + cos( ) x+2 (−x − 2)2 normal(big_expr); sin( x2 2 x2 ) + cos( ) x+2 (x + 2)2 > 68 normal . normal : • • , . Maple , , Normal(a) mod p. . > , ? (x100 − 1)/(x − 1) . . Normal ,p . . a evala 100 ,Maple • Normal((x^3-2*x^2+2*x+1)/(x^4+1))mod 5; x+3 x2 + 3 evala(Normal((x^2-3)/(x-RootOf(z^2-3,z)))); x + RootOf( Z 2 − 3) expand , . > > expand((x+1)*(x+3)/((x^2+x)*x)); x 4 3 + + x2 + x x2 + x (x2 + x) x ( ) . . rationalize > 1/(2+root[3](2)); 1 2 + 21/3 > rationalize(%); 2 1 1/3 1 2/3 −2+ 2 55 10 > (x^2+5)/(x+x^(5/7)); x2 + 5 x + x5/7 > rationalize(%); (x2 + 5) (x6/7 − x12/7 − x4/7 + x10/7 + x2/7 − x8/7 + x2 ) x3 + x . 4.3 69 4.3 , , , , . , . , simplify > , , , . . .Maple . ,Maple , Maple √ . . expr:=4^(1/2)+3; expr := 4+3 > simplify(expr); 5 > expr:=cos(x)^5+sin(x)^4+2*cos(x)^2-2*sin(x)^2 -cos(2*x); expr := cos(x)5 + sin(x)4 + 2 cos(x)2 − 2 sin(x)2 − cos(2 x) > simplify(expr); cos(x)5 + cos(x)4 , ,Maple ( ) . , . simplify , simplify , ,RootOf > expr:=ln(3*x)+sin(x)^2+cos(x)^2; expr := ln(3 x) + sin(x)2 + cos(x)2 > simplify(expr,trig); ln(3 x) + 1 > simplify(expr,ln); ln(3) + ln(x) + sin(x)2 + cos(x)2 > simplify(expr); ln(3) + ln(x) + 1 Maple , ?simplify. 70 Maple Maple > , , , . , expr:=sqrt((x*y)^2); expr := x2 y 2 > simplify(expr); x2 y 2 assume=property simplify property. > simplify(expr,assume=real); signum(x) x signum(y ) y > simplify(expr,assume=positive); xy assume . ?? . . simplify . > expr:=x*y*z+x*y+x*z+y*z; expr := x y z + x y + x z + y z > simplify(expr,{x*z=1}); xy +y +1+yz . simplify . simplify . > expr:=x^3+y^3; expr := x3 + y 3 > siderel:=x^2+y^2=1; siderel := x2 + y 2 = 1 > simplify(expr,{siderel},[x,y]); y3 + x − x y2 > simplify(expr,{siderel},[y,x]); x3 − y x 2 + y 4.3 ,Maple . , Gr¨bner o ?simplify,siderels. . simplify ,Maple x2 = 1 − y 2 , y2 y = 1−x , . 2 2 71 . x , 2 , . Maple , , > expand. . expand expand(sin(2*x)); 2 sin(x) cos(x) > ln(abs(x^2)/(1+abs(x))); ln |x| 1 + |x| 2 > expand(%); 2 ln(|x|) − ln(1 + |x|) expand . combine, > combine(sin(x)^2+cos(x)^2); 1 > combine(sin(x)*cos(x)); 1 sin(2 x) 2 > combine(exp(x)^2*exp(y)); e(2 x+y) > combine((x^a)^2); x(2 a) simplify , combine . > combine(4*cos(x)^3+x^a*x^b,power); 4 cos(x)3 + x(a+b) > combine(4*cos(x)^3+x^a*x^b,trig); cos(3 x) + 3 cos(x) + xa xb 72 > combine(exp(sin(a)*cos(b))*exp(cos(a)*sin(b)) ,[trig,exp]); esin(a+b) combine . > , , combine(int(f(x),x=a..b)+int(g(x),x=a..b)); b f(x) + g(x) dx a combine . > expand , combine expand expand((sin(5*x)-cos(5*x))^3); 10080 sin(x) cos(x)6 + 420 sin(x)2 cos(x)3 − 7200 cos(x)7 + 1500 cos(x)5 − 4096 cos(x)15 + 17472 sin(x)2 cos(x)7 + 4096 sin(x)3 cos(x)12 + 480 sin(x)3 cos(x)4 − 31680 sin(x) cos(x)8 + 17600 cos(x)9 + 33792 sin(x)2 cos(x)11 + 7680 sin(x)3 cos(x)8 − 39936 sin(x) cos(x)12 + 50688 sin(x) cos(x)10 − 9216 sin(x)3 cos(x)10 + 75 sin(x) cos(x)2 − 125 cos(x)3 − 15 sin(x)2 cos(x) + sin(x)3 − 36 sin(x)3 cos(x)2 + 15360 cos(x)13 > − 2880 sin(x)3 cos(x)6 + 12288 sin(x) cos(x)14 − 1500 sin(x) cos(x)4 − 23040 cos(x)11 − 12288 sin(x)2 cos(x)13 − 35328 sin(x)2 cos(x)9 − 4128 sin(x)2 cos(x)5 combine(%,trig); 3 1 1 3 − cos(5 x) + sin(5 x) + cos(15 x) + sin(15 x) 2 2 2 2 . . > : sin(x). convert convert(sin(x),exp); 1 1 − I (e(I x) − (I x) ) 2 e > convert(cot(x),sincos); cos(x) sin(x) > convert(arccos(x),ln); −I ln(x + I 1 − x2 ) > convert(binomial(n,k),factorial); n! k ! (n − k )! 4.4 parfrac > 73 . convert((x^5+1)/(x^4-x^2),parfrac,x); 1 1 x+ −2 x−1 x convert . > convert(.3284879342,rational); 19615 59713 , > . convert(tan(x),exp); − I ((e(I x) )2 − 1) (e(I x) )2 + 1 > convert(%,trig); − simplify I ((cos(x) + I sin(x))2 − 1) (cos(x) + I sin(x))2 + 1 tan(x). > simplify(%); sin(x) cos(x) convert . , , . 4.4 , . , . . . , Maple , Maple , , . Maple , , , : . > f:=sin(x)+2*cos(x)^2*sin(x)+3; f := sin(x) + 2 cos(x)2 sin(x) + 3 74 Maple , Maple + 3 ∗ sin x cos x “+”, . . , , whattype, type, op, nops. whattype > , . . . sin x 2 2 , ˆ . . , . , . , . whattype(f); + op . > op(f); sin(x), 2 cos(x)2 sin(x), 3 f > . nops(f); nops . 3 op(f) > , term2 := 2 cos(x)2 sin(x) . term2:=op(f)[2]; term2 > . whattype(term2); ∗ > op(term2); 2, cos(x)2 , sin(x) term2 > factor2:=op(term2)[2]; factor2 := cos(x)2 4.4 factor2 > 75 , whattype(factor2); ˆ factor2 > op(factor2); cos(x), 2 > op1:=op(factor2)[1]; op1 := cos(x) > whattype(op1); function , , . > op(op1); x x > whattype(op(op1)); string x ,x , . > op(op(op1)); x > nops(op(op1)); 1 op, nops . whattype , type . , . , , > type(f,‘+‘); true > type(f,‘*‘); false > type(op1,function); true 76 > type(op(3,f),integer); true > type(factor2,‘^‘); true , . , has > has(f,cos); true > has(f,cos(x)^2); true hastype > . hastype(sin(1+sqrt(Pi)),‘+‘); true , . , indets > expr:=(3+x)*x^2*sin(1+sqrt(Pi+3)); expr := (3 + x) x2 sin(1 + √ π + 3) > indets(expr,‘+‘); {3 + x, π + 3, 1 + √ π + 3} , , zip . . map . , > Maple . , , . map, seq, add, mul, select, remove, . map . f [a, b, c] f([a,b,c]); f([a, b, c]) > map(f, [a,b,c]); [f(a), f(b), f(c)] 4.4 > 77 map(expand,{(x+1)*(x+2),x*(x+2)}); {x2 + 2 x, x2 + 3 x + 2} > map(x->x^2,[a,b,c]); [a2 , b2 , c2 ] , map , expand . map , f f [a, b, c] [a, b, c] ,Maple , . . . {(x + 1) ∗ (x + 2), x ∗ (x + 2)} . > map(f,[a,b,c],p,q); [f(a, p, q ), f(b, p, q ), f(c, p, q )] > map(diff,[(x+1)*(x+2),x*(x+2)],x); [2 x + 3, 2 x + 2] map2 map . map . ,map2 > map2(f,p,[a,b,c],q,r); [f(p, a, q, r), f(p, b, q, r), f(p, c, q, r)] map2 . xy y xy ln(x) xy , , − 2] xz z z > map2(diff,x^y/z,[x,y,z]); [ map2 map . > map2(map,{[a,b],[c,d],[e,f]},p,q); {[a(p, q ), b(p, q )], [c(p, q ), d(p, q )], [e(p, q ), f(p, q )]} , map > map(g,x^2); g(x)g(2) g . .seq f . seq > seq(f(i),i={a,b,c}); f(a), f(b), f(c) > seq(f(p,i,q,r),i=[a,b,c]); f(p, a, q, r), f(p, b, q, r), f(p, c, q, r) 78 add > mul seq , , . add(i^2,i=[5,y,sin(x),-5]); 50 + y 2 + sin(x)2 map ,seq, add, mul , ,select large, 4 true. , . select . . > large:=x->is(x>4); large := x → is(4 < x) large > L:=[8,3,2*Pi,sin(4)]; L := [8, 3, 2 π, sin(4)] > select(large,L); [8, 2 π ] ,remove L large > remove(large,L); [3, sin(4)] Maple numeric > type type . . select select(type,L,numeric); [8, 3] . x 1−sin(x) . . > f:=diff(x/(1-sin(x)),x$3); cos(x)2 sin(x) x cos(x)3 x cos(x) sin(x) x cos(x) f := 6 −3 +6 −6 − 3 2 (1 − sin(x)) (1 − sin(x)) (1 − sin(x))4 (1 − sin(x))3 (1 − sin(x))2 , . , . . . . select seq normal f ,Maple > f1:=seq(select(p->has(p,1/(1-sin(x))^n),f),n= 2..4); sin(x) x cos(x) cos(x)2 x cos(x) sin(x) x cos(x)3 − ,6 −6 ,6 2 2 3 3 (1 − sin(x)) (1 − sin(x)) (1 − sin(x)) (1 − sin(x)) (1 − sin(x))4 f1 := −3 4.4 , . > 79 normal , add . add(normal(p),p=[f1]); − 3 sin(x) + x cos(x) cos(x) (−cos(x) + x sin(x)) x cos(x)3 +6 +6 2 3 (−1 + sin(x)) (−1 + sin(x)) (−1 + sin(x))4 > simplify(%-f); 0 . xy. > X:=[seq(ithprime(i),i=1..6)]; X := [2, 3, 5, 7, 11, 13] > Y:=[seq(binomial(6,i),i=1..6)]; Y := [6, 15, 20, 15, 6, 1] yzip x. , , : [ [x1, y 1], [x2, y 2], . . . ]. , , > pair:=(x,y)->[x,y]; pair := (x, y ) → [x, y ] zip > pair X Y. P:=zip(pair,X,Y); P := [[2, 6], [3, 15], [5, 20], [7, 15], [11, 6], [13, 1]] , zip . > zip((x,y)->x.y,[a,b,c,d,e,f],[1,2,3]); [a1, b2, c3] zip . > . , zip zip((x,y)->x.y,[a,b,c,d,e,f],[1,2,3],99); [a1 , b2 , c3 , d99 , e99 , f99 ] > zip(igcd,[7657,342,876],[34,756,213,346,123], 6!); [1, 18, 3, 2, 3] 80 zip , , > ?zip. . . sort . sort([1,3,2,4,5,3,6,3,6]); [1, 2, 3, 3, 3, 4, 5, 6, 6] ,sort . > sort([Mary,has,a,little,lamb]); [Mary , a, has , lamb , little ] , , . ,sort > sort([x,1,a]); [1, a, x] > sort([-5,10,sin(34)]); [−5, 10, sin(34)] π Maple . 2 ] 3 . , . true. , , > sort([4.3,Pi,2/3]); [π, 4.3, > sort([3.12,1,1/2],(x,y)->evalb(x>y)); [3.12, 1, 1 ] 2 . is > π sin(5) bf:=(x,y)->is(x<y); bf := (x, y ) → is(x < y ) > sort([4.3,Pi,2/3,sin(5)],bf); [sin(5), . 2 , π, 4.3] 3 > shorter:=(x,y)->evalb(length(x)<length(y)); shorter := (x, y ) → evalb(length(x) < length(y )) > sort([Mary,has,a,little,lamb],shorter); [a, has , lamb , Mary , little ] 4.5 , Maple . > 81 . , big_list:=[1,d,3,5,2,a,c,b,9]; big list := [1, d, 3, 5, 2, a, c, b, 9] , , . > list1:=select(type,big_list,string); list1 := [d, a, c, b] > list2:=select(type,big_list,numeric); list2 := [1, 3, 5, 2, 9] . > list1:=sort(list1); list1 := [a, b, c, d] > list2:=sort(list2); list2 := [1, 2, 3, 5, 9] . > sorted_list:=[op(list1),op(list2)]; sorted list := [a, b, c, d, 1, 2, 3, 5, 9] 4.5 Maple f (2) > , 2 x. diff . , . f (x) = ln sin(xecos(x) , . , y:=ln(sin(x*exp(cos(x)))); y := ln(sin(x ecos(x) )) > yprime:=diff(y,x); yprime := subs cos(x ecos(x) ) (ecos(x) − x sin(x) ecos(x) ) sin(x ecos(x) ) yprime x . > subs(x=2,yprime); cos(2 ecos(2) ) (ecos(2) − 2 sin(2) ecos(2) ) sin(2 ecos(2) ) evalf > . evalf(%); −.1388047428 82 subs > . . subs(cos(x)=3,yprime); cos(x e3 ) (e3 − x sin(x) e3 ) sin(x e3 ) Maple . > expr:=a*b*c*a^b; expr := a b c ab > subs(a*b=3,expr); a b c ab Maple > ,expr . op(expr); a, b, c, ab a*b expr . , : > subs(a=3/b,expr); 3 3 c ( )b b simplify a*b=3 > simplify(expr,{a*b=3}); 3 c ab a simplify . . 3/b . a b . algsubs > algsubs(a*b=3,expr); 3 c ab subs ,algsubs . , . x , > algsubs(x+y=3,2*x+y+z); −y + 6 + z y, > algsubs(x+y=3,2*x+y+z,[y]); x+3+z 4.5 , , > 83 exact, . algsubs(x+y=3,2*x+y+z,exact); 2x+y +z Maple > subs . expr:=z*sin(x^2)+w; expr := z sin(x2 ) + w > subs(x=sqrt(z),w=Pi,expr); z sin(z ) + π ,subs . > subs(z=x,x=sqrt(z),expr); √ z sin(z ) + w , , subs . > subs({z=x,x=sqrt(z)},expr); x sin(z ) + w > subs({x=sqrt(Pi),z=3},expr); 3 sin(π ) + w ,Maple . > eval(%); w subsop > . expr:=5^x; expr := 5x > op(expr); 5, x > subsop(1=t,expr); tx . > expr:=cos(x); expr := cos(x) 84 > subsop(0=sin,expr); sin(x) . (1 + x ) 5 −1 dx . > g:=int(1/(1+x^5),x); √ √ √ 1 1 1 ln(1 + x) − ln(2 x2 − x − 5 x + 2) − ln(2 x2 − x − 5 x + 2) 5 5 20 20 √ √ 4x −1− 5 4x −1− 5 √ arctan( arctan( √) √)5 √ 1 1 10 − 2 5 10 − 2 5 ln(2 x2 − x + 5 x + 2) + − − √ √ 5 20 10 − 2 5 10 − 2 5 √ √ 4x −1+ 5 4x −1+ 5 √ arctan( arctan( √) √) 5 √ √ 1 1 10 + 2 5 10 + 2 5 2 ln(2 x − x + 5 x + 2) 5 + + + √ √ 20 5 10 + 2 5 10 + 2 5 √ √ 10 + 2 5 10 − 2 5 , √ √ 10 + 2 5 10 − 2 5. B . , a b , 1/a 1/b, Maple a:=sqrt(10-2*sqrt(5)): b:=sqrt(10+2*sqrt(5)): g1:=subs(1/a=1/A,1/b=1/B,g); √ √ √ 1 1 1 ln(1 + x) − ln(2 x2 − x − 5 x + 2) − ln(2 x2 − x − 5 x + 2) 5 5 20 √ 20√ 4x −1− 5 4x −1− 5 √ arctan( ) 1 arctan( )5 √ 1 A A + − − ln(2 x2 − x + 5 x + 2) A 5 A √ 20 √ 4x −1+ 5 4x −1+ 5 √ arctan( ) 1 arctan( )5 √ √ 1 B B + ln(2 x2 − x + 5 x + 2) 5 + + 20 B 5 B . . , collect normal . . . op , . g := A . > > > g1 := , select > part1:=op(1,g1); part1 := 1 ln(1 + x) 5 > part2:=normal(collect(op(2,g1)+op(3,g1),ln)); √ √ 1 part2 := − (1 + 5) ln(2 x2 − x − 5 x + 2) 20 part3:=normal(collect(op(6,g1)+op(7,g1),ln)); √ √ 1 part3 := (−1 + 5) ln(2 x2 − x + 5 x + 2) 20 > 4.5 > 85 part4:=normal(op(4,g1)+op(5,g1)); √ √ 4x−1− 5 ) (−5 + 5) 1 arctan( A part4 := − 5 A √ > part5:=normal(op(8,g1)+op(9,g1)); 4x−1+ 1 arctan( B part5 := 5 B 5 ) (5 + √ 5) , > . g2:=sum(’part.i’,i=1..5); g2 := √ √ 1 1 ln(1 + x) − (1 + 5) ln(2 x2 − x − 5 x + 2) 5 20 √ √ 1 1 arctan( + (−1 + 5) ln(2 x2 − x + 5 x + 2) − 20 5 √ √ 4x−1+ 5 ) (5 + 5) 1 arctan( B + 5 B ,Maple > √ √ 4x−1− 5 ) (−5 + 5) A A , 1 a+bx5 dx. test:=int(1/(a+b*x^5),x); a 1/5 a 1/5 √ 1 ( b ) ln(x + ( b ) ) 1a a test := − ( )1/5 ln(4 %12 + 4 %11 + 2 %10 + 4 ( )1/5 x3 5 5 a 40 b b √ a a a − 2 ( )2/5 x2 5 + 2 %9 + %8 + 2 %7 + 4 x4 + 4 %6 − %5 − 4 ( )1/5 x3 + 6 ( )2/5 x2 ) a b b b a 1/5 a 1/5 √ 2 2 x − ( ) x + ( ) x 5 + 2 %3 a 1 b b I ( )1/5 arctan a 2/5 b 2 sin(%2) √ 1 1a b + + ( )1/5 5ln(4 %12 20 a 40 b √ √ a a + 4 %11 + 2 %10 + 4 ( )1/5 x3 5 − 2 ( )2/5 x2 5 + 2 %9 + %8 + 2 %7 + 4 x4 + 4 %6 b b a 1/5 3 a 2/5 2 − %5 − 4 ( ) x + 6 ( ) x ) a b b √ a a 2 x2 − ( )1/5 x + ( )1/5 x 5 + 2 %3 √ a 1 b b I ( )1/5 5 arctan a 2/5 b 2 sin(%2) 1 b − 20 a √ √ a a a a 4 x − ( )1/5 + ( )1/5 5 4 x − ( )1/5 + ( )1/5 5 √ a a b√ b b√ b 5 ( )2/5 arctan ( )2/5 arctan b b %13 %13 1 1 √ √ + + − 5 40 a %13 a %13 √ √ a a a ( )1/5 ln(4 %12 + 4 %11 + 2 %10 − 4 ( )1/5 x3 5 + 2 ( )2/5 x2 5 + 2 %9 − %8 + 2 %7 b b b √ a 1/5 |a| 2/5 a a + 4 x4 − 2 ( ) x ( ) cos(%4) 5 − 6 %6 − %5 − 4 ( )1/5 x3 + 6 ( )2/5 x2 ) a b |b| b b 86 √ a a 2 x2 − ( )1/5 x − ( )1/5 x 5 + 2 %3 a 1 b b I ( )1/5 arctan a 2/5 b 2 sin(%2) √ 1 1a b + − ( )1/5 5ln(4 %12 20 a 40 b √ √ a a + 4 %11 + 2 %10 − 4 ( )1/5 x3 5 + 2 ( )2/5 x2 5 + 2 %9 − %8 + 2 %7 + 4 x4 b b √ a a 1/5 |a| 2/5 a − 2 ( ) x ( ) cos(%4) 5 − 6 %6 − %5 − 4 ( )1/5 x3 + 6 ( )2/5 x2 ) a b |b| b b a 1/5 a 1/5 √ 2 x2 − ( ) x − ( ) x 5 + 2 %3 √ a 1 b b I ( )1/5 5 arctan a 2/5 b 2 sin(%2) 1 b + 20 a √ √ a a a a 4 x − ( )1/5 − ( )1/5 5 4 x − ( )1/5 − ( )1/5 5 √ a 2/5 a 2/5 b√ b b√ b 5 ( ) arctan ( ) arctan b b %1 %1 1 √ √ − + 5 a %1 a %1 √ a a %1 := 10 ( )2/5 − 2 ( )2/5 5 b b 11 a %2 := ( − signum( )) π 55 b a 2/5 %3 := cos(%2) b 1 π signum(a) %4 := 5 signum(b) √ √ |a| a %5 := ( )1/5 x ( )2/5 2 5 − 5 sin(%4) b |b| a |a| %6 := ( )1/5 x ( )2/5 cos(%4) b |b| √ |a| 2/5 %7 := ( ) cos(%4) 5 x2 |b| √ a 1/5 √ |a| 2/5 √ %8 := ( ) x 5 ( ) 2 5 − 5 sin(%4) b |b| √ |a| 2/5 √ %9 := ( ) 2 5 − 5 sin(%4) x2 |b| |a| %10 := ( )2/5 cos(%4) x2 |b| |a| 4/5 %11 := ( ) sin(%4)2 |b| |a| 4/5 %12 := ( ) cos(%4)2 |b| √ a a %13 := 10 ( )2/5 + 2 ( )2/5 5 b b , . > . Maple V Release 4 sprint with(share): 4.5 87 See ?share and ?share,contents for information about the share library > > readshare(sprint,system): sprint(test); << ∗4 >> + << ∗4 >> + << ∗5 >> + << ∗5 >> + << ∗6 >> + << ∗4 >> + << ∗6 >> + << ∗4 >> + << ∗5 >> + << ∗5 >> + << ∗6 >> + << ∗4 >> + << ∗6 >> EnvSpring 100. > sprint . . EnvSprint , , . _EnvSprint:=200; EnvSprint := 200 > sprint(test); a 1/5 a 1/5 a 1/5 a 1/5 1 ( b ) ln(<< +13 >>) 1 I ( b ) arctan(<< ∗4 >>) 1 ( b ) ln(x + ( b ) ) − + 5 a 40 a 20 a √ √ a a ( )1/5 5 ln(<< +13 >>) I ( )1/5 5 arctan(<< ∗4 >>) 1b 1 b + − 40 a 20 a √ a a 1/5 a ( )2/5 arctan(<< ∗2 >>) 1 ( )2/5 arctan(<< ∗2 >>) 5 1 ( b ) ln(<< +14 >>) b b √ √ + + − 5 40 a a << +2 >> a << +2 >> a 1/5 a 1/5 √ 5 ln(<< +14 >>) 1 I ( b ) arctan(<< ∗4 >>) 1 (b) + − 20 a 40 a √ a a I ( )1/5 5 arctan(<< ∗4 >>) ( )2/5 arctan(<< ∗2 >>) 1 b √ + +b 20 a a << +2 >> √ a 2/5 1 ( b ) arctan(<< ∗2 >>) 5 √ − 5 a << +2 >> 第五章 解方程 解方程是最古老的数学课题, 在本章中我们将介绍用 Maple 解方程的各种方法, 包括最常 用的命令 solve, fsolve, 以及 Gr¨bner 基方法和吴方法. o 5.1 符号解 Maple 的 solve 命令可以解各种各样的方程和方程组, 它能处理一个或多个程的集合. 对 于给定的未知量集合, 可以求出方程组的精确符号解. 下面我们先求解一个未知量的一个方程. > solve({x^2=4},{x}); {x = 2}, {x = −2} > solve({a*x^2+b*x+c=0},{x}); 1 −b + {x = 2 √ 1 −b − b2 − 4 a c }, {x = a 2 √ b2 − 4 a c } a Maple 以集合的形式返回每个可能的解, 因为上述方程都有两个解,Maple 返回解集合的序 列. 若不指定未知量,Maple 将对所有未知量求解. > solve({x+y=0}); {x = −y, y = y } 此例得到一个参数化的解,y 是参数. 以集合的方式表达方程和变量并不是必须的, 但这样做可迫使 Maple 以集合的方式返回解. 下面的例子就不是以集合的方式表达方程和变量, 因此它返回的结果也不是集合的形式, 而是 一个表达式序列. 例如 > eqn:=x^3-5*a*x^2+x=1; eqn := x3 − 5 a x2 + x = 1 > solve(eqn,x); 88 5.1 符号解 89 1 25 2 1 25 2 1 25 2 − a − a − a √ 1 1 5 1 5 1 1/3 1/3 1 /3 9 9 9 , %1 − 6 3 + a, − %1 + 3 3 + a + I 3 %1 + 6 3 1/3 1/3 1 /3 6 3 12 3 2 6 %1 %1 %1 1 25 2 1 25 2 − a − a 5 1 1 √ 1 1 /3 1 /3 3 9 9 + a − I 3 %1 + 6 3 − %1 + 3 1 /3 1 /3 12 3 2 6 %1 %1 %1 := −180 a + 108 + 1000 a3 + 12 93 − 75 a2 − 270 a + 1500 a3 从这个例子我们可以看出当 Maple 返回一个复杂的表达式的时候, 它的表达方式是用缩写 的形式表示公共的子表达式, 通常使用的名字是 %1,%2 的形式. 对于这样的标记我们同样可以 进行计算. > %1^2-1; (−180 a + 108 + 1000 a3 + 12 93 − 75 a2 − 270 a + 1500 a3 )2 − 1 需要说明的是: 上述结果是在 Maple V Release 4 中解方程得到的. 在 Maple 6 以后的版 本中,Maple 不再使用缩写的形式来表达公共的子表达式, 而是直接给出结果. 我个人还是喜欢 Maple V 的处理方法, 因为这样处理复杂的表达式使我们更容易了解表达式的结构. 若给出一个表达式而不是方程时,Maple 将自动假定该表达式等于 0. > solve({x^3-13*x+12},{x}); {x = 1}, {x = 3}, {x = −4} solve命令也可用于解方程组 > solve({x+2*y=3,y+x=1},{x,y}); {y = 2, x = −1} 对于 5 次以上的方程, 通常无法求出其符号解,Maple 经常以 RootOf 的形式返回其结果. 但 是不同的 Maple 版本返回结果的形式不完全一样, 下面的结果是 Maple V Release 4 版本返回 的结果 > solve({x^5+2*x+4=0},{x}); {x = RootOf( Z 5 + 2 Z + 4)} RootOf(expr)是一个占位者, 代表 expr 的所有根.Maple 告诉用户 x 是多项式 z 5 +2z +4 的 一个根. 在异于复数域的代数数域上进行代数运算时这种表示方式是很有用的. 利用 allvalues 命令可以得到所有解的显示形式. 90 > 第五章 解方程 allvalues("); {x = −1.119762609}, {x = −.5662131468 − 1.169221069 I }, {x = −.5662131468 + 1.169221069 I }, {x = 1.126094451 − .9211635491 I }, {x = 1.126094451 + .9211635491 I } 由于 5 次以上多项式不存在符号解, 因此 Maple 只能返回浮点近似值解. 而在 Maple 7 中, solve 命令返回的结果是下列形式 > S:=solve(x^5+2*x+4,x); S := RootOf( Z 5 + 2 Z + 4, index = 1), RootOf( Z 5 + 2 Z + 4, index = 2), RootOf( Z 5 + 2 Z + 4, index = 3), RootOf( Z 5 + 2 Z + 4, index = 4), RootOf( Z 5 + 2 Z + 4, index = 5) 用 evalf 命令直接可以求出 RootOf 的根, 下面的方法可以求出所有 5 个根. > map(evalf,[S]); [1.126094451 + .9211635491 I, −.5662131468 + 1.169221069 I, −1.119762609, −.5662131468 − 1.169221069 I, 1.126094451 − .9211635491 I ] 解的检验 对一个方程求解之后, 通常要做的第一件事就是把解代入原方程进行检验, 以保证 Maple 的解是正确的. 例如下列方程 > eqns:={x+2*y=3,y+1/x=1}; eqns := {x + 2 y = 3, y + 1 = 1} x > soln:=solve(eqns,{x,y}); soln := {x = −1, y = 2}, {x = 2, y = 1 } 2 首先我们得到它的两组解 > soln[1]; {x = −1, y = 2} > soln[2]; {x = 2, y = 1 } 2 5.1 符号解 91 为了检验解只需把解集合带入原方程或原方程组. > subs(soln[1],eqns); {1 = 1, 3 = 3} subs(soln[2],eqns); {1 = 1, 3 = 3} > 不难看出, 对于解的检验, 用集合形式表示解是比较方便的. subs 命令还有其他用处. 例如要从第一个解中取出 x 的值, 就可以用 subs 命令. > x1:=subs(soln[1],x); x1 := −1 同样可以从第一个解中取出 y 的值. > y1:=subs(soln[1],y); y1 := 2 利用这种替换技巧还可以把解集合转变为其他形式, 例如可以从第一个解出发构造一个列 表, 以 x 为第一个元素, 以 y 为第二个元素. 具体方法是首先用同样顺序构造一个变量列表, 然 后把第一个解带入这个列表. > subs(soln[1],[x,y]); [−1, 2] 第一个解现在变成了列表. 当方程的解数量较少时, 这样一个一个解的检验是可行的. 如果方程的解数量很多, 这种检 验方法的效率就太低了, 此时应当使用 map 命令. 由于 map 的语法设计,map 不能将函数应用于 序列, 因此在检验解的过程中应当用方括号来界定解, 使之转换为列表. > map(subs,[soln],eqns); [{1 = 1, 3 = 3}, {1 = 1, 3 = 3}] 下面我们再考察一个方程 > (x−1)2 (x2 −1) =0 expr:=(x-1)^2/(x^2-1); 92 第五章 解方程 expr := (x − 1)2 x2 − 1 用 Maple 的 solve 命令可以求的一个解 > soln:=solve(expr,x); soln := 1 但把解代入原方程将得到 0/0 > subs(soln,expr); Error, wrong number (or type) of parameters in function subs 求极限表明 x = 1 几乎是一个解 > limit(expr,x=1); 0 出现这种错误的原因在于 Maple 在解方程之前, 进行了自动约分, 从而导致了增根. 因此不 论用什么方法解方程, 都不要忘记检验你的根. 解三角方程 使用 solve 命令也可以解部分特殊的超越方程, 当然在大多数情况下处理超越方程是很困 难的. 在解三角方程时,Maple 通常返回一个特解. 例如 > solve(sin(x)=cos(x),x); 1 π 4 如果我们想要得到上述方程的通解 x = π/4 + nπ , 我们必须把环境变量 EnvAllSolutions 的值设为 true. > > _EnvAllSolutions:=true: solve(sin(x)=cos(x),x); 1 π + π Z˜ 4 在通常情况下,Maple 用 Z 代表整数, N 代表非负整数, B 代表二值 (0/1). 用这种方式虽然能得到通解, 但是解的表达式可能具有病态. 例如 > s:=solve(sin(x^2)=1/2); 5.1 符号解 93 s := 1√ 1√ 6 π + 24 π B1 ˜ + 72 π Z1 ˜, − 6 π + 24 π B1 ˜ + 72 π Z1 ˜ 6 6 我们希望检验第一个解, 但是化简以后并不能得到我们期待的结果. > test:=subs(x=s[1],sin(x^2)); 1 2 test := sin( π + π B1 ˜ + 2 π Z1 ˜) 6 3 > simplify(test); 1 2 sin( π + π B1 ˜ + 2 π Z1 ˜) 6 3 虽然 Z1 和 B1 都具有假设的标记 ˜, 但是在 Maple 内部并没有关于它们的信息. 即使我 们手工的加上有关的假设, 化简依然不能成功. > about(_Z1); _Z1: nothing known about this object > about(_B1); _B1: nothing known about this object > > assume(_Z1,integer,_B1,boolean); simplify(test); 1 2 sin( π + π B1 ˜ + 2 π Z1 ˜) 6 3 解的探讨 考虑下列五个未知量三个方程的方程组, > > > eqn1:=x+2*y+3*z+4*w+5*u=14: eqn2:=5*x+4*y-3*z+6*w-2*u=20: eqn3:=3*y-2*z+8*w+2*u=120: 由于未知量的个数比方程的个数多, 因此得到的解一定含有某些未知量. 我们不妨把 w, u 作为参数 > s:=solve({eqn1,eqn2,eqn3},{x,y,z}); s := {z = 23 95 86 15 1130 31 19 943 1 w− u− , y =− w− u+ ,x= w+ u− } 11 22 11 33 11 33 33 22 33 94 第五章 解方程 对于这个参数解, 我们以 w = 1, u = 1 代入, 可以得到 x, y, z 的值. > subs({w=1,u=1},s); {z = −211 333 −589 ,y= ,x= } 22 11 22 为了进一步了解这个方程组解的特征, 我们可以画出曲面的图象. 为此我们首先按照 x, y, z 的顺序返回解, 方法是先构造一个列表 [x, y, z ], 然后使用 subs 命令. > subs(s,[x,y,z]); [ 19 943 86 15 1130 1 23 95 31 w+ u− , − w− u+ , w− u− ] 33 22 33 33 11 33 11 22 11 > plot3d(%,w=0..2,u=0..2,axes=BOXED); -8.5 -9 -9.5 -10 -10.5 27 28 29 -28 -27.5 -28.5 30 31 32 33 34 -25 -26 -25.5 -27 -26.5 显然我们可以把 x, y, z 作为 w, u 的函数, 然而利用 subs 命令只能得到解的表达式 > subs(s,x); 31 19 943 w+ u− 33 22 33 这里 x 是表达式, 不是函数. 所以下面的计算没有结果. > x(1,1); x(1, 1) 要想把表达式转换为函数, 需要用 unapply 命令. 提供给 unapply 以表达式和变量,Maple 将返回一个函数. 例如 > x:=unapply(subs(s,x),w,u); 5.2 数值解 95 x := (w, u) → 19 943 31 w+ u− 33 22 33 如果我们想得到 x, y, z 这三个函数, 可以把 map 和 unapply 联合使用. 我们用 s1 来代表 这个函数列表, 那么 s1[1] 就是函数 x = x(w, u),s1[2] 就是函数 y = y (w, u), s1[3] 就是函数 z = z (w, u). > s1:=map(unapply,subs(s,[x,y,z]),w,u); s1 := [(w, u) → 31 19 943 86 15 1130 1 23 95 w+ u− , (w, u) → − w − u+ , (w, u) → w− u− ] 33 22 33 33 11 33 11 22 11 > s1[1](1,2); −285 11 > s1[2](1,1); 333 11 > s1[3](0,1); −213 22 5.2 数值解 solve命令虽然非常强大, 但是许多方程特别是超越方程还是不能得到符号解, 此时我们只 能去求方程的数值解. fsolve 可以用来求方程或方程组的数值解.fsolve 是 Newton 方法的一 个变异, 它可以计算方程或方程组, 产生浮点近似解. 我们首先解一个对数方程 > fsolve(ln(1+x)+ln(3+x)+x=5); 2.192154954 fsolve非常轻松的找到了方程的解. 但是下一个方程给我们带来一些问题. > f:=2*x+cos(x)-2*sin(2*x); 96 第五章 解方程 f := 2 x + cos(x) − 2 sin(2 x) 直接使用 fsolve 命令没有得到解, 此时我们需要给定一个范围让 Maple 去搜索解. 我们 给的第一个范围是 x=-2..2, 在这个范围内 Maple 还是没有找到解, 事实上在这个范围内方程 是有解的,fsolve 没有找到解是因为这个范围太大了, 从图象上可以看出在 −1 附近有一个解, 缩小范围后就得到了解. > fsolve(f); fsolve(2 x + cos(x) − 2 sin(2 x), x) > fsolve(f,x=-2..2); fsolve(2 x + cos(x) − 2 sin(2 x), x, −2..2) > plot(f,x=-2..2); 4 2 -2 -1 x 1 2 0 -2 -4 -6 > fsolve(f,x=-2..-1); −1.074907239 对于一般的方程,fsolve 只找出方程的单一实根. 但对于多项式,fsolve 通常给出所有实数 解 > fsolve(3*x^4-16*x^3-3*x^2+13*x-16); −1.228498600, 5.403790581 可以通过设定选项 maxsols 来求多项式一定数目的根 > fsolve(3*x^4-16*x^3-3*x^2+13*x-16,x,maxsols=1); 5.2 数值解 97 −1.228498600 当我们设置复数选项时,fsolve 才计算复数解. > fsolve(3*x^4-16*x^3-3*x^2+13*x-16,x,complex); −1.228498600, .5790206758 − .6841954755 I, .5790206758 + .6841954755 I, 5.403790581 如果用 solve 命令可以求出方程的解, 就不要用 fsolve 命令, 这是因为 fsolve 可能无法 找到所有根. 例如 > fsolve({x+y=9/2,x*y=5},{x,y}); {x = 2.500000000, y = 2.} > solve({x+y=9/2,x*y=5},{x,y}); {y = 2, x = 5 5 }, {y = , x = 2} 2 2 > evalf(%); {x = 2.500000000, y = 2.}, {x = 2., y = 2.500000000} 有时对非常简单的方程 fsolve 也找不到解, 例如 > fsolve(x^2+7); 这是因为我们没有设置 complex 项. 因此在解多项式方程是, 最好是设置 complex 项. > fsolve(x^2+7,x,complex); −2.645751311 I, 2.645751311 I 用 fsolve 解一般方程时, 通常只能得到一个解, 要想得到更多的解, 最好的办法是先画出 函数的图象, 从图中可以确定求解的区间. > f:=cos(x^2)-x/5; f := cos(x2 ) − 1 x 5 > plot(f,x=0..4); 98 第五章 解方程 1 0.5 1 x 2 3 4 0 -0.5 -1 -1.5 > fsolve(f); 2.277111200 > fsolve(f,x,x=0..2); 1.156456471 > fsolve(f,x,x=2.5..3); 2.698854693 > fsolve(f,x,x=3..3.6); 3.427972325 > fsolve(f,x,x=3.6..4); 3.649518579 在上面的例子中, 我们通过对图形进行观察来确定解的区间. 对于多项式,Maple 可以用 realroot 命令找到解所在的区间. 例如 > readlib(realroot); proc(poly , widthgoal ) . . . end poly:=x^7-2*x^6-4*x^5-x^3+2*x^2+6*x+4; > 5.3 解方程的其它命令 99 poly := x7 − 2 x6 − 4 x5 − x3 + 2 x2 + 6 x + 4 > realroot(poly); [[0, 2], [2, 4], [−2, 0]] > realroot(poly,1/100); [[ 153 77 413 207 −171 −85 , ], [ , ], [ , ]] 128 64 128 64 128 64 realroot命令的第二个参数指定解的孤立区间的大小, 这个值越小, 得到的区间就越接近 多项式的根. 5.3 解方程的其它命令 解不等式 solve命令不仅可以解方程, 也可以解不等式. 例如 > solve({abs(x)<1},x); {x < 1, −1 < x} 得到的解是 x 所满足的不等式 −1 < x < 1. > solve(x^2+x>2,{x}); {x < −2}, {1 < x} 解第二个不等式的结果需要稍加解释: 由逗号分开的两个花括号意味者 ‘或’; 也就是说 x + x > 0 蕴涵着 x < −2 或 x > 1. 2 solve 也能解简单的不等式方程组 > eq:={x+y>=5,x-y>=1,y-x/2<=1/2}; eq := {5 ≤ x + y, 1 ≤ x − y, y − 1 1 x≤ } 2 2 > solve(eq,{x,y}); {5 − x − y ≤ 0, y − 1 1 x − ≤ 0, 1 − x + y ≤ 0, 3 ≤ x} 2 2 100 第五章 解方程 求整数解 isolve命令在整数范围内解方程, 也就是对表达式中所有未知量求整数解 > isolve(x+2*y+3*z=10); {x = 10 − 2 N1 − 3 N2 , y = N1 , z = N2 } Maple 用全局变量 N1, N2 表示解的参数. 如果 Maple 找不到方程的整数解, 将什么也不返回. 例如 > isolve(x^5=5); 求模 m 的解 msolve命令可以在模 m 的整数环上解方程. > msolve({3*x-4*y=1,2*x+y=3},17); {y = 13, x = 12} 如果解有参数,Maple 通常用 NN1,... NNm 来表示 > msolve({2^n=3},11); {n = 8 + 10 NN1 ˜} 这里 msolve 对 NN1 作了假设, 使用 about 命令可知对 NN1 的假设是整数. > about(_NN1); Originally _NN1, renamed _NN1~: is assumed to be: integer 求解递推关系 使用 rsolve 命令,Maple 可以求解递推关系, 返回函数的通项表达式. 最典型的例子是 Fibonacci 数列 > rsolve({f(n)=f(n-1)+f(n-2),f(0)=1,f(1)=1},{f(n)}); √ √ 2 2 )n )n 5 (− √ 5 (− √ 2 2 − √ 5+1 + √ 5+1 f(n) = − 5 5 − 5+1 5+1 由此我们得到了 Fibonacci 数列的通项. 其它例子还有 > rsolve({T(n)=T(n-1)+n^2,T(1)=0},T(n)); 5.3 解方程的其它命令 101 1 1 1 2 (n + 1) ( n + 1) ( n + 1) − 3 (n + 1) ( n + 1) + n 2 3 2 > factor(%); 1 (n − 1) (2 n2 + 5 n + 6) 6 > rsolve({T(n)=2*T(n/2)+n-1,T(1)=0},T(n)); 1 n (2 ( ) 2 ln(n) +1 ln(2) + ln(n) − 1) ln(2) > simplify(%); − −ln(2) − ln(n) n + n ln(2) ln(2) 有关线性递归方程的进一步探讨请参阅 Maple 的 LREtools 程序包. 求解函数方程 通常 solve 可以求出方程的一个或几个解, 然而当方程的未知量是形如 f (x) 的函数时,solve 将尝试生成函数作为方程的解. 例如 > sln:=solve(f(x)+f(x)^2=1+x^3,f(x)); 11 sln := − + 22 11 5 + 4 x3 , − − 22 5 + 4 x3 我们用 test 来表示 sln 的第一个函数, 检验一下 test(x)^+test(x) 是否是方程的解. 3 > test:=unapply(sln[1],x); 11 test := x → − + 22 5 + 4 x3 > simplify(test(x)+test(x)^2); 1 + x3 检验的结果说明, 我们的解是正确的. 方程系数的匹配 match(eqn1=eqn2,x,’sln’)命令可以比较 eqn1 和 eqn2 的系数, 也就是说 match 命令试图 确定 eqn1 和 eqn2 中的未知系数, 使得两个方程恒等. 如果匹配成功, 返回值为 true, 否则返 102 第五章 解方程 回值为 false. 而方程的未知系数可以用第三个参数 sln 返回. > match(x^2-1=(x-a)*(x-b),x,’sln’); true > sln; {b = −1, a = 1} solve命令的 identity 子项也可以完成类似的工作, 它的用法是 solve(identity(eqn1=eqn2,x),a,b,...). 因此上述问题用 identity 命令的解为 > solve(identity(x^2-1=(x-a)*(x-b),x),{a,b}); {b = −1, a = 1}, {b = 1, a = −1} 用 identity 得到的解要比用 match 得到的解多. 但是 identity 命令通常只能用在多 项式上, 对于其它的函数它就无能为力了. 例如下面的方程用 match 命令可以解出, 但是用 identity 命令就不行. > match(2*sin(3*x^4)=a*sin(n*x^k),x,’sln’); true > sln; {k = 4, n = 3, a = 2} > solve(identity(2*sin(3*x^4)=a*sin(n*x^k),x),{a,n,k}); 对于复杂的多项式变换 match 也可以使用, 此时得到的解可能会包含 RootOf 形式的表 达式. > match(x^2+3*x+5=a*(x+b)^2+c*x,x,’sln’); true > sln; {b = RootOf(−5 + Z 2 ), c = −2 RootOf(−5 + Z 2 ) + 3, a = 1} > allvalues(%); 5.4 Gr¨bner 基原理 o 103 √ √ √ √ {a = 1, c = −2 5 + 3, b = 5}, {a = 1, c = 2 5 + 3, b = − 5} > subs(%[1],a*(x+b)^2+c*x); (x + √ √ 5)2 + (−2 5 + 3) x 另一方面,match 和 identity 命令对于其它复杂的函数变换就无能为力了. 例如在下面 的例子中,c = π/2 显然可以是方程匹配, 但是 match 和 identity 都求不出结果. > > solve(identity(sin(2*x)+sin(x)=cos(x+c)+cos(2*x+c),x),{c}); match(sin(2*x)+sin(x)=cos(x+c)+cos(2*x+c),x,’sln’); false 5.4 Gr¨bner 基原理 o Gr¨bner 基算法是 Buchberger 在 1965 年提出的计算多项式理想基的算法. o 这个算法 的提出对符号计算系统的发展起到了巨大的推动作用. 在这一节中, 我们将介绍用 Maple 计 算 Gr¨bner 基的方法, 以及如何利用 Gr¨bner 基解多项式方程组. o o 我们首先考察一个多项式方程组 > eqns:={x^2+y^2=1,z=x-y,z^2=x+y}; eqns := {x2 + y 2 = 1, z = x − y, z 2 = x + y } 为了使用 Gr¨bner 基原理, 我们首先把这个方程组整理成一组多项式. o > polys:=map(lhs-rhs,eqns); polys := {x2 + y 2 − 1, z − x + y, z 2 − x − y } 其中 lhs 和 rhs 分别表示方程的左边的项和右边的项. 在计算 Gr¨bner 基时我们需要 o 调用 grobner 程序包. 此外我们还要确定变量的次序以及所用的单项式序. 在这个例子中, 变量的次序为 z > x > y , 单项式序为字典序. > with(grobner); [finduni , finite , gbasis , gsolve , leadmon , normalf , solvable , spoly ] > G:=gbasis(polys,[z,x,y],’plex’); 104 第五章 解方程 G := [3 z + 4 y 3 − 3 + 2 y 2 , 3 x − 3 y − 3 + 4 y 3 + 2 y 2 , −3 y − y 2 + 2 y 4 + 2 y 3 ] 由于 Gr¨bner 基是多项式理想的基, 因此它的零点集和原方程组 eqns 的零点集是相同 o 的. 从我们得到的基可以看出最后一个多项式 24 + 2y 3 − y 2 − 3y 中只有一个变量 y , 因此我 们可以解出 y . 在第二个多项式中只含变量 x 和 y , 由 y 可以解出 x. 具体的关系是 x = y + 1 − 4/3y 3 − 2/3y 2 同样道理, 我们也可以由第一的多项式解出 z . 具体的关系是 z = −4/3y 3 − 2/3y 2 + 1 > rootlist:=[solve(G[3])]; rootlist := [0, 1, −1 + for i to nops(rootlist) do ’x’=simplify(subs(y=rootlist[i],solve(G[2],x))), ’y’=rootlist[i], ’z’=simplify(subs(y=rootlist[i],solve(G[1],z))); simplify(subs({%},polys)) od; x = 1, y = 0, z = 1 {0} x = 0, y = 1, z = −1 {0} x = −1 − √ 1√ 1√ I 2, y = −1 + I 2, z = −I 2 2 2 {0} √ 1√ 1√ x = −1 + I 2, y = −1 − I 2, z = I 2 2 2 {0} 1√ 1√ I 2, −1 − I 2] 2 2 > > > > > > 实际上用 Gr¨bner 基解方程不用这么复杂, 直接使用 gsolve 命令即可. o > gsolve(polys,[x,y,z]); [[x − 1, y, z − 1], [x, y − 1, z + 1], [2 x − z + 2, 2 y + z + 2, z 2 + 2]] 5.4 Gr¨bner 基原理 o 105 gsolve命令产生的结果是一系列的新的多项式组, 它们的根是原方程组的根, 而且这些新 方程组通常很容易求解. Gr¨bner 基的另一个重要应用是具有附加关系的化简. o 2 2 2 3 3 3 我们首先看一个例子. 例 已知 a + b + c = 3, a + b + c = 9, a + b + c = 24. 计算 a4 + b4 + c4 . 首先我们可以使用具有附加关系的化简来解决这个问题 > siderels:={a+b+c=3,a^2+b^2+c^2=9,a^3+b^3+c^3=24}; siderels := {a + b + c = 3, a2 + b2 + c2 = 9, a3 + b3 + c3 = 24} > simplify(a^4+b^4+c^4,siderels); 69 在进行具有附加关系的化简过程中, 使 Maple 获得成功的依然是 Gr¨bner 基原理. 具 o 体方法是首先把附加关系转换为一组多项式 > polys:=map(lhs-rhs,siderels); polys := {a + b + c − 3, a2 + b2 + c2 − 9, a3 + b3 + c3 − 24} 然后在字典序之下计算首系数为 1 的极小 Gr¨bner 基. 实际上用 gbasis 计算出来的 o Gr¨bner 基都满足这些要求. o > G:=gbasis(polys,[a,b,c],’plex’); G := [a + b + c − 3, b2 + c2 − 3 b − 3 c + b c, 1 − 3 c2 + c3 ] 的选择. 事实上, 当我们给定了单项式序 , 以及 一个多项式的集合 P , 我们就可以定义一个约化关系 →P . 所谓 f 可以约化到 g 实际上是 说: g = f − m1 p1 − m2 p2 − · · · − ml pl Gr¨bner 基的计算依赖于单项式序 o 其中 p1 , p2 , · · · , pl ∈ P , m1 , m2 , · · · , ml 是一组多项式, 并且满足 mi pi 的最高次项在单项式 序的意义下不超过 f 的最高次项. 也就是说, 在单项式序之下, 利用多项式集 P 中的多项 式来消去多项式 f 的最高次项最终得到多项式 g . 此时在序 之下, 我们有 g f. 应 用这种约化关系我们可以刻画 Gr¨bner 基 o 定理 一个多项式的有限集合 G 是 Gr¨bner 基的充分必要条件是由 G 生成的理想中的 o 每个多项式在约化关系 →G 下都可以约化为零多项式. 为了使读者更好的理解约化的概念, 我们通过几个例子来说明. 以刚才计算的 Gr¨bner o 基 G 为例. 从第一个多项式出发, 我们可以把 a 约化为 a →G 3 − b − c 106 第五章 解方程 从第二个多项式出发, 我们可以把 b2 约化为 b2 →G −bc + 3b − c2 + 3c 从第三个多项式出发, 我们可以把 c3 约化为 c3 →G 3c2 − 1 如果我们把这种约化反复应用到一个多项式 f , 一直到不能再约化为止, 这样得到的多项式 叫做 f 对于 G 的范式. Maple 通过 normalf 命令来计算范式. 下面我们来计算多项式 a4 + b4 + c4 对于 G 的 范式. > normalf(a^4+b^4+c^4,G,[a,b,c],’plex’); 69 它就是我们用具有附加关系化简的结果. 也就是说: 具有附加关系的化简实际的计算方 法是首先求出由附加关系所导出的多项式理想的 Gr¨bner 基, 然后利用这个基求出要化简的 o 多项式的范式. 有关 Gr¨bner 基理论的应用还有很多, 我们不在这里一一说明, 有兴趣的读者可以参阅 o [1]. Maple , , , ,Maple . . Maple . 6.1 y = f (x) > , Maple . plot(cos(x),x=-2*Pi..2*Pi); 1 0.5 -6 -4 -2 0 2 x 4 6 -0.5 -1 Maple > . f:=x->2*sin(x)+sin(3*x); f := x → 2 sin(x) + sin(3 x) > plot(f(x),x=0..10); 2 1 0 2 4 x 6 8 10 -1 -2 Maple > y x . plot(f(x),x=0..10,y=1..2.5); 107 108 2.4 2.2 2 1.8 y 1.6 1.4 1.2 10 2 4 x 6 8 10 Maple . > plot(sin(x)/x,x=0..infinity); 0 x infinity ,Maple y = 2x + 1. , , . , . > plot(2*x+1,x=0..5); 10 8 6 4 2 0 1 2 x 3 4 5 > plot(2*x,x=0..infinity); 6.1 109 infinity 0 x infinity , , plot . : . . . . ?plot,options plot , x + y = 1, , , x y 2 2 y = f (x) x . x-expr, y-expr, , y . . plot([x-expr,y-expr,parameter=range]). > plot([t^5,t^3,t=-1..1]); 1 0.5 -1 -0.5 0 0.5 1 -0.5 -1 , x = cos t y = sin t t 0 2π . > plot([cos(t),sin(t),t=0..2*Pi],scaling=constrained); 110 1 0.5 -1 -0.5 0 0.5 1 -0.5 -1 : Maple , .scaling , , . , . plot . plot Maple scaling=constrained . . > plot(exp(x),x=0..3); 20 18 16 14 12 10 8 6 4 2 0 0.5 1 1.5 x 2 2.5 3 . > plot(exp(x),x=0..3,scaling=constrained); 20 18 16 14 12 10 8 6 4 2 0.5 x2 .5 0 1.5 3 ,Maple (r, θ) . , , 6.1 r 111 y r θ 0 ,r , with > x ,θ polarplot plots Maple . with , . , with(plots); [animate , animate3d , changecoords , complexplot , complexplot3d , conformal , contourplot , contourplot3d , coordplot , coordplot3d , cylinderplot , densityplot , display , display3d , fieldplot , fieldplot3d , gradplot , gradplot3d , implicitplot , implicitplot3d , inequal , listcontplot , listcontplot3d , listdensityplot , listplot , listplot3d , loglogplot , logplot , matrixplot , odeplot , pareto , pointplot , pointplot3d , polarplot , polygonplot , polygonplot3d , polyhedraplot , replot , rootlocus , semilogplot , setoptions , setoptions3d , spacecurve , sparsematrixplot , sphereplot , surfdata , textplot , textplot3d , tubeplot ] polarplot(r=exor,angle=range). , > r = 1. polarplot(1,theta=0..2*Pi,scaling=constrained ); 1 0.5 -1 -0.5 0 0.5 1 -0.5 -1 r = cos(5θ) > . polarplot(cos(5*theta),theta=0..2*Pi); 112 0.8 0.6 0.4 0.2 -0.8 -0.6 -0.4 -0.2 0 -0.2 -0.4 -0.6 -0.8 0.2 0.4 0.6 0.8 1 , , r = sin(t), θ = cos(2t) > . polarplot([r-expr,angle-expr,parameter=range]). polarplot([sin(t),cos(2*t),t=0..2*Pi]); 0.8 0.6 0.4 0.2 -0.8 -0.6 -0.4 -0.2 0 -0.2 -0.4 -0.6 -0.8 0.2 0.4 0.6 0.8 plot , coords=polar. > plot([sin(phi)*sin(5*phi),phi,phi=0..2*Pi],coords=polar); 1 0.5 -0.4 -0.2 0 0.2 0.4 -0.5 -1 > > > S:=t->100/(100+(t-Pi/2)^8): R:=t->S(t)*(2-sin(7*t)-cos(30*t)/2): polarplot([R(t),t,t=-Pi/2..3/2*Pi],axes=none); 6.1 113 , . . x > y = y (x) , implicitplot x = x(t), y = y (t) , , F (x, y ) = 0 y . implicitplot(x^3+y^3-5*x*y+1/5=0,x=-3..3,y=-3..3,grid=[50,50]); 2 y 1 -3 -2 -1 1 x 2 0 -1 -2 -3 ,Maple F (x, y ) z. . numpoints > F (x, y ) = 0 , grid z= , implicitplot(sin(x*y),x=-4..4,y=0..10,grid=[80,80],numpoints=5000); 10 8 6 y 4 2 -4 -2 0 2 x 4 implicitplot , . , , 114 . , −1 f (x) = 1 3 x=1 x < 1, 1 ≤ x < 2, . x=2 , Maple > f (x) f:=x->piecewise(x<1,-1,x<2,1,3); f := x → piecewise(x < 1, −1, x < 2, 1, 3) > plot(f(x),x=0..3); 3 2 1 0 0.5 1 1.5 x 2 2.5 3 -1 Maple , > . discont=true Maple , plot(f(x),x=0..3,discont=true); 3 2 1 0 0.5 1 1.5 x 2 2.5 3 -1 , . > , x→ 1 (x−1)2 , . x=1 plot(1/(x-1)^2,x=-5..5); 6.1 115 1.2e+06 1e+06 800000 600000 400000 200000 -4 -2 0 2x 4 , y , y=0 8. x=1 . > plot(1/(x-1)^2,x=-5..5,y=0..10); 10 8 6 y 4 2 -4 -2 0 2x 4 , x= π 2 + nπ . > plot(tan(x),x=-2*Pi..2*Pi); 3000 2000 1000 -6 -4 -2 0 2 x 4 6 , y , y = −10 10. > plot(tan(x),x=-2*Pi..2*Pi,y=-10..10); 116 10 y5 -6 -4 -2 0 2 x 4 6 -5 -10 Maple > , discont=true . plot(tan(x),x=-2*Pi..2*Pi,y=-10..10,discont=true); 10 y5 -6 -4 -2 0 2 x 4 6 -5 -10 plot . . , , ,plot , [[x1 , y1 ], [x2 , y2 ], . . . , [xn , yn ]]. , > . data_list:=[[-2,4],[-1,1],[0,0],[1,1],[2,4],[ 3,9],[4,16]]; data list := [[−2, 4], [−1, 1], [0, 0], [1, 1], [2, 4], [3, 9], [4, 16]] > plot(data_list); 16 14 12 10 8 6 4 2 -2 -1 0 1 2 3 4 6.1 , Maple , > 117 . . style=point Maple plot(sin(x),x=0..2*Pi,style=point); 1 0.5 0 1 2 3x 4 5 6 -0.5 -1 . > > data:=seq([rand(100)(),rand(100)()],n=1..100): plot([data],style=point,symbol=diamond); 100 80 60 40 20 0 20 40 60 80 100 , . > plot plot([sin(x),x^2/20],x=0..2*Pi); 2 1.5 1 0.5 0 -0.5 -1 1 2 3x 4 5 6 > plot({seq(sin(n*x)/n,n=1..5)},x=0..Pi); 118 1 0.8 0.6 0.4 0.2 0 -0.2 -0.4 0.5 1 1.5 x 2 2.5 3 . > plot([[2*cos(t),sin(t),t=0..2*Pi],[t^2,t^3,t= -1..1]],scaling=constrained); 1 0.5 -2 -1 0 -0.5 -1 1 2 , , : > plots , 1 display . . y = x + sin(x) f:=x+sin(x); f := x + sin(x) > df:=diff(f,x); df := 1 + cos(x) > x0:=evalf(solve(df=1,x)); x0 := 1.570796327 > y0:=evalf(subs(x=x0,f)); y0 := 2.570796327 > > > p1:=plot(x+sin(x),x=0..4): p2:=plot([[x0-0.5,y0-0.5],[x0+0.5,y0+0.5]]): display([p1,p2],scaling=constrained); 6.2 119 3 2.5 2 1.5 1 0.5 0 1 2 x 3 4 Maple , , . > , . Maple linestyle . , plot([seq(sin(x+n*Pi/12),n=1..4)],x=0..2*Pi,l inestyle=[1,2,3,4]); 1 0.5 0 1 2 3x 4 5 6 -0.5 -1 Maple color > , . . plot([x^5,x^3],x=-1..1,color=[gold,plum]); 1 0.5 -1 -0.5 0 0.5 x 1 -0.5 -1 ?plot,color. 6.2 ,Maple plot , plot3d .plot3d 120 > plot3d(sin(x*y),x=-2..2,y=-2..2); , Maple plot3d style=patch > > , , style=patch , setoptions3d style . . . . setoptions3d(style=patch); plot3d(x^2-y^2,x=-1..1,y=-1..1); plot3d . axes=boxed > , plot . . plot plot3d(Im(sin(x+I*y)),x=0..2*Pi,y=-Pi/2..Pi/2 ,axes=boxed); 2 1 0 -1 -2 -1.5 0 -1 -0.5 0 y 0.5 1 1.5 6 5 4 3 x 2 1 , , , [45, 45]. . orientation 6.2 > 121 plot3d(sin(x*y),x=0..7,y=0..7,axes=framed,grid=[60,60],scaling=constrained); 1 0 -1 0 1 1 0 2 3 y4 5 6 77 6 5 4x 3 2 > > plot3d(sin(x*y),x=0..7,y=0..7,axes=framed,grid=[60,60], scaling=constrained,orientation=[-45,60]); 1 0 -1 0 7 1 2 3 x4 5 6 70 1 2 3y 4 5 6 , . x = sin(s) cos(t) plot3d . , . z = sin(t) z = f (x, y ) . , : y = cos(s) cos(t) , > > plot3d([sin(s)*cos(t),cos(s)*cos(t),sin(t)],s=0..Pi,t=0..2*Pi, scaling=constrained); 122 spacecurve > > > with(plots): spacecurve([t/10,(1+t/10)*sin(t),(1+t/10)*cos(t)],t=0..30*Pi, numpoints=500,axes=framed,shading=none,orientation=[25,62]); 10 5 0 -5 -10 -10 4 2 0 -5 0 5 10 8 6 spacecurve > ,tubeplot 1 . tubeplot([2*sin(t),2*cos(t),t/3],t=-1.5*Pi..2*Pi,orientation=[45,68]); , > > . tubeplot([-t^1.5/5,3*cos(t),3*sin(t)],t=0..8*Pi,radius=t/8, scaling=constrained,orientation=[107,56],grid=[60,15]); 6.2 123 , ,θ OP xOy x- , ,φ P OP z- :r . z r φ r 0 x θ y sphereplot plots . , ,Maple plots ( 4 )θ 3 . sin(φ) . sphereplot sphereplot sphereplot(r-expr,theta=range,phi=range). r = > sphereplot((4/3)^theta*sin(phi),theta=-1..2*Pi,phi=0..Pi,style=patch); sphereplot , . , s, t, , r = exp(s) + t, θ = cos(s + t), φ = t2 . > sphereplot([exp(s)+t,cos(s+t),t^2],s=0..2*Pi, t=-2..2,style=patch); 124 , r, θ z. z. r θ x-y - ,z z r 0 x θ y r ,Maple cylinderplot . . cylinderplot(r=expr,angle=range,z=range). > cylinderplot(theta,theta=0..4*Pi,z=-1..1); , > r z, θ . cylinderplot(z,theta=0..2*Pi,z=0..1); 6.2 125 cylinderplot > , . cylinderplot([s*t^2,s,cos(t^3)],s=0..Pi,t=-2..2,style=patch); plot3d stley=patch . . > > ,style . style , hidden, point, grid , . plot3d([sin(s)*cos(t),cos(s)*cos(t),t],s=0..2*Pi,t=0..Pi, grid=[80,20],shading=none,scaling=constrained,style=point); . pointplot3d [[x1 , y1 , z1 ], [x2 , y2 , z2 ], · · ·] . .pointplot3d style=line, , 126 . . > > , seq 1000 , pointplot3d data:=[seq(evalf([t/100,sin(t/40),cos(t/40)]),t=0..1000)]: pointplot3d(data,axes=boxed); 1 0.5 0 -0.5 -1 -1 -0.5 0 0.5 1 10 8 6 4 0 2 , z , listplot3d listplot3d xy surfdata . , . . : z , [[z11 , z12 , · · · , z1n ], [z21 , z22 , · · · , z2n ], · · · , [zm1 , zm2 , · · · , zmn ]] zij > > (i, j ) z . listplot3d([[0,0,1,0,0],[0,0,1,2,2],[0,1,2,1,0],[0,2,3,2,0]], axes=frame,style=hidden,color=black,orientation=[45,11]); 3 2 1 0 1 2 3 4 54 3 3.5 2.5 1.5 2 1 surfdata, . (x, y, z ) : . [[p11 , p12 , · · · , p1n ], [p21 , p22 , · · · , p2n ], · · · , [pm1 , pm2 , · · · , pmn ]] pij . > > > > [xij , yij , zij ] . seq surfdata . . setoptions3d(style=hidden,color=black): data:=[seq([seq([i,j,evalf(cos(sqrt(i^2+j^2)/2))], i=-5..5)],j=-5..5)]: surfdata(data,axes=frame,labels=[x,y,z]); 6.2 127 1 0.5 z0 -0.5 -1 -4 -2 0 y 0 x -2 -4 2 4 4 2 seq . . , > > > > > > > > > > > > > , , . , , Maple , seq 1 . . seq2 seq2:=proc(f,l1::list,l2::list) local i,j,data1,data2,range1,range2; range1:=evalf(l1): range2:=evalf(l2): data1:=; for i from op(1,op(2,range1[1])) to op(2,op(2,range1[1])) by range1[2] do data2:=; for j from op(1,op(2,range2[1])) to op(2,op(2,range2[1])) by range2[2] do data2:=[op(data2),eval(subs(op(1,range2[1])=j,op(1,range1[1])=i,f))]; od; data1:=[op(data1),data2]; od; RETURN(data1); end: , [x, y, z ] , . . , seq2 [x=start..end,step] > seq2([x,y,x+y],[x=1..3,1],[y=0.1..0.3,0.1]); [[[1., .1, 1.1], [1., .2, 1.2], [1., .3, 1.3]], [[2., .1, 2.1], [2., .2, 2.2], [2., .3, 2.3]], [[3., .1, 3.1], [3., .2, 3.2], [3., .3, 3.3]]] seq2 surfdata > > > . , . data:=seq2([r*sin(phi),r*cos(phi),1.5*phi+3*sin(r)], [r=Pi..2*Pi,Pi/9],[phi=0..4*Pi,Pi/10]): surfdata(data); 128 plot3d ambientlight. • color • light • shading , ambientlight , . xy. z z. > > > > , . , x, y . , shading, color, light .shading . , Maple , z . . z zhue xyz. color shading x, y . .shading z ,zhue , zgrepscale, shading=zhue with(plots): plot3d(-5*cos(sqrt(x^2+y^2))*exp(-0.1*sqrt(x^2+y^2)), x=-2*Pi..7*Pi,y=-2*Pi..4*Pi,grid=[45,35],style=patchnogrid, shading=zhue); shading none, style=point Maple hidden , . , . , . , , . , color , . . color , , color 6.2 > > > 129 p1:=plot3d(1.5*cos(x^2+y^2),x=-2..2,y=-2..2,color=red): p2:=plot3d(2*sin(sqrt(x^2+y^2)),x=-2..2,y=-2..2,color=blue): display([p1,p2]); , r(x, y ), g (x, y ), b(x, y ) > > x, y , . . color=COLOR(RGB,r(x,y),g(x,y),b(x,y)) aa:=(x,y)->COLOR(RGB,x^2,y^2,0): plot3d(sin(x*y),x=0..2,y=0..2,color=aa(sin(x+y),cos(x-y))); .plot3d light. ambientlight=[r,g,b] . light=[phi,theta,r,g,b] θ , > > > > , , φ, θ . r,g,b . , . φ , ambientlight , . . p, display p:=plot3d([sin(s)*cos(t),cos(s)*cos(t),sin(t)], s=0..Pi,t=0..2*Pi,scaling=constrained,color=white, orientation=[38,75],style=patch): display(p); 130 > display(p,light=[60,45,1,1,1]); light ambientlight > display(p,light=[60,45,1,1,1],ambientlight=[0 .3,0.3,0.3]); . > display(p,light=[60,45,1,0,0],light=[60,-45,0 ,1,0],light=[135,0,0,0,1]); 6.3 131 6.3 , . Maple , ,animate animate3d, , plots , . . animate. . > > :animate(y-expr,x=range,t=range). with(plots): animate(sin(x*t),x=-10..10,t=1..2); , ( , ) 16 . ( ) , , Maple , Play . > animate(sin(x*t),x=-10..10,t=1..2,frames=50); . > animate([a*cos(u),sin(u),u=0..2*Pi],a=0..2); animate . coords > animate( theta*t,theta=0..8*Pi,t=1..4,coords=polar); , Maple , . , . . , , > > > > > for i from 0 to 15 do t:=1+i/16; p.i:=plot(sin(x*t),x=-10..10,axes=none): od: display(array(1..4,1..4,[[p0,p1,p2,p3],[p4,p5,p6,p7], 132 > [p8,p9,p10,p11],[p12,p13,p14,p15]])); animate3d . > ,animate3d : animate3d(z-expr,x=range,y=range,time=range) animate3d(cos(t*x)*sin(t*y),x=-Pi..Pi,y=-Pi..Pi,t=1..2); , 8 , . , frame . > , animate3d(cos(t*x)*sin(t*y),x=-Pi..Pi,y=-Pi..Pi,t=1..2,frames=16); . > > animate3d([s*time,t-time,s*cos(t*time)], s=1..3,t=1..4,time=2..4,style=patch,axes=boxed); , coords . , coords=spherical. > > animate3d((1.3)^theta*sin(t*phi),theta=-1..2*Pi, phi=0..Pi,t=1..8,coords=spherical); , coords=cylindrical. > > animate3d(sin(theta)*cos(z*t),theta=1..3,z=1..4, t=1/4..2/7,coords=cylindrical); Maple , ?plots, changecoords. . display 6.4 . , , > > > > > > > > > 133 10 . . insequence=true display 10 display with(plots): data:=: for i from 1 to 10 do phi:=2*Pi/10*i: data:=[op(data), tubeplot([-t^1.5/5,3*cos(t+phi),3*sin(t+phi)],t=0..8*Pi, radius=t/8,scaling=constrained,grid=[60,15])]: od: display(data,insequence=true,orientation=[107,56]); , . animate3d , . . display display > > > > > a:=animate3d(sin(x+y+t),x=0..2*Pi,y=0..2*Pi,t=1..5,frames=20): b:=plot3d(0,x=0..2*Pi,y=0..2*Pi): c:=animate3d(t,x=0..2*Pi,y=0..2*Pi,t=-1.5..1.5,frames=20): display([a,b]); display([a,c]); , . 6.4 Maple .labels > > , . title , plot(sin(x),x=0..2*Pi,title=‘Sine function‘, labels=[‘x axis‘,‘y axis‘]); Sine function 1 0.5 y axis 0 -0.5 1 2 3 x axis 4 5 6 -1 , . > > , labels axes=framed plot3d(sin(x*y),x=-1..1,y=-1..1,axes=framed, labels=[‘length‘,‘width‘,‘height‘]); 134 0.8 0.6 0.4 0.2 height 0 -0.2 -0.4 -0.6 -0.8 -1 -0.5 width0 -0.5 0 length -1 0.5 11 0.5 textplot align RIGHT, . > > > , .align ABOVE, BELOW. textplot , , [x,y,‘text‘]. LEFT, display p1:=plot(sin(x),x=0..2*Pi): p2:=textplot([3*Pi/2,0.5,‘sin(x)‘]): display([p1,p2]); 1 0.5 sin(x) 0 1 2 3x 4 5 6 -0.5 -1 , textplot3d font, titlefont, axesfont , labelfont textplot . . labels titlefont ,font , ,axesfont textplot [font,style,size]. , font ,labelfont . ,style , font style. COURIER style > > > ,size , point. SYMBOL. SYMBOL . TIMES . COURIER, HELVETICA, TIMES style BOLD, OBLIQUE . BOLDITALIC HELVETICA BOLDOBLIQUE ROMAN, BOLD, ITALIC display([p1,p2],title=‘Sine function‘, font=[TIMES, ITALIC, 40],axesfont=[COURIER, OBLIQUE, 20], titlefont=[HELVETICA, BOLD, 50]); 6.5 135 1 Sine function sin(x) 1 2 3 0.5 0 x 4 5 6 -0.5 -1 6.5 , plots . , : , , ex > > 2 y , . logplot ex , e3x+1 y . with(plots): logplot({exp(x),exp(x^2),exp(3*x+1)},x=0.1..10,1..10^6); 1e+06 1e+05 10000. 1000. 100. 10. 1. 2 4 x 6 8 10 logplot sample > > > > y x . ,loglogplot . : , . . s:=I*omega: f:=(1+s*2/5)*(1+s/9)/((1+s)*(1+s/99)): loglogplot(abs(f),omega=0.01..10000,axes=boxed, sample=[seq(10^(n/10),n=-20..50)]); 136 4. 2. 1. .7000000000 .1000000000 1. 10. omega 100. 1000. 10000. , . > > semilogplot , logplot , x ,y semilogplot(argument(f)*180/Pi,omega=0.01..10000,axes=boxed, sample=[seq(10^(n/10),n=-20..50)]); 50 40 30 20 10 0 -10 .1000000000e-1 .1000000000 1. 10. omega 100. 1000. 10000. contourplot densityplot > > §6.2 , z = f (x, y ) . . . , densityplot f:=(x-1)*(x+2)*(y-2)*(y+2): densityplot(f,x=-3..3,y=-3..3,axes=boxed); 3 2 1 y0 -1 -2 -3 -3 -2 -1 0 x 1 2 3 , contourplot . . z = f (x, y ) , f (x, y ) 6.5 > 137 contourplot(f,x=-3..3,y=-3..3); 3 2 y 1 0 -1 -2 -3 -3 -2 -1 1 x 2 3 ,contour . > > contourplot(f,x=-3..3,y=-3..3,grid=[30,30],contours=25, color=black,axes=boxed); 3 2 1 y0 -1 -2 -3 -3 -2 -1 0 x 1 2 3 filled true, , . .coloring=[white,black] > > contourplot(f,x=-3..3,y=-3..3,axes=boxed,filled=true, coloring=[white,black]); 3 2 1 y0 -1 -2 -3 -3 -2 -1 0 x 1 2 3 contourplot contourplot3d. . . shading=none > > contourplot3d(f,x=-3..3,y=-3..3,axes=boxed,filled=true, coloring=[white,black],shading=none); 138 40 20 0 -20 -40 -3 -2 -2 -3 -1 0 y 1 2 33 2 1 0 x -1 contourplot3d . > > > > [f x, f y, f z ] , , fz contourplot3d , . s:=’s’: contourplot3d([t*sin(s),t*cos(s),sin(s*t)],s=0..2*Pi,t=0..2, contours=20,filled=true,coloring=[white,grey], shading=none,orientation=[0,0]); Maple . > filledcontourplot, . contourplot with(share): See ?share and ?share,contents for information about the share library > > > readshare(fconplot,plots): filledcontourplot(f,x=-3..3,y=-3..3,coloring=[black,white], levels=10,axes=boxed); 3 2 1 y0 -1 -2 -3 -3 -2 -1 0 x 1 2 3 6.5 139 , . . , conformal complexplot complexplot . > , plot3d . . , x, y ( ) . , . complexplot, complexplot3d plot . tan complexplot(tan(x*(2+I)),x=-Pi..Pi); 1.5 1 0.5 -0.8 -0.6 -0.4 -0.2 0 -0.5 -1 -1.5 0.2 0.4 0.6 0.8 style=point > . solve(z^12=1); 2−2I √ 1 3, − 2 2−2I √ 3, 1 1√ 1 1√1 1√ 1 1 1√ 1, − + I 3, − − I 3, −1, − I 3, + I 3, I, −I, 22 22 22 22 2 √ √ 1 1 2 + 2 I 3, − 2+2I 3 2 2 > complexplot([%],style=point,axes=boxed); 1 0.5 0 -0.5 -1 -1 -0.5 0 0.5 1 , plot3d , color complexplot3d . , ,complexplot3d 140 . . > > complexplot3d(sin(z),z=-I..2*Pi+I,orientation=[-70,21], axes=boxed,style=patch,grid=[40,40]); 1 1.5 1 0.5 0 0 0.5 0y 1 2 3 x -0.5 4 5 6 -1 complexplot3d . > > , z , f:=sin(z): complexplot3d([Re(f),abs(f)],z=0..Pi+I,style=patch,axes=boxed); 1.4 1.2 1 0.8 0.6 0.4 0.2 0 0 0.2 0.4 y 0.6 0.8 1 3 2.5 2 1.5 x 1 0.5 0 Newton (N (z ) = z − p(z )/(p (z ) + sI )). > ,Newton . p:=(z-1)*(z^2+z+5/4); 5 p := (z − 1) (z 2 + z + ) 4 > f:=unapply(z-p/(diff(p,z)-0.5*I),z); 5 (z − 1) (z 2 + z + ) 4 f := z → z − 5 2+z+ z + (z − 1) (2 z + 1) − .5 I 4 f@@4 . > > f (f (f (f (z )))), f . Maple f@@4 f (f (f (f (z )))) complexplot3d(f@@4,-4-4*I..4+4*I,view=-1..2,style=patchnogrid, grid=[100,100]); 6.5 141 conformal . , . conformal . > . , , , ,Maple f (z ) = z , conformal(z,z=-1-I..2+I,axes=boxed); 1 0.5 0 -0.5 -1 -1 -0.5 0 0.5 1 1.5 2 1/z . > . conformal conformal(1/z,z=-1-I..2+I,-4-3*I..4+3*I,grid=[50,50],axes=boxed,view=[-4..4,-3..3]); 3 2 1 0 -1 -2 -3 -2 0 2 4 (z + 1 + I )3 > . conformal((z+1+I)^3,z=-I*2..2+2*I,grid=[40,40 ],axes=frame,scaling=constrained); 142 50 40 30 20 10 0 -10 -20 -50 -40 -30 -20 -10 0 10 20 plots . , • feasible region: inequal .inequal , , . . : . . < ≤ > ≥ . . optionclose • closed lines: . > > > > > • open lines: • excluded regions: optionfeasible, optionexcluded, optionopen optionname=(expression sequence of plot option). with(plots): inequal({x+2*y>=-1,x-y<=1,y-x/3<4},x=-6..8,y=-2..7, optionsfeasible=(color=grey),optionsexcluded=(color=white), optionsclosed=(thickness=3), optionsopen=(linestyle=3,thickness=1),axes=boxed); 6 4 2 0 -2 -6 -4 -2 0 2 4 6 8 6.1 implicitplot3d. z+ . x2 + y 2 − sin(y 2 + z 3 ) = 0 > implicitplot3d(z+sqrt(x^2+y^2)-sin(y^2+z^3)=0, 6.5 > > 143 x=-2..2,y=-2..2,z=-2..2,grid=[20,20,10], orientation=[18,83],style=wireframe,color=black); Maple : (dodecahedron) polyhedraplot(L,option). (tetrahedron), L (icosahedron). .polyhedraplot (octahedron), polyhedraplot (hexahedron), . [[x1 , y1 , z1 ], [x2 , y2 , z2 ], · · · , [xn , yn , zn ]] , polytype, . > > > > > . .polytype . polyscale tetrahedron polys:=array([tetrahedron,octahedron,hexahedron, dodecahedron,icosahedron]): seq(polyhedraplot([2.5*n^1.6,n,n/3],polytype=polys[n], polyscale=1.5+n*0.7),n=1..5): display([%],scaling=constrained,orientation=[-126,56]); 第七章 微分与积分 在 Maple 中有许多命令可以用来解决微积分的问题. 在这一章中, 我们介绍其中的一小部 分命令. 并讨论一些典型的应用. 7.1 极限 极限是微积分中的基本概念之一,Maple 用命令 limit(f(x),x=x0) 求函数 f (x) 在 x = x0 点的极限. 其中 f (x) 既可以是 x 的函数, 也可以是表达式. 例如 > f:=x->(sqrt(1+x)-1)/x; f := x → limit(f(x),x=0); √ x+1−1 x > 1 2 > limit(tan(Pi/4+x)^cot(2*x),x=0); e limit不仅能求出趋进于有限点的极限, 也可以处理 x 趋于无穷时的极限. 对于 ∞ ∞ 和 0 0 的情况 通常也能得到正确的结果. > limit(x!/x^x,x=infinity); 0 > limit(sin(x)/x,x=0); 1 如果表达式中还有其它未知量,Maple 可以用符号的形式求极限, 前提条件是这些未知量对 于极限的结果没有影响. 例如 > limit((x^a-x^(-a))/(x-x^(-1)),x=1); a 如果未知量对极限的结果有影响, 情况就不同了. 例如 > limit(arctan(h*x/sqrt((z*r)^2-(x*h)^2)), x=z*r/h); hx lim arctan( √ ) 2 r 2 − x2 h2 zr z x→( ) h 144 7.1 极限 145 在上式中有三个未知量 z, r, h, 此时 Maple 不知道如何求极限. 解决这个问题的方法是对未知量 做某些假设. > > > assume(z*r>0); assume(h>0); limit(arctan(h*x/sqrt((z*r)^2-(x*h)^2)), x=z*r/h); undefined 我们已经对三个未知量做了假设, 但是极限的结果依然是 undefined, 问题出在哪里? 仔细观察 以下我们的表达式可以发现: 这个极限从左、右两个方向求出的结果是不同的, 因此指定求极 限的方向就可以得到正确的结果. 具体方法是 > limit(arctan(h*x/sqrt((z*r)^2-(x*h)^2)), x=z*r/h,left); 1 π 2 limit(arctan(h*x/sqrt((z*r)^2-(x*h)^2)), x=z*r/h,right); 1 −π 2 hx (zr )2 −(xh)2 > 从上面的计算我们可以看出从左、右两个方向求出的极限是不同的, 因此函数 √ x= zr h 在 点是不连续的. limit 用第三个参数控制求极限的方向, 在省缺情况下 limit 从两个方向求极限, 如果把 第三个参数指定为 left 或 right, limit 将从左边或右边求极限. 方向参数可能的取值还有 complex, 它的含义是在复平面上从任意方向求极限. 例如 > f:=z->z^2/(z-2-I); f := z → z2 z−2−I > limit(f(z),z=2+I,complex); ∞ limit还可以对多个变量求极限, 例如 > limit(x+1/y,{x=0,y=infinity}); 0 虽然 limit 的能力非常强, 但是对于一些比较困难的问题它也无能为力. 例如我们要求下 列级数的极限 n k2 3 lim 1+ 3 −1 n→∞ n k=1 > limit(Sum((1+k^2/n^3)^(1/3)-1,k=1..n),n=infinity); n n→∞ lim ((1 + k=1 k 2 1 /3 ) − 1) n3 146 第七章 微分与积分 对于这类问题, 我们的解决方法是使用 Maple 的数值计算功能求出一个近似值, 进而猜测正确 的结果. 然后再用其它数学方法去证明你的结论. > L:=[seq(10^n,n=1..5)]; L := [10, 100, 1000, 10000, 100000] > evalf(seq(Sum((1+k^2/n^3)^(1/3)-1,k=1..n),n=L )); .1256341969, .1125564268, .1112555644, .1111255556, .1111125556 由此我们猜测 n n→∞ lim 3 1+ k=1 k2 1 −1= 3 n 9 如何证明这个结果不是本书的课题, 请读者自己完成. 对于一个函数 f (x), 我们可以通过从左右两个方向计算极限来判断 f 在某点是否连续. 在 Maple 中, 还提供了两个函数 iscont, discont 来判断函数的连续性. 其中 iscont 可以判断函 数在一个区间中是否连续, discont 命令则能在实数范围内求出函数的不连续点. 要使用 iscont 命令, 必须用 readlib 将其调入. 通常 iscont 可以判断一个函数在开区间 上是否连续. 当函数连续时, 它就返回 true. > > readlib(iscont): iscont(1/(1-x),x=0..1); true 1 很显然 1−x 在区间的端点 1 是不连续的, 此时我们可以用 iscont 的第三个参数 ‘closed‘ 来 指定 iscont 在闭区间内判断函数的连续性. 于是我们有 > iscont(1/(1-x),x=0..1,‘closed‘); false 当函数中有某些无法确定的参数时,iscont 可能无法确定函数是否连续, 此时 iscont 返回的值 为 FAIL, 它代表结果不确定, 或判定失败. 例如 > iscont(1/(x-a),x=0..1); FAIL discont命令可以求出函数在实数范围内可能的不连续点. 它也需要用 readlib 调入. > > readlib(discont): discont(tan(x),x); {π Z1 ˜ + 1 π} 2 > discont(GAMMA(x/2),x); {−2 NN2 ˜} 其中 Z1~ 代表整数, NN2~ 代表非负整数. 7.2 微分 147 7.2 况. 微分 在 Maple 中计算微分的命令有三个, 分别为 diff, implicitdiff, D. 它们适用于不同的情 diff 主要用于对表达式求微分. 通常的用法是 diff(expr,var), 其中 var 是要求微分的 变量. 例如: > diff(x^5-4*x^4+3*sin(x)^2*tan(x^2)+3,x); 5 x4 − 16 x3 + 6 sin(x) tan(x2 ) cos(x) + 6 sin(x)2 (1 + tan(x2 )2 ) x > diff(a*exp(4*x^2-sin(x))+b*x,x); a (8 x − cos(x)) e(4 x 2 −sin(x)) +b > diff(sin(cos(x)),x); −cos(cos(x)) sin(x) 在表达式中可以包含一些未知的量. 也可以有未知的函数, 例如 > diff(g(f(x)),x); D(g )(f(x)) ( ∂ f(x)) ∂x 此时 Maple 把微分算子 D 作用到未知的函数 g 上. 在 diff 的变量部分可以指定多个变量, 此时 diff 将对不同的变量求偏导数. > diff((x^2+y^2)^4,x,y); 48 (x2 + y 2 )2 x y 如果要对一个变量多次求导, 可以使用 x$n 的方式, 它实际上是一种缩写的形式.n 代表变量 x 重复的次数. > diff(sin(x)^5,x$3); 60 sin(x)2 cos(x)3 − 65 sin(x)4 cos(x) implicitdiff命令主要用于对隐函数求微分, 通常隐函数是由一个方程 f (x, y ) = 0 确定的. 使用的方法是 implicitdiff(f,y,x), 它求出 > dy dx . 例如 implicitdiff(x^2+y^2=1,y,x); − x y 在上面的例子中,implicitdiff 认为 y 是 x 的函数. 不过在更复杂的情况下, 应当指定这中依 赖的关系. 通常 implicitdiff 的第一个参数是一组方程, 第二个参数用 y (x) 的方式定义了变 量之间的依赖关系. 第三个参数说明要求导的对象, 第四个参数指定了求导的变量. > > f:=x^2+y^2=1: implicitdiff({f},{y(x)},{y},x); x {D(y ) = − } y 148 第七章 微分与积分 选项 notation=Diff 则使结果以更清楚的方式显示. > implicitdiff({f},{y(x)},{y},x,notation=Diff); ∂ x { y=− } ∂x y 在下面的例子中 z 是由方程 x3 y 2 z 2 = 1 定义的, 我们用 implicitdiff 求出二次偏导数 ∂ ∂x∂y z 2 > > f:=x^3*y^2*z^2=1: implicitdiff({f},{z(x,y)},{z},x,y,notation=Diff); ∂2 3z { z= } ∂y ∂x 2 xy D是一个函数算子, 它作用于函数, 而不是表达式, 这是它与 diff 的主要区别. 例如 > D(sin); cos > f:=x->x^2+3*x; f := x → x2 + 3 x > D(f); x → 2x + 3 如果没有额外的参数,D 只适用于求单变量函数的微分. 在下面的例子中, [email protected] 的含义是复 合函数 sin(cos(x)). > D([email protected]); −cos(2) sin 对于二元和多元函数, 可以用 D[n](f) 的方式指定对第 n 个变量求微分. 例如下面的 D[1](f) 就是对函数 f (x, y ) 的第一个变量 x 求微分. 同样 D[2](f) 表示对第二个变量 y 求 微分. > f:=(x,y)->sin(x)^2*cos(y)+sin(x*y^2); f := (x, y ) → sin(x)2 cos(y ) + sin(x y 2 ) > D[1](f); (x, y ) → 2 sin(x) cos(y ) cos(x) + cos(x y 2 ) y 2 > D[2](f); (x, y ) → −sin(x)2 sin(y ) + 2 cos(x y 2 ) x y 最简捷的求多次导数的方法是 D[n1$m1,n2$m2,...](f). 这里对函数 f 的第 n1 个变量求 导了 m1 次, 对 f 的第 n2 个变量求导了 m2 次. > D[2](f)(alpha,beta); −sin(α)2 sin(β ) + 2 cos(α β 2 ) α β 7.2 微分 > 149 D[2$2](f); (x, y ) → −sin(x)2 cos(y ) − 4 sin(x y 2 ) x2 y 2 + 2 cos(x y 2 ) x 下面两个例子完成的是同样的工作, 都是对函数 f (x, y ) 关于 x 求三次导数, 对 y 求四次导数. 它们的主要区别是: 使用 D 算子得到的是关于 (x, y ) 的函数, 使用 diff 得到的是一个包含变量 x, y 的表达式. 对于表达式使用 unapply 命令就可以转换成函数的形式. > D[1$3,2$4](f); (x, y ) → −8 sin(x) cos(y ) cos(x) − 16 cos(x y 2 ) y 10 x4 − 240 sin(x y 2 ) y 8 x3 + 1020 cos(x y 2 ) y 6 x2 + 1320 sin(x y 2 ) x y 4 − 360 cos(x y 2 ) y 2 > diff(f(x,y),x$3,y$4); −8 sin(x) cos(y ) cos(x) − 16 cos(x y 2 ) x4 y 10 − 240 sin(x y 2 ) x3 y 8 + 1020 cos(x y 2 ) x2 y 6 + 1320 sin(x y 2 ) x y 4 − 360 cos(x y 2 ) y 2 此外使用 convert 命令也可以在 D 和 diff 两种形式之间进行转换. > > restart; diff(f(x,y,z),x,y,z$2); ∂z 2 ∂4 f(x, y, z ) ∂y ∂x > convert(%,D); D1, 2, 3, 3 (f )(x, y, z ) > lprint(%); D[1,2,3,3](f)(x,y,z) > convert(%,diff); ∂4 f(x, y, z ) ∂z 2 ∂y ∂x > lprint(%); diff(diff(diff(diff(f(x,y,z),x),y),z),z) 在上面的例子中,lprint 命令的作用是显示出表达式的内部结构. 对于特别复杂的函数, 我们可以用过程来定义. 微分算子 D 也可以作用到过程上得到一个 新的过程. > > > > > f:=proc(x) local t0,t1,t2; t0:=sin(x): t1:=cos(x)^2: t2:=(t0^2+t1+2)/(t0^3-t1); 150 > > > 第七章 微分与积分 sin(t2^2+1)*cos(t2^2-1); end: f(x); sin( (sin(x)2 + cos(x)2 + 2)2 (sin(x)2 + cos(x)2 + 2)2 + 1) cos( − 1) (sin(x)3 − cos(x)2 )2 (sin(x)3 − cos(x)2 )2 > fd1:=diff(f(x),x); cos( fd1 := −2 sin( +2 %2 %1 2 2 2 2 + 1) %2 (3 sin(x)2 cos(x) + 2 sin(x) cos(x)) cos( %1 3 2 %2 %1 2 2 − 1) %2 %1 + 1) sin( 3 %2 %1 2 2 2 − 1) %2 (3 sin(x)2 cos(x) + 2 sin(x) cos(x)) %1 3 2 %1 := sin(x) − cos(x) %2 := sin(x)2 + cos(x)2 + 2 > fd2:=D(f); fd2 := proc(x) localt0 , t1 , t2 , t1x , t0x , t2x ; t0x := cos(x) ; t0 := sin(x) ; t1x := −2 ∗ cos(x) ∗ sin(x) ; t1 := cos(x)2 ; t2x := (2 ∗ t0 ∗ t0x + t1x )/(t0 3 − t1 ) − (t0 2 + t1 + 2) ∗ (3 ∗ t0 2 ∗ t0x − t1x )/(t0 3 − t1 )2 ; t2 := (t0 2 + t1 + 2)/(t0 3 − t1 ) ; 2 ∗ cos(t2 2 + 1) ∗ t2 ∗ t2x ∗ cos(t2 2 − 1) − 2 ∗ sin(t2 2 + 1) ∗ sin(t2 2 − 1) ∗ t2 ∗ t2x end > simplify(fd1-fd2(x)); 0 下面我们用 student 程序包中的一些命令来演示一下导数的几何意义. 从导数的定义我们 知道函数 f (x) 在 x0 点的导数是过 x0 切线的斜率. 我们首先来定义一个函数 f (x) = esin(x) . > > with(student): f:=x->exp(sin(x)); f := x → esin(x) 我们要求 f (x) 在 x0 = 1 点的导数值. > x0:=1; x0 := 1 7.2 微分 151 首先在曲线上取两个点 p0 = (x0 , f (x0 )), p1 = (x0 + h, f (x0 + h)). > p0:=[x0,f(x0)]; p0 := [1, esin(1) ] > p1:=[x0+h,f(x0+h)]; p1 := [1 + h, esin(1+h) ] 然后我们用 slope 命令求过 p0 , p1 点的割线的斜率. > m:=slope(p0,p1); m := − esin(1) − esin(1+h) h 当 h → 0 时, 斜率的极限就应当是过 x0 的切线的斜率. 为此我们给出 h 的一系列值, 先来 观察一些斜率的变化趋势. > h_value:=[seq(1/i^2,i=1..20)]; h value := 111 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [1, , , , , , , , , , , , , , , , , , , ] 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 > seq(evalf(m),h=h_value); .162800903, 1.053234750, 1.17430578, 1.21091762, 1.22680697, 1.23515485, 1.2400915, 1.2432565, 1.2454086, 1.2469391, 1.2480669, 1.2489216, 1.2495855, 1.2501111, 1.2505343, 1.2508805, 1.2511671, 1.2514069, 1.2516098, 1.2517828 从这些数值的变化趋势可以看出它们似乎收敛. 求一下 h → 0 的极限, 我们得到 > limit(m,h=0); esin(1) cos(1) > evalf(%); 1.253380768 这个答案当然是 f (x0 ) 的值, 为了验证这一点, 我们可以求出 f (x) 的导函数, 并计算它在 x0 的值. > f1:=D(f); f1 := x → cos(x) esin(x) > f1(x0); esin(1) cos(1) 152 第七章 微分与积分 上面的计算仅仅是数值计算的结果, 下面我们用几何的图形来演示这个事实. 首先我们求出割线的方程. > y-p0[2]=m*(x-p0[1]); y − esin(1) = − (esin(1) − esin(1+h) ) (x − 1) h 使用 isolate 命令把方程转换成斜截式. > isolate(%,y); y=− (esin(1) − esin(1+h) ) (x − 1) + esin(1) h 我们还需要把这个方程转换成函数. > secant:=unapply(rhs(%),x); secant := x → − (esin(1) − esin(1+h) ) (x − 1) + esin(1) h 下面我们可以对不同的 h 值做割线和函数的图形, 我们做一系列的图形, 并用割线的斜率作为 其标题, 然后用程序包 plots 中的 display 命令依次显示这些图象即可作成一个动画. > > > S:=seq(plot([f(x),secant(x)],x=0..4,view=[0..4,0..4], title=convert(evalf(m),string)),h=h_value): plots[display](S,insequence=true,view=[0..4,0..4]); 使用 showtangent 命令可以直接显示出 f (x) 的过 x0 = 1 点的切线. > showtangent(f(x),x=1,view=[0..4,0..4]); 4 3 2 1 -10 -5 0 5 x 10 7.3 积分 对一个表达式求微分是一个相对简单的过程, 但是它的反运算: 求一个表达式的积分则是 一个非常困难的问题. 从数学上讲, 没有一个统一的算法求表达式的积分. 当我们手工求积分 时, 通常的做法是将积分转化为某种特殊的类型, 然后查积分表解决. 当要积分的表达式不能归 到已知的类时, 就需要采用各种特殊的方法来处理, 即使如此依然有许多表达式无法求出积分. 在 Maple 中, 求积分的主要算法是 Risch 算法, 这种算法可以统一处理有理函数的积分问题. 7.3 积分 153 符号积分 Maple 用 int 命令求不定积分和定积分. 具体的用法是: > int(x^3+x^2,x); 14 13 x+ x 4 3 > int(1/(1+x^3),x); √ 1 1 1 1√ 3 arctan( (2 x − 1) 3) ln(1 + x) − ln(x2 − x + 1) + 3 6 3 3 如果我们要求定积分, 只需要给出积分的区域即可. > int(x^2+x^5,x=a..b); 13 16 13 16 b+ b− a− a 3 6 3 6 Maple 也可以计算无限区间的定积分 > int(1/x^2,x=1..infinity); 1 对于某些特殊的表达式, 用 Maple 求出的积分是特殊函数, 例如 > int(sin(x)/x,x=0..Pi); Si(π ) 此时可以用 evalf 命令求出它的浮点值. > evalf(%); 1.851937052 在用 Maple 求积分的过程中, 经常会出现一些特殊函数, 最常见的特殊函数是椭圆函数, 下 面的表格列出了一些常见的椭圆积分. Maple 中的椭圆积分 函数定义 1 √ dt (1−t2 )(1−k2 t2 ) √ x 1−k2 t2 √ dt 0 1−t2 1 √ dt (1−at2 ) (1−t2 )(1−k2 t2 ) 1 1 √ dt 0 (1−t2 )(1−k2 t2 ) √ 2 t2 1 1−k √ dt 0 1−t2 1 √ dt (1−at2 ) (1−t2 )(1−k2 t2 ) 1 1 √ dt 0 (1−t2 )(1−c2 t2 ) √ 1 1−c2 t2 √ dt 0 1−t2 1 √ dt (1−at2 ) (1−t2 )(1−c2 t2 ) x 0 Maple 函数 LegendreF(x, k ) LegendreEx, k LegendrePi(x, a, k ) LegenreKc(k ) LegendreEc(k ) LegenrePic(a, k ) LegendreKc1(k ) LegendreEc1(k ) LegendrePic1(a, k ) x 0 1 0 其中 0 < k < 1, c = √ 1 0 1 − k2 . 154 第七章 微分与积分 数值积分 在前面的例子中, 我们看到许多表达式的积分是无法用符号方法求解, 此时我们不得不借 助于数值积分. 在 Maple 中, 求数值积分的方法是使用 evalf 函数. 需要说明的是, 用 evalf 求 数值积分有两种用法. 一种是 evalf(int(...)), 它先符号的求积分, 然后再进行数值计算. 另 一种方法是 evalf(Int(...)), 它使用了积分的占位形式 Int. 此时 Maple 只进行纯粹的数值 计算. 用这两种方法得到的结果是相同的. Maple 在计算数值积分时有三种方法可以选择, 这可以通过指定选项来实现. 这三种方法 为: 1. Clenshaw-Curtis 算法, 这是省缺的算法. 对应的选项为 CCquad; 2. Double-exponential 算法. 对应的选项为 Dexp; 3. Newton-Cote 算法. 对应的选项为 NCrule. 如果我们不指定用那种算法,Maple 通常会选择第一种算法, 但是当收敛的速度很慢时 (特别 是接近奇点的时候),Maple 会采用第二种算法. 第三种算法通常用于精度比较低的情况 (Digits<=15). 具体用法如下: > evalf(Int(1/sqrt(x),x=0..1,20,_Dexp)); 1.9999999999987781927 evalf(Int(x/sin(x),x=0..1,10,_NCrule)); 1.059762793 > 重积分 对于重积分,Maple 没有特殊的算法, 它的处理方法也是把重积分化成一重积分进行计算. 例如: > int(int(x^2+y^2,y=1..x^2),x=1..2); 1006 105 在 student 程序包中有一个重积分的占位形式命令 Doubleint, 它可以产生重积分的符号, 但是对于复杂的重积分计算并没有什么帮助. > > with(student): Doubleint(x^2+y^2,y=1..x^2,x=1..2); 2 1 1 x2 x2 + y 2 dy dx > %=value(%); 2 1 1 x2 x2 + y 2 dy dx = 1006 105 7.3 积分 155 最后我们看一个三重积分的例子, 它来自于量子物理. > > > > assume(P>0):assume(S>0): p1:=(x^2+y^2+z^2)/P^2: p2:=subs(x=x-a,y=y-b,z=z-c,P=S,p1): f:=(2*x^2-P^2)*exp(-p1-p2); f := (2 x2 − P ˜2 ) e(− x2 +y 2 +z 2 P ˜2 − (x−a)2 +(y −b)2 +(z −c)2 S ˜2 ) > int(int(int(f,x=-infinity..infinity), y=-infinity..infinity),z=-infinity..infinity); e (− c 2 +b2 +a2 S ˜2 +P ˜2 ) P ˜5 S ˜3 (2 a2 P ˜2 − S ˜4 − 2 P ˜2 S ˜2 − P ˜4 ) π 3/2 (S ˜2 + P ˜2 )7/2 P ˜5 S ˜5 π 3/2 (S ˜2 + P ˜2 )5/2 ) + > e (− c 2 +b2 +a2 S ˜2 +P ˜2 factor(%); e (− c 2 +b2 +a2 S ˜2 +P ˜2 ) P ˜7 S ˜3 π 3/2 (2 a2 − S ˜2 − P ˜2 ) (S ˜2 + P ˜2 )7/2 复函数的积分 对于在复平面上没有奇点的函数, 计算路径积分是没有任何问题的. 例如 > int(z^3,z=0..2+3*I); − 119 − 30 I 4 > int(z*cos(z^2),z=0..Pi*I); 1 − sin(π 2 ) 2 1 然而我们经常需要计算环绕奇点的闭路径积分, 例如计算复函数 z 环绕 z = 0 点的路径积 分. 通常的做法是将 z 代换为 z0 + r ∗ (cos(t) + I sin(t)), 然后对实变量 t 计算 0 到 2π 的积分. 在计算过程中还要特别注意不要忘记 z 对 t 的导数 dz(t) . 最后结果的符号依赖于积分的方向. dt 2π f (z )dz = C 0 f (z (t)) dz (t) dt dt z (t) = z0 + r(cos(t) + I sin(t)) 下面我们就按照上面的步骤在 Maple 中的计算: > z:=cos(t)+I*sin(t); z := cos(t) + I sin(t) > dz:=diff(z,t); dz := −sin(t) + I cos(t) > f:=1/z; f := 1 cos(t) + I sin(t) 156 > 第七章 微分与积分 int(f*dz,t=0..2*Pi); 0 直接的计算得到了一个错误的结果, 把 f dz 先化简之后在计算才能得到正确的结果. > simplify(f*dz); I > int(%,t=0..2*Pi); 2I π 为什么会出现错误? 我们先求一下符号积分就能发现问题 > i1:=int(f*dz,t); i1 := ln(cos(t) + I sin(t)) Maple 求出的 f dz 的积分为 ln(cos(t) + I sin(t)). 由于三角函数的周期性,t = 0 和 t = 2π 的到相 同的值. 因此定积分的值为零. 下面我们将三角函数转化为指数函数, 再化简可以去掉周期性. 我们才能得到正确的结果. > convert(i1,exp); ln(e(I t) ) > i2:=simplify(%); i2 := I t > subs(t=2*Pi,i2)-subs(t=0,i2); 2I π 下面我们再看一个例子: 计算 > sin(z ) z4 的路径积分. 使用刚才的做法同样会得到错误的答案. sin(cos(t) + I sin(t)) (cos(t) + I sin(t))4 f:=sin(z)/z^4; f := > int(f*dz,t=0..2*Pi); 0 改变以下积分的上下限就能得到正确的结果 > int(f*dz,t=-Pi..Pi); 1 − Iπ 3 为了发现问题, 我们再一次计算符号积分 > i1:=int((f*dz),t); i1 := − 1 sin(%1) 1 cos(%1) 1 sin(%1) 1 − + − Ci(%1) 3 %13 6 %12 6 %1 6 %1 := cos(t) + I sin(t) 7.3 积分 157 对于这个结果, 再想使用化简的方法来消去周期函数是不可能的. 要想解决这类问题, 最保险的 做法是使用留数规则. k f (z )dz = 2πI C j =1 Resz=zj f (z ) 1 z 一个函数的留数是函数在奇点的 Laurent 级数展开中 数的方法是使用 residue 命令. > 或 1 z −z0 项的系数. 在 Maple 中计算留 readlib(residue); proc(f, a) . . . end 为了使用这个命令, 我们需要将它调入. 此外我们还需要将 z 的定义取消, 并重新定义函数 f . > z:=’z’; z := z > f:=sin(z)/z^4; f := sin(z ) z4 根据留数公式我们现在可以得到正确的结果. > 2*Pi*I*residue(f,z=0); 1 − Iπ 3 最后我们计算 48z−22zz2−zz3 的路径积分, 路径为以 0 为圆心半径为 3/2 的圆. 解决这个问 −5 + 的第一步是确定在积分区域中的奇点. > 2 f:=(-z^2-22*z+8)/(z^3-5*z^2+4*z); −z 2 − 22 z + 8 f := 3 z − 5 z2 + 4 z solve(denom(f)); 0, 1, 4 > 使用 solve 命令可以求出在积分区域中的奇点有 z = 0 和 z = 1. 利用留数公式计算得到 > 2*Pi*I*(residue(f,z=0)+residue(f,z=1)); 14 I π 观察 Maple 的积分过程 如果我们想知道 Maple 是如何得到它的结果, 通过改变系统变量 infolevel 关于 int 的值 就可以看到. 这个值省缺为 1, 我们可以把它设置为 2 到 5. 值越高, 显示的信息就越多. 例如我 1 们要求 1+ln(x) 的积分, 我们可以把 infolevel[int] 的值设置为 3. 从显示的信息中, 我们可以 看到 Maple 在积分过程中所尝试的各种方法. > > infolevel[int]:=3: int(1/(1+ln(x)),x); 158 第七章 微分与积分 int/indef: first-stage indefinite integration int/indef2: second-stage indefinite integration int/ln: case of integrand containing ln int/rischnorm: enter Risch-Norman integrator int/risch: enter Risch integration int/risch/algebraic1: RootOfs should be algebraic numbers and functions int/risch: the field extensions are [ X , ln( X )] int/risch: Introduce the namings: { th 1 = ln( X )} unknown: integrand is 1 1 + th 1 int/risch/ratpart: integrating 1 1 + th 1 int/risch/ratpart: Hermite reduction yields 1 dX 1 + th 1 int/risch/ratpart: Rothstein’s method - resultant is X −z nonconstant coefficients; integral is not elementary int/risch: exit Risch integration 1 dx 1 + ln(x) 用 Maple 演示定积分的定义 在本章的最后, 我们用 student 程序包中的命令来演示 Riemann 积分的定义. 按照 Riemann 积分的定义, 一个函数在区间 [a, b] 上的定积分是函数 f 的图象与 x 轴所夹的面积. 为了求出这 个面积, 我们可以用一系列矩形来逼近. 使用 student 程序包中的 leftbox 和 rightbox 命令 可以很容易的画出这些矩形的图象. x2 下面我们考察一个函数 f (x) = 1 + sin(x) 在 x = 1 到 2 的定积分. 首先画出函数的图形: > f:=x->1+x^2/sin(x); f := x → 1 + x2 sin(x) 7.3 积分 > 159 plot(f(x),x=1..2); 5 4.5 4 3.5 3 2.5 1 1.2 1.4 x 1.6 1.8 2 使用 leftbox 命令可以画出函数 f 的图象以及一系列矩形, 每个矩形的高度是 f 在该矩 形左边的值. 对于 leftbox 如果不指定矩形的个数, 它会使用省缺值 6. 为了得到更好的逼近, 我们通常要画许多矩形, 下面的图形我们画了 10 个矩形. > leftbox(f(x),x=1..2,10); 5 4 3 2 1 01 1.2 1.4 x 1.6 1.8 2 使用 leftsum 命令可以计算出这些矩形的面积. > leftsum(f(x),x=1..2,10); 9 1 10 i=0 12 i) 10 1 + 1 sin(1 + i) 10 (1 + 这个数就是 > 2 1 f (x)dx 的近似. 它的值如下 evalf(%); 3.270685922 使用 rightbox 命令同样可以画出函数 f 的图象以及一系列矩形, 每个矩形的高度是 f 在 该矩形右边的值. > rightbox(f(x),x=1..2,10); 160 第七章 微分与积分 5 4 3 2 1 01 1.2 1.4 x 1.6 1.8 2 用 rightsum 命令也同样可以求出这些矩形的面积 > rightsum(f(x),x=1..2,10); 1 10 i=1 10 12 i) 10 1 + 1 sin(1 + i) 10 (1 + 面积为 > evalf(%); 3.591746480 从上面的图形可以看出 leftsum 和 rightsum 的值从左右两个方向数值的逼近了定积分 的值. 为了更精确的计算积分, 我们可以画更多的矩形. > 2 1 f (x)dx boxes:=[seq(i^2,i=3..14)]; boxes := [9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196] > seq(evalf(leftsum(f(x),x=1..2,n)),n=boxes); 3.253784917, 3.328451740, 3.363649396, 3.382938595, 3.394627076, 3.402236586, 3.407464212, 3.411208779, 3.413982166, 3.416093167, 3.417736987, 3.419041908 > seq(evalf(rightsum(f(x),x=1..2,n)),n=boxes); 3.610518871, 3.529114589, 3.492073620, 3.472122084, 3.460149640, 3.452402298, 3.447101318, 3.443314834, 3.440516097, 3.438389039, 3.436734653, 3.435422549 我们为 rightbox 图形指定一个标题, 例如用 rightsum 的值作为标题. > > S:=seq(rightbox(f(x),x=1..2,n,title=convert(evalf(rightsum(f(x),x=1..2,n)), string)),n=boxes): 使用 plots 程序包中的 display 命令可以动态的显示这一系列图形, 构成一幅动画图形. > plots[display](S,insequence=true); 这个积分的值为 > int(f(x),x=1..2); 7.4 级数展开 2 161 1+ 1 x2 dx sin(x) > evalf(%); 3.427221819 我们刚才用 leftsum 和 rightsum 计算的值已经非常逼近了积分值. 7.4 级数展开 在微积分的应用中, 我们经常把函数在某点展开成级数, 在这一节中, 我们讨论级数展开的 问题. 在 Maple 中, 最常用的级数展开命令是 taylor. 例如将函数 sin(x) 在 x = 0 点展开成级 数. > taylor(sin(x),x=0); x− 15 13 x+ x + O(x6 ) 6 120 在省缺情况下, 级数展开的阶数为 6, 这是由全局变量 Order 决定的. 在 taylor 命令中的第三 个参数可以指定级数展开的阶数. > taylor(sin(x),x=0,15); 1 15 1 1 1 1 x − x3 + x− x7 + x9 − x11 + x13 + O(x15 ) 6 120 5040 362880 39916800 6227020800 在 Maple 中标准的级数展开命令是 series, 它可以生成 Taylor 级数,Laurent 级数或广义 幂级数. 它的用法和 taylor 命令相同, 但是适用的范围更广. 例如 series 命令可以将复函数 展开. > series(1/(z^2+1),z=I); 1 11 1 1 1 − I (z − I )−1 + + I (z − I ) − (z − I )2 − I (z − I )3 + (z − I )4 + O((z − I )5 ) 2 48 16 32 64 series命令也可以在无穷远点展开级数, 此时 series 调用的是 asympt 命令. 于是命令 series(f,x=infinity) 和 asympt(f,x) 是等价的. > series(arctan(x),x=infinity); 1 11 1 1 11 − + O( 6 ) π− + 2 x 3 x3 5 x5 x asympt(arctan(x),x); 11 1 1 1 11 − + O( 6 ) π− + 3 5 2 x 3x 5x x > 对于使用级数展开命令 series 产生的级数,Maple 以特定的数据结构存储. 数据类型为 series. 由于这个原因, 用 series 生成的级数不能立即做进一步的计算. 例如 > f:=series(log(x),x=1); 1 1 1 1 f := x − 1 − (x − 1)2 + (x − 1)3 − (x − 1)4 + (x − 1)5 + O((x − 1)6 ) 2 3 4 5 162 > 第七章 微分与积分 evalf(subs(x=2,f)); Error, invalid substitution in series > whattype(f); series 如果我们想把级数展开的多项式部分单独处理, 就需要用 convert 命令把级数转换为多项式, 然后对多项式做进一步的计算. > p:=convert(f,polynom); 1 1 1 1 p := x − 1 − (x − 1)2 + (x − 1)3 − (x − 1)4 + (x − 1)5 2 3 4 5 下面的图形显示了 log(x) 的级数展开和原函数在 x = 1 点附近的差别. > plot({p,log(x)},x=0..2); 0 0.5 x 1 1.5 2 -1 -2 -3 在 series 命令中, 我们可以对级数进行其它数学操作, 例如我们分别求出 sin(x) 和 cos(x) 在 x = π/2 的级数展开, 把它们相乘得到的级数与直接把函数 sin(x) cos(x) 展开的级数是相同 的. > s1:=series(sin(x),x=Pi/2); 1 1 1 1 1 s1 := 1 − (x − π )2 + (x − π )4 + O((x − π )6 ) 2 2 24 2 2 s2:=series(cos(x),x=Pi/2); 1 1 1 1 1 1 s2 := −(x − π ) + (x − π )3 − (x − π )5 + O((x − π )6 ) 2 6 2 120 2 2 s3:=series(sin(x)*cos(x),x=Pi/2); 2 1 1 1 2 1 (x − π )5 + O((x − π )6 ) s3 := −(x − π ) + (x − π )3 − 2 3 2 15 2 2 s4:=series(s1*s2,x=Pi/2); 2 1 2 1 1 1 (x − π )5 + O((x − π )6 ) s4 := −(x − π ) + (x − π )3 − 2 3 2 15 2 2 > > > 7.4 级数展开 163 对于已经展开的级数也可以做其它的运算, 不过这样计算的结果不一定准确. 例如对于级数 s1 使用 arcsin 函数得到的结果就是不正确的. > series(arcsin(s1),x=Pi/2); 1 1 1 π − (x − π ) + O((x − π )5 ) 2 2 2 simplify(convert(%,polynom)); π−x > 多变量 Taylor 展开 series虽然可以进行各种级数展开, 但它只能处理单变量的函数. 要想对多变量函数进行 级数展开, 就需要一些特殊命令.mtaylor 可以对多变量的函数进行 Taylor 展开. 由于 mtaylor 不是一个常用的命令, 我们需要用 readlib 命令调入. > > readlib(mtaylor): mtaylor(log(x+2*y),[x=1,y=1]); 1 2 1 2 2 1 x−1+ y− (x − 1)2 − (y − 1) (x − 1) − (y 1)2 + (x − 1)3 3 3 18 9 9 81 2 4 8 1 2 (y − 1) (x − 1)2 + (y − 1)2 (x − 1) + (y − 1)3 − (x − 1)4 − (y − 1) (x − 1)3 27 27 81 324 81 2 8 4 1 (y − 1)2 (x − 1)2 − (y − 1)3 (x − 1) − (y − 1)4 + (x − 1)5 27 81 81 1215 8 16 16 2 (y − 1) (x − 1)4 + (y − 1)2 (x − 1)3 + (y − 1)3 (x − 1)2 + (y − 1)4 (x − 1) 243 243 243 243 32 (y − 1)5 1215 ln(3) + + − + + 与 series 命令不同的是,mtaylor 产生的是一个正常的多项式, 不带高阶项. poisson 命令是 mtaylor 命令的一个变种, 这两个命令的主要区别在于, 当展开的系数是 三角函数时,poisson 命令产生的系数以典范 Fourier 形式出现. 此外 poisson 不能在任意点展 开级数. 下面的例子就是用 poisson 命令和 mtaylor 命令展开函数 sin(a + x) cos(b + y ). 这两个命 令产生的系数形式上是不同的, 不过使用 combine 命令化简后的结果是相同的. > > readlib(poisson): f:=sin(a+x)*cos(b+y); f := sin(a + x) cos(b + y ) > f1:=poisson(f,[x,y],3); f1 := 1 1 1 1 sin(a + b) + sin(a − b) + ( cos(a − b) + cos(a + b)) x 2 2 2 2 1 1 1 1 + (− cos(a − b) + cos(a + b)) y + (− sin(a + b) + sin(a − b)) y x 2 2 2 2 1 1 1 1 2 + (− sin(a + b) − sin(a − b)) y + (− sin(a + b) − sin(a − b)) x2 4 4 4 4 164 > 第七章 微分与积分 f2:=mtaylor(f,[x,y],3); f2 := sin(a) cos(b) − sin(a) sin(b) y + cos(a) x cos(b) − − 1 sin(a) x2 cos(b) − cos(a) x sin(b) y 2 1 sin(a) cos(b) y 2 2 > combine(f1-f2,trig); 0 形式幂级数 series, mtaylor 和 poisson 等命令虽然能将函数展开成任意阶的级数, 但是它不能将级 数写成求和的方式, 也不能给出幂级数系数的一般形式. 在 Maple V Release 4 中, 使用共享库 中的 FormalPowerSeries 命令可以生成形式幂级数. > with(share): See ?share and ?share,contents for information about the share library > > readshare(FPS,analysis): FormalPowerSeries(cos(x),x); ∞ k=0 (−1)k x(2 k) (2 k )! 对于形式幂级数,Maple 提供了一个专用的程序包 powseries. 它提供了许多处理形式幂级 数的命令. 这些命令主要包括: 计算两个形式幂级数的和、 积、 对数和三角函数等. 此外 差、 商、 还包括形式幂级数的微分, 积分等. 其中最基本的两个命令是 powcreate 和 tpsform. powcreate 命令用来定义形式幂级数,tpsform 命令用来将形式幂级数转换为普通的幂级数的形式. > with(powseries); [compose , evalpow , inverse , multconst , multiply , negative , powadd , powcos , powcreate , powdiff , powexp , powint , powlog , powpoly , powsin , powsolve , powsqrt , quotient , reversion , subtract , tpsform ] > > powcreate(p(n)=1/n!); tpsform(p,x,5); 1+x+ 12 13 14 x+ x+ x + O(x5 ) 2 6 24 这里 p 就是我们定义的形式幂级数. 在形式幂级数的定义过程中, 可以使用递归方式. 但前提 是变量 p 没有使用过. > p:=’p’; p := p 7.4 级数展开 > > 165 powcreate(p(n)=p(n-1)*p(n-2),p(0)=1,p(1)=1/2) ; tpsform(p,x,5); 1 1 1 1 1 + x + x2 + x3 + x4 + O(x5 ) 2 2 4 8 在形式幂级数的定义过程中, 我们没有直接的方式区分奇数项和偶数项. 例如我们要定义正弦 函数的形式幂奇数, 方法是在每一项乘上一个 (1 + (−1)n+1 )/2. 当 n 为偶数时, 此项为 0,n 为 奇数时, 此项为 1. 这样我们就去掉了所有的偶数项. 同样的方法也可用于定义余弦函数的形式 幂级数. > > powcreate(p(n)=(1+(-1)^(n+1))/2*(-1)^((n-1)/2 )/n!); tpsform(p,x,9); 1 15 1 x − x3 + x− x7 + O(x9 ) 6 120 5040 powcreate(q(n)=(1+(-1)^n)/2*(-1)^(n/2)/n!); tpsform(q,x,9); 1 14 16 1 1 − x2 + x− x+ x8 + O(x9 ) 2 24 720 40320 > > evalpow命令可以完成通常的形式幂级数计算, 例如我们用 evalpow 求两个幂级数 p 和 q 的乘积. > r:=evalpow(p*q); r := proc(powparm ) . . . end > tpsform(r,x,9); x− 23 25 47 x+ x− x + O(x9 ) 3 15 315 > series(sin(x)*cos(x),x,9); 2 25 47 x − x3 + x− x + O(x9 ) 3 15 315 我们把得到的结果和 sin(x) cos(x) 的级数展开相比较, 发现结果是相同的. reversion 命令可以构造形式幂级数的反函数. 于是 reversion(p) 对应的级数应该与 arcsin(x) 的幂级数展开是相同的. > r:=reversion(p); r := proc(powparm ) . . . end > tpsform(r,x,9); x+ 13 35 57 x+ x+ x + O(x9 ) 6 40 112 > series(arcsin(x),x,9); x+ 13 35 57 x+ x+ x + O(x9 ) 6 40 112 166 第七章 微分与积分 powdiff可以计算形式幂级数的微分, 于是 powdiff(p) 与 cos(x) 的级数展开是一致的. > r:=powdiff(p); r := proc(powparm ) . . . end > tpsform(r,x,9); 1− 12 14 16 1 x+ x− x+ x8 + O(x9 ) 2 24 720 40320 使用 powpoly 命令可以把多项式转换为形式幂级数, 不过这样转换过来的形式幂级数只有 有限项. 在下面的例子中, 我们把 tan(x) 展开为级数, 取它的前 10 项转换为多项式. 然后再用 powpoly 命令转换为级数, 此时的级数只有 tan(x) 的前 10 项. > s:=series(tan(x),x=0,10); 1 25 17 7 62 9 s := x + x3 + x+ x+ x + O(x10 ) 3 15 315 2835 r:=powpoly(convert(s,polynom),x); r := proc(powparm ) . . . end > 下面我们计算级数 p + r, 把它展开到第 15 项. 注意这样得到的结果只有前 9 项与 tan(x)+sin(x) 的级数展开是一致的. > t:=evalpow(p+r); t := proc(powparm ) . . . end > tpsform(t,x,15); 1 17 5 271 7 7937 9 1 1 2 x + x3 + x+ x+ x− x11 + x13 + O(x15 ) 6 120 5040 362880 39916800 6227020800 series(tan(x)+sin(x),x=0,15); 1 17 5 271 7 7937 9 353791 11 22368257 13 2 x + x3 + x+ x+ x+ x+ x + O(x15 ) 6 120 5040 362880 39916800 6227020800 > 7.5 积分变换 在微积分的应用中, 积分变换是非常重要的工具. 在这一节中, 我们介绍 Maple 中有关积 分变换的命令. 对于函数 f 及核 K , 积分变换的定义为 b T (f )(s) = a f (t)K (s, t)dt. 最常用的积分变换有:Laplace 变换、 Fourier 变换和 Mellin 变换, 它们对应的核函数分别为 e−st , −ist s−1 e 和t . 积分变换 变换 Laplace Fourier Mellin 定义 ∞ f (t)e−st dt 0 ∞ f (t)e−ist dt −∞ ∞ f (t)ts−1 dt 0 Maple 函数 laplace(f(t),t,s) fourier(f(t),t,s) mellin(f(t),t,s) 7.5 积分变换 Laplace 变换 167 Maple 的积分变换命令来自于 inttrans 程序包. 因此我们首先需要调入 inttrans 程序包 > with(inttrans); [addtable , fourier , fouriercos , fouriersin , hankel , hilbert , invfourier , invhilbert , invlaplace , laplace , mellin ] 下面我们对函数 cos(t − a) 做 laplace 变换 > laplace(cos(t-a),t,s); s cos(a) + sin(a) s2 + 1 laplace变换的逆变换为 invlaplace > invlaplace(%,s,t); cos(a) cos(t) + sin(a) sin(t) > combine(%,’trig’); cos(t − a) laplace变换常用于解微分方程, 有关的例子我们将在下一章中给出. 下面我们考察一个积分方 程的例子. > int_eqn:=int(exp(a*x)*f(t-x),x=0..t)+b*f(t)=t ; t int eqn := 0 > e(a x) f(t − x) dx + b f(t) = t laplace(%,t,s); 1 laplace(f(t), t, s) + b laplace(f(t), t, s) = 2 s−a s > readlib(isolate); proc(expr , x, n) . . . end > isolate(%%,laplace(f(t),t,s)); laplace(f(t), t, s) = 1 1 s2 ( + b) s−a (−1+b a) t > invlaplace(%,s,t); ) b at 1 e( + f(t) = − (−1 + b a)2 −1 + b a (−1 + b a)2 下面我们对结果进行检验. > f:=unapply(rhs(%),t); ) b 1 at e( f := t → + − (−1 + b a)2 −1 + b a (−1 + b a)2 (−1+b a) t 168 > 第七章 微分与积分 normal(int_eqn,’expanded’); t=t Fourier 变换 Maple 用 fourier 和 invfourier 计算解析函数的 Fourier 变换和逆变换. > fourier(1/(1+t^3),t,s); 1 (I s ) Ie π (Heaviside(−s) − Heaviside(s)) + e(−1/2 I s) 3 √ √ √ √ 1 1 ( I (−1 − I 3) e(1/2 3 s) π Heaviside(−s) − I (−1 + I 3) e(−1/2 3 s) π Heaviside(s)) 3 3 下面我们求它的逆变换, 以验证结果是否正确. > invfourier(%,s,t); − (t + 1) (−I + √ 4 √ 3 + 2 I t) (− 3 − I + 2 I t) > normal(%,’expanded’); 1 1 + t3 快速 Fourier 变换 在信号处理及其它许多领域, 我们经常使用离散 Fourier 变换,Maple 提供了 FFT 和 iFFT 命令做快速 Fourier 变换及其逆变换. 下面我们回一下离散 Fourier 变换的定义: 设 x = [x0 , x1 , · · · , xN −1 ] 是长度为 N 的一组数据, 它的 Fourier 变换 y = [y0 , y1 , · · · , yN −1 ] 定义为 N −1 yk = j =0 xj e−2πijk/N 当 N 是 2 的方幂时, 离散 Fourier 变换有一快速算法, 即快速 Fourier 变换. 下面我们令 N = 23 , 计算 x = [−1, −1, −1, −1, 1, 1, 1, 1] 的快速 Fourier 变换. 首先我们用 readlib 将 FFT 调入, 它的用法是 FFT(n,x,y), 其中 N = 2n , x 是数据的实 部,y 是虚部. > readlib(FFT); proc(m, x, y ) . . . end > x:=array([-1$4,1$4]); x := [−1, −1, −1, −1, 1, 1, 1, 1] > y:=array([0$8]); y := [0, 0, 0, 0, 0, 0, 0, 0] > FFT(3,x,y); 8 7.5 积分变换 169 FFT命令的输出为 N , 变换后的数据存储在变量 x 和 y 中.x 为实部,y 为虚部. 由于 x, y 都是向 量, 我们用 print 命令显示. > print(x); [0, −2.000000001, 0, −1.999999999, 0, −1.999999999, 0, −2.000000001] > print(y); [0, 4.828427122, 0, .828427124, 0, −.828427124, 0, −4.828427122] 下面我们用 iFFT 做逆 Fourier 变换. > iFFT(3,x,y); 8 > print(x); [−1.000000000, −.9999999990, −.9999999995, −.9999999985, 1.000000000, .9999999990, .9999999995, .9999999985] > print(y); [0, .2500000000 10−9 , 0, −.2500000000 10−9 , 0, −.2500000000 10−9 , 0, .2500000000 10−9 ] 由于快速 Fourier 变换是数值计算算法, 因此其计算结果有误差. 快速 Fourier 变换的一个重要应用是对测量数据做卷积运算, 以便得到光滑的曲线. 下面是 一个简单的例子. 给定一个正弦曲线, 我们用随机数据加上噪声. > > > > > re_data:=array([seq(sin(0.0625*k)+rand(1000)( )/1000,k=1..2^8)]): im_data:=array([0$2^8]): xcoords:=array([seq(0.0625*k,k=1..2^8)]): plotdata:=convert(zip((a,b)->[a,b],xcoords,re _data),’list’): plot(plotdata,style=POINT); 2 1.5 1 0.5 0 -0.5 2 4 6 8 10 12 14 16 由于在做快速 Fourier 变换时, 数据必须是向量类型. 而在画图时, 数据为列表类型, 因此我们 t 用 convert 命令做数据类型的转换. 我们用函数 t → exp(−100( 256 )2 ) 来做卷积. > > re_kernel:=array([seq(exp(-100.0*(k/2^8)^2),k =1..2^8)]): im_kernel:=array([0$2^8]): 170 > > 第七章 微分与积分 plotdata:=convert(zip((a,b)->[a,b],xcoords,re_kernel),’list’): plot(plotdata,style=POINT); 1 0.8 0.6 0.4 0.2 0 2 4 6 8 10 12 14 16 > FFT(8,re_data,im_data); 256 > FFT(8,re_kernel,im_kernel); 256 > > > > > data:=zip((a,b)->(a+b*I),re_data,im_data): kernel:=zip((a,b)->(a+b*I),re_kernel,im_kernel): newdata:=zip((a,b)->(a*b),data,kernel): new_re_data:=map(Re,newdata): new_im_data:=map(Im,newdata): 最后用逆快速 Fourier 变换计算出卷积 > iFFT(8,new_re_data,new_im_data); 256 > > plotdata:=convert(zip((a,b)->[a,b],xcoords,new_re_data),’list’): plot(plotdata,style=POINT); 30 25 20 15 10 5 0 -5 2 4 6 8 10 12 14 16 7.5 积分变换 Mellin 变换 Maple 进行 Mellin 变换的命令是 mellin. > 171 mellin(sin(t),t,s); 1 Γ(s) sin( π s) 2 > mellin(t^a,t,s); Γ(s + a) Heaviside(s + a) Γ(−a − s) Heaviside(−a − s) + Γ(s + a + 1) Γ(1 − s − a) Mellin 变换的结果可以化简 > simplify(%); Heaviside(s + a) − Heaviside(−a − s) s+a Z 变换 在本节的最后, 我们介绍 Z 变换.Z 变换可以将离散函数 f (n) 转换为连续函数 F (z ). 它的 定义为: ∞ f (n) F (z ) = zn n=0 完成 Z 变换和逆变换的命令是 ztrans 与 invztrans > ztrans(n^2,n,z); z (z + 1) (z − 1)3 > invztrans(%,z,n); n2 Z 变换常用来解差分方程. 例如方程 f (k + 2) − 4f (k + 1) + 3f (k ) = 1 且 f (0) = 0, f (1) = 1 就可以用 Z 变换来求解. > sys:=f(k+2)-4*f(k+1)+3*f(k)=1; sys := f(k + 2) − 4 f(k + 1) + 3 f(k ) = 1 > > alias(F(z)=ztrans(f(k),k,z)): ztrans(sys,k,z); z 2 F(z ) − f(0) z 2 − f(1) z − 4 z F(z ) + 4 f(0) z + 3 F(z ) = z z−1 将 f (0) = 0, f (1) = 1 带入就可以解出 F (z ). > subs(f(0)=0,f(1)=1,%); z 2 F(z ) − z − 4 z F(z ) + 3 F(z ) = z z−1 172 > 第七章 微分积分 sol:=solve(%,F(z)); sol := z3 − 5 z2 z2 + 7z − 3 对 F (z ) 做逆变换求出 f (k) 的通解公式. > fk:=invztrans(%,z,k); fk := 3k 1 3 3 − k− 4 2 4 > seq(fk,k=1..6); 1, 5, 18, 58, 179, 543 第八章 微分方程 在这一章中, 我们将展示如何用 Maple 解微分方程. 对许多常微分方程,Maple 的命令 dsolve 可以给出一般解. 如果给定了初值,dsolve 也能给出特解. 当然许多微分方程是难以求 出符号解的.Maple 提供了许多工具来刻画它们的特征, 或者给出数值解. 8.1 微分方程的符号解 常微分方程是如下形式的方程: F (y, y , · · · , y (n) , x) = 0 其中 y , y , · · · , y (n) 是 y (x) 导数的缩写,F 是定义在 Rn+2 上的实函数. 如果 F 中出现了第 n +1 项 y (n) , 则称 F 的阶数为 n. 如果函数 F 的前 n + 1 都是线性的, 则称 F 是线性常微分方程. 它的形式如下: an (x)y (n) + an−1 (x)y (n−1) + · · · + a1 (x)y + a0 (x)y + a(x) = 0. 当 F 是多项式映射时, 它的第 n + 1 项 y (n) 的次数称为微分方程的次数. Maple 解常微分方程的基本命令是 dsolve, 解微分方程的大部分数学原理都可以用 dsolve 来实现. 不过 Maple 用符号方法解微分方程的能力是非常有限的, 只有一些特殊类型的微分方 程可以求出符号解. 下面我们考虑一个简单的微分方程: dy =x+y dx 用 Maple 解这个方程的命令是 > dsolve(diff(y(x),x)=x+y(x),y(x)); y(x) = −x − 1 + ex C1 得到的解中含有一个常数项 C1. 这是一个通解, 给 C1 指定一个特定的值就可以得到特解. 在 常微分方程中, 通常是给定一个初值, 例如初值为 y (0) = 2. 下面的命令可以解带初值的微分方 程: > sol1:=dsolve({diff(y(x),x)=x+y(x),y(0)=2},y (x)); sol1 := y(x) = −x − 1 + 3 ex 我们想画出这个微分方程的解, 此时需要将表达式转化为函数 > y1:=unapply(rhs(sol1),x); y1 := x → −x − 1 + 3 ex 174 8.1 微分方程的符号解 > 175 plot(y1(x),x=0..3); 50 40 30 20 10 0 0.5 1 1.5 x 2 2.5 3 下面的常微分方程带有一个边界条件: y y (1 + x2 ) = x, y (1) = 1 对于边界条件 y (1) = 1, 我们用 D(y)(1)=1 来表示. > de:=D(y)(x)*y(x)*(1+x^2)=x; de := D(y )(x) y(x) (1 + x2 ) = x > dsolve({de,D(y)(1)=1},y(x)); y(x) = ln(1 + x2 ) − ln(2) + 1 4 对某些微分方程,dsolve 会给出隐含解. 例如考虑下列微分方程 xy = y ln(xy ) − y > ode:=x*diff(y(x),x)=y(x)*ln(x*y(x))-y(x); ∂ y(x)) = y(x) ln(x y(x)) − y(x) ode := x ( ∂x dsolve(ode,y(x)); x = C1 ln(x) + C1 ln(y(x)) > Maple 给出的是隐含解. 如果要得到显示解, 可以用 isolate 解出 y > readlib(isolate)(%,y(x)); y(x) = e( x− C1 ln(x) ) C1 也可以给 dsolve 第三个参数 explicit 要它求出显示解 > dsolve(ode,y(x),’explicit’); y(x) = e( x− C1 ln(x) ) C1 微分方程的解可以化简 > expand(%); y(x) = e( x C1 ) x 176 第八章 微分方程 最后我们把解带回微分方程验证一下结果. > subs(%,ode); x ∂ e( C1 ) ∂x x x = e( x C1 ) ln(e( x x C1 ) ) − e( x C1 ) x > expand(lhs(%)-rhs(%)); 0 使用选项 output=basis, dsolve 会给出微分方程的通解和特解的一个列表. 例如 > de:=diff(y(x),x$4)+5*diff(y(x),x$2)-36*y(x)=s in(x); de := ( ∂4 ∂2 y(x)) + 5 ( 2 y(x)) − 36 y(x) = sin(x) ∂x4 ∂x > dsolve(de,y(x)); y(x) = − 57 1 1 1 sin(x) − sin(3 x) + sin(x) cos(x)2 + cos(3 x) sin(2 x) 3380 676 169 156 1 1 1 − cos(3 x) sin(4 x) + sin(3 x) cos(4 x) − sin(3 x) cos(2 x) + C1 e(2 x) 312 312 156 + C2 e(−2 x) + C3 cos(3 x) + C4 sin(3 x) > dsolve(de,y(x),output=basis); 57 1 1 sin(x) − sin(3 x) + sin(x) cos(x)2 3380 676 169 1 1 1 + cos(3 x) sin(2 x) − cos(3 x) sin(4 x) + sin(3 x) cos(4 x) 156 312 312 1 − sin(3 x) cos(2 x)] 156 [[e(2 x) , e(−2 x) , cos(3 x), sin(3 x)], − dsolve命令还可以用来解微分方程组. 例如我们要解下列微分方程组 dy = −x(t) dt dx = y (t) dt 初值条件为 x(0) = 1, y (1) = 2. Maple 的解法如下: > > sol:=dsolve({D(y)(t)=-x(t),D(x)(t)=y(t),x(0)=1,D(y)(1)=2}, {x(t),y(t)}); sin(t) (cos(1) + 2) cos(t) (cos(1) + 2) sol := {x(t) = − + cos(t), y(t) = − − sin(t)} sin(1) sin(1) 使用 subs 可以把 x(t) 和 y (t) 分别提取出来. > funcx:=subs(sol,x(t)); funcx := − sin(t) (cos(1) + 2) + cos(t) sin(1) 8.1 微分方程的符号解 > 177 funcy:=subs(sol,y(t)); funcy := − cos(t) (cos(1) + 2) − sin(t) sin(1) 如果我们想看到 Maple 解微分方程的具体过程和细节, 我们可以改变 infolevel[dsolve] 的省缺值为 2 到 5 之间的一个整数, 它的省缺值为 1. > > infolevel[dsolve]:=3: dsolve(3*D(y)(x)+y(x)*(1+(2*x-1)*y(x)^3)=0,y(x)); dsolve/diffeq/dsol1: -> first order, first degree methods : dsolve/diffeq/dsol1: trying linear bernoulli dsolve/diffeq/dsol1: trying separable dsolve/diffeq/dsol1: trying exact dsolve/diffeq/dsol1: trying general homogeneous dsolve/diffeq/dsol1: trying Riccati dsolve/diffeq/linsubs: trying linear substitution dsolve: Warning: no solutions found 从 Maple 输出的信息我们可以看到,dsolve 会根据微分方程的类型去尝试各种解法, 如果 这些解法都无法解决问题,dsolve 会告诉我们它无法解出这个方程. dsolve 所能解的微分方程 类型见下表 一阶常微分方程 微分方程的类型 线性 正合 非正合 可分 齐次 高阶 Bernoulli Clairaut Riccati 微分方程的类型 线性 Eular Bessel 微分方程的形式 y + P (x)y = Q(x) P y = − Q(x,y) , 满足 (x,y ) ∂Q ∂x = ∂P ∂y F (x, y )y + G(x, y ) = 0 但是存在积分因子 y = f (x)g (y ) y y = F (xy n ) x x = F (y, y ) y + P (x)y = Q(x)y n y = xy + F (y ) y = P (x)y 2 + Q(x)y + R(x) 二阶常微分方程 微分方程的形式 ay + by + cy = d(x), 其中 a, b, c 是复数 x2 y + axy + by = c(x) x2 y + (2k + 1)xy + (α2 x2r + β 2 )y = 0, 其中 k, α, r, β 是复数, 而且 αr = 0 在不同的 Maple 版本下,Maple 解微分方程的能力也不尽相同. 有时明显有解的微分方程, 在某些 Maple 版本中未必能够解出. 例如下面的微分方程在 Maple V Release 4 中就无法求出 解. > de:=diff(y(x),x)+2*y(x)*exp(x)-y(x)^2-exp(2*x )-exp(x)=0; ∂ de := ( y(x)) + 2 y(x) ex − y(x)2 − e(2 x) − ex = 0 ∂x 178 > 第八章 微分方程 dsolve(de,y(x)); dsolve/diffeq/dsol1: -> first order, first degree methods : dsolve/diffeq/dsol1: trying linear bernoulli dsolve/diffeq/dsol1: trying separable dsolve/diffeq/dsol1: trying exact dsolve/diffeq/dsol1: trying general homogeneous dsolve/diffeq/dsol1: trying Riccati dsolve: Warning: no solutions found 这个方程有一个明显的解 y (x) = ex . > > subs(y(x)=exp(x),de): expand("); 0=0 遇到这样的问题有两种处理方法. 一种是换一个 Maple 版本, 也许就可以求出微分方程的解. 另一种方法是做一个简单的代换,Maple 就可以解出这个方程. 我们令 y (x) = z (x) + ex . 下面的 过程就是我们在 Maple V Release 4 中解决这个问题的过程. > subs(y(x)=z(x)+exp(x),de); ∂ (z(x) + ex )) + 2 (z(x) + ex ) ex − (z(x) + ex )2 − e(2 x) − ex = 0 ( ∂x expand("); ( ∂ z(x)) − z(x)2 = 0 ∂x > > dsolve(",z(x)); dsolve/diffeq/dsol1: -> first order, first degree methods : dsolve/diffeq/dsol1: trying linear bernoulli dsolve/diffeq/bernsol: trying Bernoulli solution dsolve/diffeq/linearsol: solving 1st order linear d.e. dsolve/diffeq/dsol1: linear bernoulli successful 1 = −x + C1 z(x) > isolate(",z(x)); z(x) = − 1 x − C1 1 x − C1 > solution:=exp(x)+rhs("); solution := ex − > > subs(y(x)=solution,de): expand("); 0=0 8.2 用 Laplace 变换解微分方程 179 在 Maple 7 当中, 直接使用 dsolve 就可以求出方程的解. 8.2 用 Laplace 变换解微分方程 变换是一种数学运算, 它将一个函数变换为另一个新函数. 在解微分方程的过程中,Laplace 变换是常用的方法. 这种方法主要用于解常系数的线性微分方程, 和包含不连续函数的微分方 程.Laplace 变换的核心作用在于它将微分运算变换为乘法运算. 是一个微分方程就被变换为 代数方程. 函数 f 的 Laplace 变换是一个新的函数, 一般记作 F . 定义如下: ∞ F (s) = 0 f (t)e−st dt 我们把 e−st 称为变换的核. 对于一个 2 阶线性微分方程, 考察它的初值问题 ay (t) + by (t) + cy (t) = f (t), y (0) = y0 , y (0) = y0 我们把 Laplace 变换应用到这个方程上得到 a(s2 Y (s) − sy0 − y0 ) + b(sY (s) − y0 ) + cY (s) = F (s) 其中 Y (s) 是 y (t) 的 Laplace 变换,F (s) 是 f (t) 的 Laplace 变换. 我们解这个关于 Y (s) 的代数 方程得到 F (s) + asy0 + ay0 + by0 Y (s) = , as2 + bs + c 然后计算 Y (s) 的逆 Laplace 变换, 就得到了微分方程的解 y (t). 下面我们考察一个初值问题. y (t) − 2y (t) + y (t) = sin(t), y (0) = 1, y (0) = 2. 我们对这个方程做 Laplace 变换, 得到 > > with(inttrans): de:=diff(y(t),t$2)-2*diff(y(t),t)+y(t)=sin(t) ; ∂2 ∂ de := ( 2 y(t)) − 2 ( y(t)) + y(t) = sin(t) ∂t ∂t laplace(de,t,s); > s (s laplace(y(t), t, s) − y(0)) − D(y )(0) − 2 s laplace(y(t), t, s) + 2 y(0) + laplace(y(t), t, s) 1 =2 s +1 将初值 y (0) = 1, y (0) = 2 代入, > subs([y(0)=1,D(y)(0)=2],%); s (s laplace(y(t), t, s) − 1) − 2 s laplace(y(t), t, s) + laplace(y(t), t, s) = s2 1 +1 180 第八章 微分方程 将 laplace(y(t),t,s) 解出, 再做逆 Laplace 变换就得到了微分方程的解. > readlib(isolate)(%,laplace(y(t),t,s)); 1 +s laplace(y(t), t, s) = 2 + 1 s − 2s + 1 s2 > invlaplace(%,s,t); y(t) = 3 t 1t 1 t e + e + cos(t) 2 2 2 给 dsolve 命令一个选项 method=laplace, Maple 将用 Laplace 变换法解出微分方程. > dsolve({de,y(0)=1,D(y)(0)=2},y(t),method=laplace); 3 1 1 y(t) = t et + et + cos(t) 2 2 2 对于包含分段连续函数的常微分方程,Laplace 变换特别有用. 构造分段连续函数的基本单 元是单位阶跃函数 u(t), 它的定义为 u(t) = 0 if t < 0, 1 if t ≥ 0. 为了纪念物理学家 Oliver Heaviside, 他提出了用 Laplace 变换方法解微分方程. 这个函数被命 名为 Heaviside 函数. 在 Maple 中也是用 Heaviside 表示这个函数. 下面我们用 Heaviside 函数来定义一个分段连续函数 0 t < 0, 1 0 ≤ t < 1, g (t) = −1 1 ≤ t < 2, 0 t ≥ 2. 这个函数可以写成 g (t) = u(t) − 2u(t − 1) + u(t − 2) 写成 Maple 的函数就是: > g:=t->Heaviside(t)-2*Heaviside(t-1)+Heaviside (t-2); g := t → Heaviside(t) − 2 Heaviside(t − 1) + Heaviside(t − 2) 下面我们考察初值问题: y (t) + 3y (t) + y (t) = g (t), y (0) = 1, y (0) = 1 其中 g (t) 就是上面定义的分段函数. 用 Laplace 变换解这个方程得到下列复杂的函数. > de:=diff(y(t),t$2)+3*diff(y(t),t)+y(t)=g(t); ∂2 ∂ y(t)) + 3 ( y(t)) + y(t) = Heaviside(t) − 2 Heaviside(t − 1) + Heaviside(t − 2) 2 ∂t ∂t de := ( 8.3 微分方程的级数解 > 181 sol:=dsolve({de,y(0)=1,D(y)(0)=1},y(t),meth or=laplace); sol := y(t) = Heaviside(t) − − √ 3 (1/2 (−3+√5) t) e Heaviside(t) 5 10 1 (1/2 (−3+√5) t) e Heaviside(t) − 2 Heaviside(t − 1) 2 √ √ √ 3 + Heaviside(t − 1) 5 e(1/2 (−3+ 5) (t−1)) + Heaviside(t − 1) e(1/2 (−3+ 5) (t−1)) 5 √ √ 3 + Heaviside(t − 2) − Heaviside(t − 2) 5 e(1/2 (−3+ 5) (t−2)) 10 √ √ 1 3 (−1/2 (3+√5) t) − Heaviside(t − 2) e(1/2 (−3+ 5) (t−2)) + e Heaviside(t) 5 2 10 √ √ √ 1 3 − e(−1/2 (3+ 5) t) Heaviside(t) − Heaviside(t − 1) 5 e(−1/2 (3+ 5) (t−1)) 2 5 √ √ √ 3 + Heaviside(t − 1) e(−1/2 (3+ 5) (t−1)) + Heaviside(t − 2) 5 e(−1/2 (3+ 5) (t−2)) 10 √ √ 1 1√ 1 − Heaviside(t − 2) e(−1/2 (3+ 5) (t−2)) + ( 5 + ) e(1/2 (−3+ 5) t) 2 2 2 √ √ (−1/2 (3+√5) t) 1 (−5 + 5) 5 e + 10 由于这个解过于复杂, 我们画出它的图形以及 g (t) 的图形. > plot({rhs(sol),g(t)},t=0..5); 1 0.5 0 1 2 t 3 4 5 -0.5 -1 8.3 微分方程的级数解 Laplace 变换主要解决 2 阶线性常系数的微分方程, 对于 2 阶线性变量系数的齐次微分方 程, 我们可以用级数方法解决. 考察下列微分方程: a(x)y (x) + b(x)y (x) + c(x)y (x) (8.1) 假设系数函数 a(x), b(x), c(x) 是解析的, 即在 x = x0 的邻域中有收敛的级数表示. 为了简单起 见, 不妨设 x0 = 0. 我们假设 a(0) = 0, 用 a(x) 去除方程的两边我们得到下列微分方程 y (x) + p(x)y (x) + q (x)y (x) = 0 (8.2) 对于方程 (8.2), 我们寻求它的级数解 ∞ y (x) = n=0 an xn (8.3) 182 第八章 微分方程 如果我们能找到这个级数解, 则 y (0) = a0 , y (0) = a1 . 于是如果我们给了初值条件, 也就可以给 出级数解的前两项系数. 将级数 (8.3) 代入方程 (8.2), 将 p(x), q (x) 也展开成级数, 将同次的项组合起来, 就可以得 到一个关于 an 的递推公式. 解关于 an 的递推关系就可以求出微分方程的解. 如果求出的 an 比较简单, 并且恰好能找到对应的函数 y (x), 那我们就求出了微分方程 (2) 的解, 即使找不到对应的函数 y (x), 我们也能得到一个 y (x) 的近似解. dsolve 使用选项 type=series 给出方程的级数解. 下面我们看一个简单的例子 y − xy − y = 0, y (0) = 2, y (0) = 1 用 dsolve 求出的解如下 > de1:=diff(y(x),x$2)-x*diff(y(x),x)-y(x)=0; de1 := ( ∂2 ∂ y(x)) − y(x) = 0 y(x)) − x ( ∂x2 ∂x > sol1:=dsolve({de1,y(0)=2,D(y)(0)=1},y(x),type=series); 1 1 15 sol1 := y(x) = 2 + x + x2 + x3 + x4 + x + O(x6 ) 3 4 15 通常 dsolve 只求到级数的第六项, 这是由 Order 所决定的. 如果我们想求更精确的解, 可以重 设 Order 的值. 例如我们令 Order=20, 重新解这个方程就可以得到更精确的解. > > Order:=20: dsolve({de1,y(0)=2,D(y)(0)=1},y(x),type=series); 13 14 15 16 17 18 19 1 x+ x+ x+ x+ x+ x+ x+ x10 + 3 4 15 24 105 192 945 1920 1 1 1 1 1 1 x11 + x12 + x13 + x14 + x15 + x16 + 10395 23040 135135 322560 2027025 5160960 1 1 1 x17 + x18 + x19 + O(x20 ) 34459425 92897280 654729075 y(x) = 2 + x + x2 + 对于这个微分方程,dsolve 可以求出精确解. > sol2:=dsolve({de1,y(0)=2,D(y)(0)=1},y(x)); √ 2 2√ 1 1√ sol2 := y(x) = 2 e(1/2 x ) + e(1/2 x ) π 2 erf( 2 x) 2 2 读者如果有兴趣可以把两个解的图画出来比较一下. 级数解通常只用于齐次方程, 对于非齐次的方程, 它就不一定能求出解. 考察下列微分方程 √ y − y = sin( x), y (0) = 0 用 dsolve 命令以及级数解都很难求出方程的解 > de2:=y(x)-diff(y(x),x)=sin(sqrt(x)); √ ∂ de2 := y(x) − ( y(x)) = sin( x) ∂x 8.3 微分方程的级数解 > 183 dsolve({de2,y(0)=0},y(x)); y(x) = −e x 0 x √ sin( u) du + ex eu 0 0 √ sin( u) du eu dsolve虽然给出了公式解, 但是这些积分我们依然无法求出. 使用级数解根本得不到结果. > dsolve({de2,y(0)=0},y(x),type=series); √ 对于这个问题, 使用 Maple 的级数展开公式可以手工的求出一个近似解. 首先将 sin( x) 展开 为级数. > siq:=series(sin(sqrt(x)),x,8); 1 5 /2 1 1 1 1 1 3/2 x+ x− x7/2 + x9/2 − x11/2 + x13/2 6 120 5040 362880 39916800 6227020800 1 − x15/2 + O(x8 ) 1307674368000 x− siq:=siq-select(has,siq,O); 1 5 /2 1 1 1 1 1 3/2 x+ x− x7/2 + x9/2 − x11/2 + x13/2 6 120 5040 362880 39916800 6227020800 1 − x15/2 1307674368000 x− √ √ siq := > siq := siq是 sin(sqrt(x)) 的一个级数展开的近似. 我们去掉了 8 阶以上的项. 与通常的级数展开不 同, 我们把 y (x) 的级数展开写成下列形式 i y (x) = i=1 nf tyai x(2i+1)/2 由初值条件 y (0) = 0, 我们去掉了 a0 这一项. 在 Maple 中写出 y (x) 的前七项 > > a:=array(1..7): fy:=sum(a[i]*x^(i+1/2),i=1..7); fy := a1 x3/2 + a2 x5/2 + a3 x7/2 + a4 x9/2 + a5 x11/2 + a6 x13/2 + a7 x15/2 记做 fy, 求 fy 的微分, 代入原方程. > fyd:=diff(fy,x); 3√ 5 7 9 11 13 15 fyd := a1 x + a2 x3/2 + a3 x5/2 + a4 x7/2 + a5 x9/2 + a6 x11/2 + a7 x13/2 2 2 2 2 2 2 2 deqn:=fy-fyd-siq; 3√ 5 a1 x − a2 x3/2 2 2 √ 7 1 3/2 9 11 13 15 1 5 /2 5/2 7 /2 9 /2 11/2 13/2 − a3 x − a4 x − a5 x − a6 x − a7 x − x+ x − x 2 2 2 2 2 6 120 1 1 1 1 1 + x7/2 − x9/2 + x11/2 − x13/2 + x15/2 5040 362880 39916800 6227020800 1307674368000 > deqn := a1 x3/2 + a2 x5/2 + a3 x7/2 + a4 x9/2 + a5 x11/2 + a6 x13/2 + a7 x15/2 − 184 第八章 微分方程 将 deqn 整理成级数的形式. > coef:=series(deqn,x,10); √ 3 15 7 1 a1 ) x + (a1 + − a2 ) x3/2 + (− a3 + a2 − ) x5/2 2 62 2 120 9 1 11 1 13 1 − a4 + a3 ) x7/2 + (− + a4 − a5 ) x9/2 + (a5 + − a6 ) x11/2 +( 5040 2 362880 2 39916800 2 15 1 1 + (− a7 + a6 − ) x13/2 + (a7 + ) x15/2 2 6227020800 1307674368000 coef := (−1 − 使用 op 命令可以取出级数中的项, 连续使用两次可以求出级数项的系数. 例如 > op(1,coef); (−1 − √ 3 a1 ) x 2 > op(1,op(1,coef)); −1 − 3 a1 2 用这种方法取出级数中的前 7 项的系数, 最后一项不用. > eq:=seq(op(1,op(i,coef)),i=1..nops(coef)-1); 15 7 1 1 9 1 11 3 a1 , a1 + − a2 , − a3 + a2 − , − a4 + a3 , − + a4 − a5 , 2 62 2 120 5040 2 362880 2 1 13 15 1 a5 + − a6 , − a7 + a6 − 39916800 2 2 6227020800 eq := −1 − 解代数方程 eq 就可以求出 a1 , · · · , a7 的值. solve({eq}); −328111 −95699 −29 −299 −5 −1 −2 {a7 = , a6 = , a5 = , a4 = , a3 = , a2 = , a1 = } 6671808000 259459200 12096 22680 84 5 3 > 使用 assign 命令把它们的值代入 fy, 就得到了方程的近似解. > > assign(%); fy; 2 3 / 2 1 5 /2 5 7 /2 299 9/2 29 95699 328111 −x −x − x− x− x11/2 − x13/2 − x15/2 3 5 84 22680 12096 259459200 6671808000 最后一个例子是量子力学中的古典问题: 求解一维调和振子. 以无维量单位形式给出的 Schr¨dinger 方程如下: o d2 y (x) + ( − x2 )y (x) = 0 dx2 渐近分析的研究建议我们做代换 y (x) = h(x)e−x > 2 /2 , 得到关于 h(x) 的微分方程. de1:=diff(y(x),x$2)+(epsilon-x^2)*y(x); de1 := ( ∂2 y(x)) + (ε − x2 ) y(x) ∂x2 8.3 微分方程的级数解 > 185 subs(y(x)=exp(-x^2/2)*h(x),de1); ( 2 ∂ 2 (−1/2 x2 ) e h(x)) + (ε − x2 ) e(−1/2 x ) h(x) 2 ∂x > collect(%,exp(-x^2/2))/exp(-x^2/2); −h(x) + x2 h(x) − 2 x ( ∂ ∂2 h(x)) + ( 2 h(x)) + (ε − x2 ) h(x) ∂x ∂x > de2:=collect(%,[diff(h(x),x$2),diff(h(x),x),h ]); de2 := ( ∂2 ∂ h(x)) − 2 x ( h(x)) + (−1 + ε) h(x) ∂x2 ∂x 下面我们用形式幂级数方法解这个微分方程,powsolve 可以求出幂级数. > > with(powseries): H:=powsolve(de2); H := proc(powparm ) . . . end 写出它的前 10 项. > h:=tpsform(H,x,10); 1 1 1 (−1 + ε) C0 x2 − (−3 + ε) C1 x3 + (−5 + ε) (−1 + ε) C0 x4 + 2 6 24 1 1 (−7 + ε) (−3 + ε) C1 x5 − (−9 + ε) (−5 + ε) (−1 + ε) C0 x6 − 120 720 1 1 (−11 + ε) (−7 + ε) (−3 + ε) C1 x7 + (−13 + ε) (−9 + ε) (−5 + ε) (−1 + ε) C0 x8 5040 40320 1 + (−15 + ε) (−11 + ε) (−7 + ε) (−3 + ε) C1 x9 + O(x10 ) 362880 h := C0 + C1 x − 把它转换为多项式, 并整理得 > collect(convert(%,’polynom’),[C0,C1]); (1 − 1 1 1 (−1 + ε) x2 + (−5 + ε) (−1 + ε) x4 − (−9 + ε) (−5 + ε) (−1 + ε) x6 2 24 720 1 1 (−13 + ε) (−9 + ε) (−5 + ε) (−1 + ε) x8 )C0 + (x − (−3 + ε) x3 + 40320 6 1 1 5 + (−7 + ε) (−3 + ε) x − (−11 + ε) (−7 + ε) (−3 + ε) x7 120 5040 1 (−15 + ε) (−11 + ε) (−7 + ε) (−3 + ε) x9 )C1 + 362880 下面我们看一下幂级数 h 的系数的递推关系. > H(_k); − (3 + ε − 2 k ) a( k − 2) k ( k − 1) 186 第八章 微分方程 它可以解释为 ak = (3 + − 2k )ak−2 k (k − 1) 或等价的形式 (k + 1)(k + 2)ak+2 = (2k + 1 − )ak 从这个公式可以发现如果 = 2k + 1, 则 h 就是一个有限的幂级数. 这就是调和振子能量层级 的量子化. 波函数的例子如下: > > C0:=1: C1:=0: epsilon:=9: tpsform(H,x,10): convert(%,’polynom’); 4 1 − 4 x2 + x4 3 它恰好是第 4 个 Hermite 多项式的倍数. > orthopoly[H](4,x)/12; 1 − 4 x2 + 44 x 3 8.4 微分方程的数值解 用符号方法确实能解一些常微分方程, 不过大部分微分方程是求不出符号解的. 对于常微 分方程的初值问题,Maple 供了数值解. 下面我们通过几个特殊的微分方程来说明 Maple 的数 值解法. 首先我们考查 Van der Pol 方程 y − (1 − y 2 )y + y = 0 初值为 y (0) = 0, y (0) = −0.1. 在 Maple 中求微分方程数值解的方法是使用 dsolve 的选项 type=numeric. > alias(y=y(t),y0=y(0),yp0=D(y)(0)): 为了方便, 我们设置了几个别名. > eqn:=diff(y,t$2)-(1-y^2)*diff(y,t)+y=0; ∂ ∂2 eqn := ( 2 y ) − (1 − y 2 ) ( y ) + y = 0 ∂t ∂t init:=y0=0,yp0=-0.1: F:=dsolve({eqn,init},y, type=numeric); F := proc(rkf45 x ) . . . end > > 此时得到的解是一个过程, 由它可以求出 t 点所对应的 y 值, 例如 t = 0, 1, 2 的值为 > F(0); [t = 0, y = 0, ∂ y = −.1] ∂t 8.4 微分方程的数值解 > 187 F(1); [t = 1, y = −.1447686096006437, ∂ y = −.1781040958088073] ∂t ∂ y = −.09785618462025715] ∂t > F(2); [t = 2, y = −.3033587096669120, 通常我们用图形的方式表示微分方程的解, 此时我们需要 plots 程序包中的 odeplot 过程. > > with(plots): odeplot(F,[t,y],0..15); 2 1 0 2 4 6 8 10 12 14 -1 -2 下面我们考查一个微分方程组的数值解. 这个方程组描述了质子在磁场中的运动. 为了表 达方便, 我们用向量来表示这个微分方程. 下面我们在 Maple 中给出这个方程. > with(linalg): Warning, new definition for norm Warning, new definition for trace > > > > > > > > m:=1.6e-27: q:=1.6e-19: bz:=1: r:=vector([x(t),y(t),z(t)]): v:=map(diff,r,t): a:=map(diff,v,t): b:=vector([0,0,bz]): sys:=evalm(m*a-q*crossprod(v,b)); ∂2 ∂ ∂2 ∂ x(t)) − .16 10−18 ( y(t)), .16 10−26 ( 2 y(t)) + .16 10−18 ( x(t)), 2 ∂t ∂t ∂t ∂t sys := .16 10−26 ( .16 10−26 ( > ∂2 z(t)) ∂t2 de:=seq(sys[i]=0,i=1..3); de := .16 10−26 ( ∂2 ∂ ∂2 ∂ x(t)) − .16 10−18 ( y(t)) = 0, .16 10−26 ( 2 y(t)) + .16 10−18 ( x(t)) = 0, ∂t2 ∂t ∂t ∂t ∂2 .16 10−26 ( 2 z(t)) = 0 ∂t 188 > 第八章 微分方程 condition:=x(0)=0,y(0)=0,z(0)=0,D(x)(0)=1e8,D (y)(0)=0,D(z)(0)=1e6: 在上面的定义中,m 是质子的质量,q 是电荷, 向量 r 表示质子的空间位置,v 是质子的速度,a 是 加速度, 向量 b 描述了磁场的分布. 下面我们给出微分方程的解. > sol:=dsolve({de,condition},convert(r,set),t ype=numeric); sol := proc(rkf45 x ) . . . end 对于这个解, 我们同样可以用 odeplot 来画出它的图形. > odeplot(sol,[x(t),y(t),z(t)],0..1e-7,axes=box ); 0.08 0.06 0.04 0.02 0 -2 -1.5 -1 -0.5 0 0 0.2 0.4 0.6 0.8 -0.8 -0.6 -0.4 -0.2 出人意料的是这个图形非常不光滑, 也许是我们计算的精度不够高. 下面我们令 Digits:=16, 我们来计算 t = 25 × 10−9 , 26 × 10−9 时 x(t) 的值. > > Digits:=16: subs(sol(25e-9),x(t)),subs(sol(26e-9),x(t)); .5984721470104983, .5984721470104983 从结果中发现, 即使精确到 16 位, 这两个值也是完全相同的, 这就说明了为什么图形是不光滑 的. 不过从微分方程的理论出发, 这个方程的解不应该是这样的. 下面我们再求一次微分方程 的解 > > sol:=dsolve({de,condition},convert(r,set),type=numeric): subs(sol(25e-9),x(t)),subs(sol(26e-9),x(t)); .5984721441039561, .5155013718214635 同样计算 t = 25 × 10−9 , 26 × 10−9 时 x(t) 的值. 我们发现这两个值差距很大, 再画一次图形, 得 到一个非常光滑的曲线. > odeplot(sol,[x(t),y(t),z(t)],0..1e-7,axes=box ); 8.5 微分方程的图形表示 189 0.1 0.08 0.06 0.04 0.02 0 -2 -1.5 -1 -0.5 01 0.5 0 -0.5 -1 由这个例子, 我们得到的结论是微分方程的数值解和计算的精度有密切的关系, 对此需要格外 注意. 在我们计算微分方程的数值解时,Maple 在省缺情况下使用的方法是 rkf45, 即 Fehlberg 的 4-5 阶 Runge-Kutta 算法. 除了这种算法以外,Maple 还提供了许多其他算法, 有关这些算法的 用法请参见 Maple 的帮助命令 ?dsolve,numeric. 8.5 微分方程的图形表示 Maple 提供了两类图形命令可以表示微分方程的解. 第一类命令就是我们上一节用到的 来自于 plots 程序包的 odeplot 命令, 它可以用可视化的方式表达微分方程的数值解. 第二类 命令来自于 DEtools 程序包, 这类命令不需要解出微分方程就可以给出它的相位场或相位曲线, 由这些图形我们可以研究微分方程的基本特征. 在这一节中, 我们主要介绍第二类命令的用法. > with(DEtools); [DEnormal , DEplot , DEplot3d , Dchangevar , PDEchangecoords , PDEplot , autonomous , convertAlg , convertsys , dfieldplot , indicialeq , phaseportrait , reduceOrder , regularsp , translate , untranslate , varparam ] 首先我们必须调入 DEtools 程序包. 在新的 Maple 7 版本中,DEtools 程序包中增加了许多命 令, 为了简明起见, 我们只介绍下列几个命令: dfielplot 画出以箭头表示的向量场 phaseportrait 画出向量场以及个别的积分曲线 DEplot 图形表示微分方程 DEplot3d 在三维空间中图形表示微分方程 PDEplot 画出拟线性的一阶偏微分方程 所有这些命令都以微分方程或微分方程组作为第一个参数, 第二个参数是变量列表, 第三 个参数是独立变量 (一般为 t). 这些命令可以处理单独的形如 y = f (y, t) 的微分方程, 或形如 x = f 1(x, y, t), y = f 2(x, y, t) 的微分方程组.DEplot 和 PDEplot 还可以支持更复杂形式的微分 方程. 190 第八章 微分方程 在大多数命令中初始条件必须指定, 可以用两种方式: 一种是 {y (t0 ) = y0 , y (t1 ) = y1 , · · ·} 或 {[x(t0 ) = x0 , y (t0 ) = y0 ], [x(t1 ) = x1 , y (t1 ) = y1 ], · · ·}; 另一种是缩写的方式 {[t0 , y0 ], [t1 , y1 ], · · ·} 或 {[t0 , x0 , y0 ], [t1 , x1 , y1 ], · · ·}. 一些重要的选项是:color 可设置箭头的颜色;linecolor 可设置相位曲线的颜色; arrow 可 设置箭头的形状包括 (SMALL, MEDIUM, LARGE, LINE 或 NONE); dirgrid 可设置箭头的个数 (缺 省值为 dirgrid=[20,20]) 以及 stepsize 可改变数值计算的步长. 当然其他关于图形的选项也 可以使用. 下面我们来看一些实例. 考查一个简单的常微分方程 dy (t) = e−t − 2y (t) dt 它的向量场如下 > dfieldplot(diff(y(t),t)=exp(-t)-2*y(t),y(t),t =-2..3,y=-2..3,axes=BOXED); 3 2 1 y(t) 0 -1 -2 -2 -1 0 t 1 2 3 向量场可以给出微分方程的定性的信息, 对它的含义我们有如下的解释: 对于一个一般的微分方程 dy = f (t, y ) dt 在 (t, y )- 平面上, 我们可以在每个点 (t, y ) 处画一个斜率为 f (t, y ) 的向量. 这些向量就构成了 向量场. 微分方程的解所满足的特征是: 在它的每个点上的切线应当与向量场在这个点的方向 一致. 因此当我们画出一个微分方程的向量场后, 通过向量场, 我们就可以观察到积分曲线的大 致情况. 这也是 Maple 图解微分方程的基本方法. 对于上面的例子, 从向量场中可以发现微分方程解的一些特征: 1. 当 y < 0 时, 积分曲线在这一点是递增的; 2. 当 t → ∞ 时, 所有的解都趋进于 0. 如果我们给定了一些初始条件, 使用 phaseportrait 命令就可以在向量场中画出积分曲线. > > phaseportrait(diff(y(t),t)=exp(-t)-2*y(t),y(t),t=-2..3, {[0,0],[0,0.2],[0,0.4],[0,0.6],[0,0.8],[0,1]},y=-2..3,axes=BOXED); 8.5 微分方程的图形表示 191 3 2 1 y(t) 0 -1 -2 -2 -1 0 t 1 2 3 对于二阶常微分方程及一阶常微分方程组, 向量场同样是定性描述微分方程的重要工具. 特别地, 对于包含两个方程的一阶常微分方程组,plots 程序包中的 fieldplot 命令也可以画出 向量场. 假设一阶常微分方程组为 x y = F (x, y ) = G(x, y ) 则命令 fieldplot([F(x,y),G(x,y)],x=x0..x1,y=y0..y1) 就可以画出常微分方程组的向量场. x = x(1 − x − y ) 例如对下列微分方程组 我们可以用 fieldplot 命令画出它 y = y (0.75 − 0.5x − y ) 的向量场. > > with(plots): fieldplot([x*(1-x-y),y*(0.75-0.5*x-y)],x=0..2,y=0..1); 1 0.8 0.6 y 0.4 0.2 0 0.5 1x 1.5 2 在这个向量场中, 在 x = (x, y ) 点的向量标志向量的方向和 f (x) 的长度. 因此在微分方 程组的奇异点, 向量的长度都非常的短, 接近于 0. 从上面的向量场中就可以看到这种现象. 但 是这种向量场的缺点是我们很难观察出接近奇异点的向量的方向. 解决上述问题的方法是使用 dfieldplot 命令代替 fieldplot, 并使用等长度的向量. > dfieldplot([diff(x(t),t)=x(t)*(1-x(t)-y(t)), 192 > 第八章 微分方程 diff(y(t),t)=y(t)*(0.75-0.5*x(t)-y(t))],[x(t) ,y(t)], t=0..1,x=0..1.5,y=0..1,arrows=SLIM,axes=BOXED ); > 1 0.8 0.6 y 0.4 0.2 0 0 0.2 0.4 0.6 x0.8 1 1.2 1.4 DEplot是一般的常微分方程作图命令, 这个命令的通常用法是 DEplot(ode, dep-var, range, [ini-conds]) 其中 ode 是你想图解的微分方程, dep-var 是因变量, range 是自变量的变化范围, 而 ini-conds 是初始条件的列表. 例如对微分方程 y (t) + sin(t)2 ∗ y (t) + y (t) = cos(t)3 , 已知其初始条件是 y (0) = 1, y (0) = 0, 使用 DEplot 命令可以得到下图. > > DEplot(diff(y(t),t$2)+sin(t)^2*diff(y(t),t)+y(t)=cos(t)^3,y(t),t=0..20, [[y(0)=1,D(y)(0)=0]]); 2 y(t) 1 0 -1 -2 5 10 t 15 20 但是上述图形不是很光滑, 通过设置较小的 stepsize 可以得到光滑的曲线. > > DEplot(diff(y(t),t$2)+sin(t)^2*diff(y(t),t)+y(t)=cos(t)^3,y(t),t=0..20, [[y(0)=1,D(y)(0)=0]],stepsize=0.1); 8.5 微分方程的图形表示 193 2 y(t) 1 0 -1 -2 5 10 t 15 20 如果我们给两组初始条件, 使用 DEplot 命令就可以画出两条曲线. > > DEplot(diff(y(t),t$2)+sin(t)^2*diff(y(t),t)+y(t)=cos(t)^3,y(t),t=0..20, [[y(0)=1,D(y)(0)=0],[y(0)=1,D(y)(0)=2]],stepsize=0.1); 3 2 y(t) 1 0 -1 -2 -3 5 10 t 15 20 对于微分方程组, 也可以同样使用 DEplot 命令 > > > > > eq1:=diff(y(t),t)+y(t)+x(t)=0: eq2:=y(t)=diff(x(t),t): ini1:=x(0)=0,y(0)=5: ini2:=x(0)=0,y(0)=-5: DEplot({eq1,eq2},[x(t),y(t)],t=-5..5,[[ini1],[ini2]],stepsize=0.1); 194 第八章 微分方程 60 40 y 20 0 -20 -40 -60 -60 -40 -20 20 x40 60 对于上面的常微分方程组, 还可以使用 DEplot3d 命令画出三维的曲线. > DEplot3d({eq1,eq2},[x(t),y(t)],t=-5..5,[[ini1],[ini2]],stepsize=0.1); 40 20 y(t) 0 -20 -40 -60 -40 -2 -4 -20 0 x(t) 20 40 60 4 2 0 t 8.6 偏微分方程 一般来说, 偏微分方程是很难求解的. 在 Maple 系统中提供了一些工具来求解偏微分方程. 在不同的 Maple 版本中, 求解偏微分方程的命令是不完全相同的, 在 Maple V Release 4 中, 可 以用 pdesolve 命令求解偏微分方程. 从 Maple V Release 5 以后的版本,Maple 又提供了一个新 命令 pdsolve. 在 Maple V Release 5 中,pdesolve 和 pdsolve 两个命令都可以使用, 到了 Maple 6 以后的版本,pdesolve 命令就已经无法使用了, 为了适应 Maple 版本的变化, 在本节中, 我们 主要介绍 pdsolve 命令的用法. pdsolve 命令的基本用法是 pdsolce(pde,var), 其中 pde 是偏微分方程,var 是你想求解的 变量. 下面是一维波动方程. > wave:=diff(u(x,t),t,t)-c^2*diff(u(x,t),x,x); ∂2 ∂2 wave := ( 2 u(x, t)) − c2 ( 2 u(x, t)) ∂t ∂x 8.6 偏微分方程 195 对 u(x, t) 求解得到: > sol:=pdsolve(wave, u(x,t)); sol := u(x, t) = F1(c t + x) + F2(c t − x) 这个解是由两个任意函数 F1 和 F2 表示出来的. 为了画出这个解的图形, 需要指定两个特殊 的函数. > f1:=xi->exp(-xi^2); f1 := ξ → e(−ξ 2 ) > f2:=xi->piecewise(-1/2<xi and xi<1/2, 1,0); f2 := ξ → piecewise( 1 −1 < ξ and ξ < , 1, 0) 2 2 把这两个函数带入解的表达式得到 > eval(sol,{_F1=f1,_F2=f2,c=1}); u(x, t) = e(−(t+x) ) + 2 1 −t + x < 0 otherwise 1 1 and t − x < 2 2 用 rhs 命令得到解的函数表达式 > rhs(%); e(−(t+x) ) + 2 1 −t + x < 0 otherwise 1 1 and t − x < 2 2 再用 unapply 命令将解的表达式转换为函数. > f:=unapply(%,x,t); f := (x, t) → e(−(t+x) ) + piecewise(−t + x < 2 1 1 and t − x < , 1, 0) 2 2 现在我们可以画出这个解的图形. > plot3d(f,-8..8,0..5,grid=[40,40]); 196 第八章 微分方程 在 PDE 中变换因变量 在 pdsolve 命令的选项中, 提供了变量分离的工具. 例如考察下列一维热传导方程. > heat:=diff(u(x,t),t)-k*diff(u(x,t),x,x)=0; heat := ( ∂ ∂2 u(x, t)) − k ( 2 u(x, t)) = 0 ∂t ∂x 我们可以尝试求形如 X (x)T (t) 的解, 具体方法是使用 pdsolve 的 HINT 选项. > pdsolve(heat,u(x,t),HINT=X(x)*T(t)); (u(x, t) = X(x) T(t)) &where [{ ∂2 ∂ X(x) = c 1 X(x), T(t) = k c 1 T(t)}] ∂x2 ∂t 得到的结果是正确的, 但是 Maple 没有给出实际的解. 解这个方程的另一种方法是使用分离变量法, 具体做法是将 HINT 设为 ‘*‘, 然后使用’build’ 选项求解偏微分方程. > sol:=pdsolve(heat,u(x,t),HINT=‘*‘,’build’); sol := u(x, t) = C3 e(k c 1 t) C1 e( √ c 1 x) + C3 e(k √ e( c 1 t) c 1 x) C2 给解中的常量赋予特殊的值 > S:=eval(rhs(sol),{_C3=1,_C1=1,_C2=1,k=1,_c[1]=1}); S := et ex + et ex 然后画出方程的解 > plot3d(S,x=-5..5,t=0..5); 8.6 偏微分方程 197 将方程的解带入原方程检验得到 > eval(heat,u(x,t)=rhs(sol)); C3 k c 1 e(k − k ( C3 e(k %1 := e( √ c 1 t) c 1 t) C1 %1 + C3 k c 1 e(k c 1 t) C2 %1 C3 e(k c 1 t) C2 c 1 )=0 C1 c 1 %1 + %1 c 1 x) > simplify(%); 0=0 这说明我们的解法是合理的. 图解偏微分方程 许多偏微分方程的解可以用 PDEtools 程序包中的 PDEplot 命令画出. > with(PDEtools): PDEplot命令的一般用法是 PDEplot( pde, var, ini, s=range ) 这里 pde 是偏微分方程, var 是因变量, ini 是带有参数 s 的三维空间中的参数曲线, range 是 s 的变化范围. 考察下列偏微分方程. > pde:=diff(u(x,y),x)+cos(2*x)+diff(u(x,y),y)=-sin(y); pde := ( ∂ ∂ u(x, y )) + cos(2 x) + ( u(x, y )) = −sin(y ) ∂x ∂y 198 第八章 微分方程 我们用曲线 z = 1 + y 2 作为初始条件, 即 x = 0, y = s, z = 1 + s2 . > ini:=[0,s,1+s^2]; ini := [0, s, 1 + s2 ] PDEplot可以画出初始条件曲线和解曲面. > PDEplot(pde,u(x,y),ini,s=-2..2); 6 u(x,y) 4 2 0 –4 –2 y –1 x –2 0 2 4 2 1 0 为了画出这个曲面,Maple 计算那些基特征曲线, 初始条件曲线比在上面的图形更明显. > PDEplot(pde,u(x,y),ini,s=-2..2,basechar=only); 5 4 3 u(x,y) 2 1 0 –4 –2 y 0 2 4 2 1 0 –1 x –2 basechar=true选项告诉 PDEplot 除了画出特征曲线和曲面以外, 还要画出初始条件曲线. > PDEplot(pde,u(x,y),ini,s=-2..2,basechar=true); 8.6 偏微分方程 199 6 u(x,y) 4 2 0 –4 –2 y –1 x –2 0 2 4 2 1 0 第九章 向量与矩阵计算 在这一章中, 我们将介绍 Maple 中向量与矩阵的计算. 在 Maple 中提供了两个计算线性代 数的程序包: linalg 和 LinearAlgebra. 其中 LinearAlgebra 程序包是 Maple 6 以后的版本提 供的新程序包, 它增加了许多新的功能. 而 linalg 是老的线性代数程序包, 在 Maple 6 以前的 版本中都有这个程序包. 也许是为了保持兼容性, 在新的 Maple 版本中继续保留了 linalg 这 个程序包. 下面我们将分别介绍这两个程序包的使用方法, 以及他们的区别. 此外我们还要介绍 Maple 和 Matlab 的互相调用. 9.1 建立向量和矩阵 在这一节中, 我们主要介绍 linalg 程序包中建立向量和矩阵的命令. 在进行任何线性代数 的计算之前, 首先我们需要调用 linalg 程序包. > with(linalg); Warning, new definition for norm Warning, new definition for trace [BlockDiagonal , GramSchmidt , JordanBlock , LUdecomp , QRdecomp , Wronskian , addcol , addrow , adj , adjoint , angle , augment , backsub , band , basis , bezout , blockmatrix , charmat , charpoly , cholesky , col , coldim , colspace , colspan , companion , concat , cond , copyinto , crossprod , curl , definite , delcols , delrows , det , diag , diverge , dotprod , eigenvals , eigenvalues , eigenvectors , eigenvects , entermatrix , equal , exponential , extend , ffgausselim , fibonacci , forwardsub , frobenius , gausselim , gaussjord , geneqns , genmatrix , grad , hadamard , hermite , hessian , hilbert , htranspose , ihermite , indexfunc , innerprod , intbasis , inverse , ismith , issimilar , iszero , jacobian , jordan , kernel , laplacian , leastsqrs , linsolve , matadd , matrix , minor , minpoly , mulcol , mulrow , multiply , norm , normalize , nullspace , orthog , permanent , pivot , potential , randmatrix , randvector , rank , ratform , row , rowdim , rowspace , rowspan , rref , scalarmul , singularvals , smith , stack , submatrix , subvector , sumbasis , swapcol , swaprow , sylvester , toeplitz , trace , transpose , vandermonde , vecpotent , vectdim , vector , wronskian ] 在 Maple 中行向量和列向量是有区别的. 几乎所有的关于向量的命令都是以行向量作为参 数, 定义行向量的命令是 vector. > v1:=vector([a,b,c]); 200 9.1 建立向量和矩阵 201 v1 := [a, b, c] > v2:=vector(3,n->x^n); v2 := [x, x2 , x3 ] 注意: 在第二个例子中, 我们用无名的函数定义了行向量的每个元素, 此时 vector 的第一 个参数是行向量中元素的个数. 对于行向量可以做一些基本的计算, 但是由于最后名求值的原因, 我们必须使用 evalm 命 令才能求出值. > evalm(v1+1); [a + 1, b + 1, c + 1] > evalm(v1*2); [2 a, 2 b, 2 c] > evalm(v1+v2); [a + x, b + x2 , c + x3 ] 两个行向量之间的乘法是不可能的, 但有两个特殊的行向量乘法命令: dotprod 和 crossprod. dotprod 求行向量的点积, 即两个向量的相应元素相乘. > dotprod(v1,v2); a x + b x2 + c x3 > dotprod([1,I],[1,I]); 2 > dotprod([1,I],[1,I],orthogonal); 0 当第二个向量中包含复数时,dotprod 命令自动对第二个向量取复共轭. 对 dotprod 使用 orthogonal 选项可以取消对第二个向量取复共轭. 上面的第三个例子就是这种情况. crossprod 可以计算两个向量的叉积. 但是它只对含有三个元素的向量有意义. 202 > 第九章 向量与矩阵计算 crossprod(v1,v2); [b x3 − c x2 , c x − a x3 , a x2 − b x] Maple 没有提供直接输入列向量的命令, 要想输入列向量只有把它作为矩阵来处理. 例如 对 crossprod 命令的结果可以用 convert 命令转化为列向量. > convert(%,matrix); b x3 − c x2 c x − a x3 a x2 − b x 使用 matrix 命令输入列向量的命令如下: > v3:=matrix(3,1,[d,e,f]); d v3 := e f > v3:=matrix([[d],[e],[f]]); d v3 := e f 由于 dotprod 和 crossprod 命令的参数只能是行向量, 有时我们还需要将列向量转化为行 向量. > convert(v3,vector); [d, e, f ] 对于向量, 我们可以使用 norm 命令计算它的范数. > norm([a,b,c]); max(|a| , |b| , |c|) > norm([a,b,c],1); 9.1 建立向量和矩阵 203 |a| + |b| + |c| > norm([a,b,c],2); |a| + |b| + |c| 2 2 2 > norm([a,b,c],infinity); max(|a| , |b| , |c|) norm命令的变种 normalize 可以计算长度为 1 的规范化向量. > normalize([1,2,3]); 1√ 3√ 1√ 14, 14, 14 14 7 14 angle命令可以计算两个向量的夹角. > angle([1,0,0],[1,1,1]); arccos( 1√ 3) 3 在 linalg 程序包中, 定义矩阵的标准命令是 matrix. 它的通常用法是指定矩阵的维数并 给出矩阵元素的列表 > matrix(2,2,[a,b,c,d]); a c b d 当矩阵的元素以列表的列表形式给出时, 也可以不指定维数,Maple 会自己确定维数. > matrix([[a,b,c],[d,e,f],[g,h,i]]); d g a b e h c f i 定义矩阵也可以不给出矩阵元素的列表, 只指定维数. 然后用其它方法再给矩阵的元素赋 值. > A:=matrix(2,2); 204 第九章 向量与矩阵计算 A := array(1..2, 1..2, ) > print(A); A1, 1 A2, 1 A1, 2 A2, 2 > A[1,1]:=1; A1, 1 := 1 > A[1,2]:=2; A1, 2 := 2 > A[2,1]:=2; A2, 1 := 2 > A[2,2]:=1; A2, 2 := 1 > print(A); 12 21 像向量一样, 矩阵元素的定义也可以使用无名的函数. > matrix(3,3,(n,m)->n*x^m); x x2 x3 2 x 2 x2 3 x 3 x2 2 x3 3 x3 另一种定义矩阵的命令是 array, 此时我们把矩阵作为二维阵列来输. 但是使用 array 命令时要特别注意下标的用法. 只有使用 1..m, 1..n 形式的下标,Maple 才把输入的阵列当作 矩阵来看待. 我们可以用 type 命令检测以下输入的阵列是不是矩阵. 9.1 建立向量和矩阵 > 205 array(1..2,1..2,[[1,2],[3,4]]); 12 34 > type(%,matrix); true > > array(0..1,0..1,[[a,b],[c,d]]): type(%,matrix); false array命令与指标函数组合可以输入一些特殊的矩阵, 例如恒等矩阵就可以用如下的方法 输入. > array(1..3,1..3,identity); 0 1 00 10 0 0 1 可以与 array 命令组合的其它指标函数还有 antisymmetric, diagonal, sparse, symmetric 等. linalg 还提供了许多命令用来建立特殊的矩阵. diag 命令可以建立对角矩阵 > diag(a,b,c); 0 0 a0 b 0 0 0 c band命令可以建立带状矩阵. > band([1,x,-1],4); x −1 x 1 0 0 −1 x 1 0 1 0 0 0 −1 x toeplitz命令可以建立 Toeplitz 矩阵, 它的其它行的元素是第一行元素的循环. 206 > 第九章 向量与矩阵计算 toeplitz([a,b,c,d]); a b a b c c b d b c d c a b ba jacobian命令可以建立函数的 Jacobian 矩阵. > alias(f=f(x,y,z),g=g(x,y,z),h=h(x,y,z)); I, f, g, h > jacobian([f,g,h],[x,y,z]); ∂ ∂x ∂ ∂x ∂ ∂x f g h ∂ ∂y ∂ ∂y ∂ ∂y f g h ∂ ∂z ∂ ∂z ∂ ∂z f g h 有时我们为了测试的目的, 还需要随机矩阵. 使用 randmatrix 命令可以建立随机矩阵. > randmatrix(3,3); −35 79 −85 −55 −37 97 56 50 49 randmatrix可以有第三个参数来指定随机矩阵的结构. 可用的参数有 sparse, dense, symmetric, antisymmetric, unimodular. 其中省缺值是 dense. randmatrix 命令还可使用 entries=f 的 方式, f 是一个函数, 它用来生成矩阵的元素. > randmatrix(5,5,sparse); 83 0 −93 0 0 −18 −84 0 0 −62 0 0 53 0 41 0 0 88 54 0 0 0 0 0 0 生成矩阵的其它命令还有 blockmatrix, vandermonde, sylvester, hilbert 等, 有关这 些命令的用法请读者参见 Maple 的帮助. 对于已经定义的矩阵和向量, 我们可以用 submatrix 和 subvector 去裁剪. 例如 9.1 建立向量和矩阵 > > 207 m1:=matrix([[1,2,3,4],[a,b,c,d],[e,f,g,h],[w, x,y,z]]); 1 2 b f x 3 c g y 4 a m1 := e w d h z > submatrix(m1,1..2,2..3); 23 b c > submatrix(m1,[1,3],[2,4]); 2 f 4 h > subvector(m1,2,1..4); [a, b, c, d] > subvector(m1,[4,3,1],1); [w, e, 1] 此外还可以用 row 和 col 命令得到矩阵的指定的行或列. > row(m1,2); [a, b, c, d] > col(m1,3); [3, c, g, y ] 对已定义的矩阵进行处理的命令还有 copyinto 和 extend. copyinto(m1,m2,r,c) 命令的 作用是将第一个矩阵 m1 的项复制到第二个矩阵 m2 中, 复制的方法是将元素 m1[1,1] 复制到 m2[r,c] 的位置,m1 中其余的项也一同复制到 m2 中. > m2:=matrix(4,4,0); 208 第九章 向量与矩阵计算 0 m2 := 0 0 0 00 00 00 00 0 0 0 0 > copyinto(m1,m2,2,2); 0 0 0 0 0 1 2 0 a b 0ef 3 c g expand命令可以扩展矩阵, 具体用法是 expand(m1,m,n,x) 其中 m,n 是需要扩展的行和列 数,x 是一表达式, 用来初始化新增的项. > extend(m1,2,2,x); 1 a e w x x 2 b f x x x 3 c 4 xx x x g h x x y z x x x x x x xxxx d 9.2 矩阵计算 在 linalg 程序包中, 提供了各种矩阵计算的命令. 下面我们分别介绍这些命令. > with(linalg); Warning, new definition for norm Warning, new definition for trace 9.2 矩阵计算 209 [BlockDiagonal , GramSchmidt , JordanBlock , LUdecomp , QRdecomp , Wronskian , addcol , addrow , adj , adjoint , angle , augment , backsub , band , basis , bezout , blockmatrix , charmat , charpoly , cholesky , col , coldim , colspace , colspan , companion , concat , cond , copyinto , crossprod , curl , definite , delcols , delrows , det , diag , diverge , dotprod , eigenvals , eigenvalues , eigenvectors , eigenvects , entermatrix , equal , exponential , extend , ffgausselim , fibonacci , forwardsub , frobenius , gausselim , gaussjord , geneqns , genmatrix , grad , hadamard , hermite , hessian , hilbert , htranspose , ihermite , indexfunc , innerprod , intbasis , inverse , ismith , issimilar , iszero , jacobian , jordan , kernel , laplacian , leastsqrs , linsolve , matadd , matrix , minor , minpoly , mulcol , mulrow , multiply , norm , normalize , nullspace , orthog , permanent , pivot , potential , randmatrix , randvector , rank , ratform , row , rowdim , rowspace , rowspan , rref , scalarmul , singularvals , smith , stack , submatrix , subvector , sumbasis , swapcol , swaprow , sylvester , toeplitz , trace , transpose , vandermonde , vecpotent , vectdim , vector , wronskian ] > m1:=matrix([[a,b,c],[d,e,f]]); a d b c m1 := ef > m2:=matrix([[1,2,3],[4,5,6]]); 12 45 3 6 m2 := 我们首先定义了两个矩阵 m1, m2. 对 m1 和 m2 可以做一些初等的矩阵计算. 例如加法、 标 量乘法. 在进行初等矩阵计算的过程中, 我们必须使用 evalm 命令完成矩阵的求值. > evalm(m1+m2); a+1 d+4 b+2 c+3 e+5 f +6 > evalm(m1*3); 3a 3d 3b 3c 3e 3f 像列表和集合一样, 对矩阵也可以使用 map 命令. 210 > 第九章 向量与矩阵计算 map(sqrt,m2); 1 2 √ √ 2 5 √ √ 3 6 transpose命令可以对矩阵进行转置. 另一个转置命令是 htranspose, 它可以对复矩阵进行 共轭转置. > m3:=transpose(m2); 14 m3 := 2 5 36 矩阵乘法算子是 &*, 它与普通的乘法运算 * 是不同的, 这是因为矩阵乘法是非交换的. > evalm(m3&*m1); 2a + 5d 3a + 6d a + 4d b + 4e c + 4f 2b + 5e 2c + 5f 3b + 6e 3c + 6f 对于方阵, 可以用 inverse 命令求逆矩阵. > m4:=matrix([[1,2],[3,4]]); 12 34 m4 := > inverse(m4); −2 3 2 1 −1 2 矩阵的幂运算对于方阵也是可行的. > evalm(m4^4); 199 290 435 634 对于向量和矩阵之间的乘法, 也可使用 &* 运算. > v1:=vector([u,v,w]); 9.2 矩阵计算 211 v1 := [u, v, w] > evalm(v1&*m3); [u + 2 v + 3 w, 4 u + 5 v + 6 w] > m5:=matrix([[a,b,c],[d,e,f],[g,h,i]]); m5 := d g a b e h c f i 只有维数相容, 还可以使用 innerprod 命令做向量与矩阵之间的乘法. 这个命令的一般用 法是 innerprod(u,A1,A2,...,An,v), 其中 u 和 v 是向量,A1, A2,...,An 是矩阵. 当然他们的 维数必须相容. > innerprod(v1,m5); [u a + v d + w g, u b + v e + w h, u c + v f + w i] > innerprod(v1,m5,v1); u2 a + u v d + u w g + v u b + v 2 e + v w h + w u c + w v f + w 2 i 对于方阵, 可以用 det 命令计算行列式, 用 trace 命令计算矩阵的迹, 用 rank 命令计算矩 阵的秩. > det(m5); aei − af h − dbi + dch + gbf − gce > trace(m5); a+e+i > rank(m5); 3 > rank([[1,2],[3,6]]); 212 第九章 向量与矩阵计算 1 在 linalg 程序包中还提供了解线性方程组的专用命令 linsolve, 它可以处理各种线性方 程组的问题. linsolve 可以直接解线性方程组 Ax = b, 并给出解向量 x. > A:=matrix([[1,2,6],[4,-5,6],[9,8,7]]); 1 26 A := 4 −5 6 9 87 b:=vector([14,12,46]); b := [14, 12, 46] > > linsolve(A,b); 1178 628 600 , , 431 431 431 当方程组无解时 (例如矩阵 A 包含线性相关的行或列), linsolve 不返回任何结果. 此时可 以使用 kernel 或 nullspace 命令求出方程组 Ax = 0 的解. > A:=matrix([[1,2,3],[4,8,12],[1,-1,0]]); A := 4 1 2 8 3 12 1 −1 0 > > linsolve(A,b); kernel(A); {[−1, −1, 1]} > evalm(A&* %[1]); [0, 0, 0] 当线性方程组有无穷多解存在时, linsolve 可以用参数方式表达线性方程组的解. 命令的 用法是 linsolve(A,b, ’rank’, ’v’). 其中变量 v 告诉 Maple 用 vi 来表示独立的变量. 第三 个选项 rank 用来储存矩阵的秩. > A:=matrix([[1,2,3],[4,5,6],[0,0,0]]); 9.2 矩阵计算 A := 4 0 b:=vector([1,2,0]); b := [1, 2, 0] > 213 1 2 5 0 3 6 0 > sln:=linsolve(A,b,’rank’,’v’); 1 2 sln := − + v1 , − 2 v1 , v1 3 3 > evalm(A &* sln); [1, 2, 0] > 如果我们的方程组不是写成矩阵形式, 可以用 genmatrix 命令来生成矩阵形式. eqnsys:={x+2*y-3*z=10, 4*x+3*y-2*z=12, > 8*x+2*y-5*z=40}; eqnsys := {x + 2 y − 3 z = 10, 4 x + 3 y − 2 z = 12, 8 x + 2 y − 5 z = 40} > eqnmat:=genmatrix(eqnsys,[x,y,z]); eqnmat := 4 3 82 12 −3 −2 −5 不过 genmatrix 命令生成的矩阵仅是系数矩阵. 如果给 genmatrix 命令加上第三个参数 flag, 它就可以生成增广矩阵. > eqnmat:=genmatrix(eqnsys,[x,y,z],flag); 12 −3 10 eqnmat := 4 3 −2 12 8 2 −5 40 使用 submatrix 和 col 命令可以从增广矩阵中提取出系数矩阵. > A:=submatrix(eqnmat,1..3,1..3); 214 A := 4 3 82 12 −3 −2 −5 第九章 向量与矩阵计算 > b:=col(eqnmat,4); b := [10, 12, 40] > linsolve(A,b); 46 −44 −64 , , 15 15 15 使用 rowspace 和 colspace 命令还可以求出有矩阵的行向量和列向量生成的向量空间的 基. 下面我们就对增广矩阵 eqnmat 使用这两个命令. > rowspace(eqnmat); { 0, 1, 0, −44 −64 46 , 0, 0, 1, , 1, 0, 0, } 15 15 15 > colspace(eqnmat); {[1, 0, 0], [0, 1, 0], [0, 0, 1]} 在矩阵计算中, 经常需要求矩阵的特征多项式、特征值和特征向量. 使用 linalg 中的 charpoly, eigenvalues 和 eigenvects 可以求出矩阵的特征多项式、特征值和特征向量. > P1:=matrix([[1,2,3],[4,5,6],[1,-1,2]]); 1 2 3 P1 := 4 5 6 1 −1 2 > f:=charpoly(P1,’x’); f := x3 − 8 x2 + 12 x + 15 ew:=eigenvalues(P1); > 9.2 矩阵计算 215 ew := 5, 3 1√ 3 1√ + 21, − 21 22 22 > ev:=eigenvects(P1); ev := [5, 1, { −3 −9 3 1√ 1√ 3 1√ 21, 1, { − + 21, −1 − 21, 1 }], , , 1 }], [ + 2 2 22 26 3 3 1√ 3 1√ 1√ [− 21, 1, { − − 21, −1 + 21, 1 }] 22 26 3 在 linalg 程序包中还提供了大量的命令处理各种矩阵变换. 最重要的命令有 gausselim 和 ffgausselim. gausselim 使用 Gauss 消去法将矩阵变换为上三角矩阵, 从而可以迅速的求 出方程组 Ax = b 的解.ffgausselim 是 Gauss 消去法的无分式的实现. > P2:=randmatrix(4,4); 77 66 54 −5 99 −61 −50 −12 P2 := −18 31 −26 −62 1 −47 −91 −47 A:=gausselim(P2); 0 −815 A := 0 0 0 0 1 −47 −91 −1664 −75425 163 0 > −47 −908 −80114 163 −12267841 377125 > B:=ffgausselim(P2); 1 −47 −91 −47 0 −815 −1664 −908 B := 0 0 339503 387121 0 0 0 12267841 linalg程序包还提供了用于矩阵分解的命令, 主要包括:LUdecomp 和 QRdecomp. LUdecomp 命令的调用方法是 LUdecomp(A, P=’p’, L=’l’, U=’u’ , U1=’u1’, R=’r’, rank=’ran’, det=’d’) 其中 P 是主元置换因子,L 是下三角因子,U 是上三角因子,U1 是改进的上三角因子,R 是行约化因 子,rank 是矩阵 A 的秩, det 是矩阵 U 1 的行列式. 这些因子满足的关系是 A = P ∗ L ∗ U . 如果 216 第九章 向量与矩阵计算 将 U 进一步分解为 U 1 ∗ R, 那么就有 A = P ∗ L ∗ U 1 ∗ R. > A:=randmatrix(5,5); A := −85 −55 −37 −35 50 79 56 49 97 63 57 −59 45 −8 −93 92 43 −62 77 66 54 −5 99 −61 −50 > > LUdecomp(A,P=’p’,L=’l’,U=’u’,U1=’u1’,R=’r’,de t=’d’,rank=’ran’); −85 −55 793 0 17 0 0 0 0 0 0 −37 582 17 359064 3965 0 0 −35 483 17 21355 793 6815363 89766 0 97 2041 17 66742 305 19335800 44883 10148464579 27261452 LUdecomp返回的矩阵是上三角矩阵 U , 由于最后名求值的原因, 我们必须使用 evalm 命令 显示其它矩阵因子. > evalm(p); 0 1 0 0 0 0 00 10 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 > evalm(l); 1 −10 17 −57 85 −92 85 −54 85 0 1 −1630 793 −281 793 −679 793 0 0 1 −89129 89766 138521 119688 0 0 0 1 −32342577 27261452 0 0 0 0 1 9.2 矩阵计算 > 217 evalm(u); −85 −55 793 0 17 0 0 0 0 0 0 −37 582 17 359064 3965 0 0 −35 483 17 21355 793 6815363 89766 0 97 2041 17 66742 305 19335800 44883 10148464579 27261452 > evalm(u1); −85 −55 793 0 17 0 0 0 0 0 0 −37 582 17 359064 3965 0 0 −35 483 17 21355 793 6815363 89766 0 97 2041 17 66742 305 19335800 44883 10148464579 27261452 > evalm(r); 0 1 0 0 0 0 00 10 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 > d; −10148464579 ran; 5 > QRdecomp命令的调用方法是 QRdecomp(A, Q=’q’, rank=’r’) > A:=matrix([[1,2,4],[2,1,0],[4,5,6]]); 218 A := 2 4 R:=QRdecomp(A,Q=’q’,rank=’r’); √ 21 R := 0 0 8√ 21 7 3√ 14 7 0 4√ 21 3 √ 14 1√ 6 3 1 2 1 5 4 0 6 第九章 向量与矩阵计算 > > evalm(q); 1√ 21 21 2√ 21 21 4√ 21 21 1√ 1√ 14 6 7 3 3√ 1√ − 14 6 14 6 1√ 1√ 14 − 6 14 6 > r; 3 这里 QRdecomp 命令返回的是上三角矩阵 R, 正交矩阵 Q 则是由 QRdecomp 的 Q=’q’ 选项 返回. > A:=matrix([[1,2,4],[2,1,0],[4,5,6]]); A := 2 4 R:=QRdecomp(A,Q=’q’,rank=’r’); √ 21 R := 0 0 8√ 21 7 3√ 14 7 0 4√ 21 3 √ 14 1√ 6 3 1 2 1 5 4 0 6 > 9.2 矩阵计算 > 219 evalm(q); 1√ 21 21 2√ 21 21 4√ 21 21 1√ 1√ 14 6 7 3 3√ 1√ − 14 6 14 6 1√ 1√ 14 − 6 14 6 > r; 3 对于 m × n, m = n 的矩阵,QRdecomp 的结果则由选项 fullspan 来决定. 在省缺情况 下,fullspan 的值为 true, QRdecomp 的 R 因子的维数与矩阵 A 相同. > A:=matrix([[1,3],[0,2],[2,3],[4,1]]); 1 3 0 A := 2 4 2 3 1 > R1:=QRdecomp(A,Q=’Q1’,rank=’r1’); √ 21 R1 := 0 0 0 13 √ 21 21 1√ 6594 21 0 0 > evalm(Q1); 1√ 25 √ 3√ 21 6594 1570 0 21 3297 157 1√ 5√ 1√ 0 6594 − 1570 5 157 471 3 2√ 37 √ 59 √ 4√ 21 6594 − 1570 − 5 6594 4710 15 21 31 √ 7√ 2√ 4√ 21 − 6594 1570 5 21 6594 4710 15 > r1; 220 第九章 向量与矩阵计算 2 如果 fullspan=false, 则 Q 因子的维数与 A 相同, 如果 A 的秩与 A 的列秩相同, 则 Q 的 列向量张成 A 的列空间. > R2:=QRdecomp(A,Q=’Q2’,rank=’r2’,fullspan=false); √ R2 := 21 13 √ 21 21 1√ 6594 21 0 > evalm(Q2); 1√ 21 21 0 2√ 21 21 4√ 21 21 25 √ 6594 3297 1√ 6594 157 37 √ 6594 6594 31 √ − 6594 6594 9.3 LinearAlgebra 程序包 LinearAlgebra程序包是 Maple 6 以后的版本提供的用于线性代数计算的新程序包, 它包括 了 linalg 程包中几乎所有的功能. 由于它采用了新的数据结构, 对于计算大的数值矩阵, 这 个程序包的命令更有效. LinearAlgebra 程序包的基本数据类型是 Vector 和 Matrix, 它们分 别由 Vector 和 Matrix 命令来定义. 它们的实现是基于 Maple 6 提供的 rtable 数据结构, 这种 数据结构改进了矩阵的存储方式, 提高了矩阵的计算速度. linalg 程序包中定义的 vector 和 matrix 是用 array 数据结构定义的. 由于 array 数据结构和 rtable 数据结构是完全不同的两 种数据结构, 因此 linalg 和 LinearAlgebra 的差别也很大. 特别是对于包含浮点数的大的矩阵 和向量,LinearAlgebra 程序包中的命令效率更高. 主要的原因是,LinearAlgebra 程序包中的命 令调用了 Maple 内建的数值线性代数库. 这个库是由 Numerical Algorithms Group 提供的. 矩阵和向量的定义 在 LinearAlgebra 程序包中定义矩阵和向量的命令分别为 Matrix 和 Vector, 由于这两个 命令是用 rtable 数据结构来实现的, 他们的用法和 linalg 中的 matrix 与 vector 稍有些不 同. 下面我们通过一系列例子来详细的说明 Vector 和 Matrix 的用法: > with(LinearAlgebra); 9.3 LinearAlgebra 程序包 221 [Add , Adjoint , BackwardSubstitute , BandMatrix , Basis , BezoutMatrix , BidiagonalForm , BilinearForm , CharacteristicMatrix , CharacteristicPolynomial , Column , ColumnDimension , ColumnOperation , ColumnSpace , CompanionMatrix , ConditionNumber , ConstantMatrix , ConstantVector , CreatePermutation , CrossProduct , DeleteColumn , DeleteRow , Determinant , DiagonalMatrix , Dimension , Dimensions , DotProduct , Eigenvalues , Eigenvectors , Equal , ForwardSubstitute , FrobeniusForm , GaussianElimination , GenerateEquations , GenerateMatrix , GetResultDataType , GetResultShape , GivensRotationMatrix , GramSchmidt , HankelMatrix , HermiteForm , HermitianTranspose , HessenbergForm , HilbertMatrix , HouseholderMatrix , IdentityMatrix , IntersectionBasis , IsDefinite , IsOrthogonal , IsSimilar , IsUnitary , JordanBlockMatrix , JordanForm , LA Main , LUDecomposition , LeastSquares , LinearSolve , Map , Map2 , MatrixAdd , MatrixInverse , MatrixMatrixMultiply , MatrixNorm , MatrixScalarMultiply , MatrixVectorMultiply , MinimalPolynomial , Minor , Multiply , NoUserValue , Norm , Normalize , NullSpace , OuterProductMatrix , Permanent , Pivot , QRDecomposition , RandomMatrix , RandomVector , Rank , ReducedRowEchelonForm , Row , RowDimension , RowOperation , RowSpace , ScalarMatrix , ScalarMultiply , ScalarVector , SchurForm , SingularValues , SmithForm , SubMatrix , SubVector , SumBasis , SylvesterMatrix , ToeplitzMatrix , Trace , Transpose , TridiagonalForm , UnitVector , VandermondeMatrix , VectorAdd , VectorAngle , VectorMatrixMultiply , VectorNorm , VectorScalarMultiply , ZeroMatrix , ZeroVector , Zip ] > Vector(3,[1,2,3]); 1 2 3 从上面的例子可以看出,Vector 和 vector 的差别. 在 linalg 程序包中,vector 仅能产生行 向量, 但是 Vector 省缺的输出是列向量. 当然我们通过 Vector 的选项来得到行向量. > Vector[row](3,[4,5,6]); [4, 5, 6] 此外 Vector 还可以使用指标函数来生成向量. 对于 Vector, 系统内置的指标函数有 zero,unit,constant 等, 使用指标函数的具体用法如下: > Vector(4,shape=zero); 0 0 0 > 0 Vector(4,shape=unit[3],orientation=row); [0, 0, 1, 0] > Vector(3,shape=constant[2]); 222 2 第九章 向量与矩阵计算 2 2 上面的例子中,orientation=row 也是产生行向量的一种方法. 此外 Vector 还有其他许多 选项. 例如: readonly=true 用来指定向量是只读的. storage=name, 其中 name 是存储的结构, 它 的省缺值为 rectangular, 这个选项可以指定存储的方式. datatype=name, 其中 name 为任何 Maple 类型, 用来指定向量的数据类型. fill=value, 可以指定向量中未说明的项的值. 关于这些选项以及其他选项的详用法, 请读者参考 Maple 的帮助. 使用 rtable 结构, 我们还可以用更简单的方式定义向量和矩阵. 定义方法如下: > <1,2,3>; 1 2 3 用逗号分隔开的项代表行. > <1|2|3>; [1, 2, 3] 用 | 分隔的项代表列. 将两者组合起来就可以定义矩阵. > <<1,2,3>|<4,5,6>|<7,8,9>>; 2 3 1 47 5 8 69 这是定义矩阵的一种简便方式. 一般情况下,Matrix 命令的用法和 matrix 的用法是基本相同的. > Matrix(2,3,[[1,2,3],[4,5,6]]); 1 4 2 5 3 6 与 Vector 一样,Matrix 也具有 fill 选项, 它的省缺值是 0. 因此, 如果我们定义一个矩阵, 但是某些项没有给初始值, Matrix 将把未设置初始值的项赋值为 0. > Matrix(3); 0 0 0 00 0 0 00 如果给选项 fill 设置一个值, Matrix 就用这个值来填充. > Matrix(3,2,fill=3); 9.3 LinearAlgebra 程序包 3 3 3 3 3 3 223 此外, Matrix 还可以用指标函数来定义矩阵.Maple 系统提供了许多内置的指标函数来定义 特殊形式的矩阵. 例如:unit 单位矩阵,zero 零矩阵,diagonal 对角矩阵,band 带状矩阵,triangular[upper] 上三角矩阵, triangular[lower] 下三角矩阵,symmetric 对称矩阵,antisymmetric 反对称矩阵 等. 有关内置指标函数的用法请参照 Maple 的帮助文件 ?rtable indexfcn. 用户也可以自己定义指标函数, 例如: > f:=(i,j)->(i+j)*x^(i+j); f := (i, j ) → (i + j ) x(i+j ) > Matrix(3,3,f); 2 x2 3 x3 4 x4 5 x5 4 x4 3 x3 4 x4 5 x5 6 x6 除了使用 Matrix 定义矩阵以外,Maple 还提供了一些特殊矩阵的构造函数. 例如: BandMatrix 可以定义带状矩阵, 它的一般调用方法是 BandMatrix(L,n,r,c,outopts} 其中 L 是对角线及次对角线元素的列表的列表,n 是一非负整数, 用来标志次对角线的位置,r 和 c 表示矩阵的行和列, outopts 是一些其他构造矩阵的选项. 当我们省略掉 r,c 时,BandMatrix 会构造一个包含了所需的对角元的最小矩阵, 当省略掉 n 时,BandMatrix 将放置 iquo(nops(L),2) 个下对角线, 以保持下对角线和上对角线个数基本 相同. > L:=[[w,w],[x,x,x],[y,y,y],[z,z]]; L := [[w, w], [x, x, x], [y, y, y ], [z, z ]] BandMatrix(L); > x w 0 > y z y x w z y x 0 如果我们指定了 n 的值,BandMatrix 将把 L 的第 n + 1 个列表做为主对角线元素. BandMatrix(L,1); w 0 x y x w z 0 y z xy 224 第九章 向量与矩阵计算 如果指定了 r,c 的值,BandMatrix 将构造一个 r × c 的矩阵, 并根据 n 的值来确定对角线 的位置, 对于未定义的部分, 赋值为 0. > BandMatrix(L,1,5,4); w 0 0 0 x y x w 0 0 z 0 y z x y 0 0 00 对于其它的构造矩阵的命令, 我们就不再一一列举了, 读者可以参看 Maple 的帮助. 由于 Matrix 使用了 rtable 数据结构, 因此, 使用 Matrix 定义的矩阵和使用 matrix 定义 的矩阵是两种不同的数据结构, 这两种矩阵之间可以互相转换, 完成转换的命令是 convert. 例 如: > M:=matrix(3,3,[[1,1,1],[4,5,6],[7,8,9]]); 111 M := 4 5 6 7 8 9 11 1 6 9 M1:=convert(M,Matrix); > M1 := 4 5 78 > type(M,Matrix); false > type(M1,Matrix); true 反之, 由 Matrix 定义的矩阵也可转换为 matrix 类型的矩阵. > A:=<<1,2,3>|<4,2,1>|<7,8,9>>; 14 31 7 8 9 A := 2 2 > type(A,matrix); false > A1:=convert(A,matrix); A1 := 2 2 31 14 7 8 9 > type(A1,matrix); true 9.4 在 Maple 中调用 Matlab 225 在 linalg 程序包中, 执行许多矩阵计算命令时, 我们经常要使用 evalm 命令以便完成计 算, 这主要是因为 Maple 对于阵列数据类型需要进行最后名求值. 但是对于 LinearAlgebra 程 序包就没有这种问题, 用户可以直接完成矩阵的计算. 例如: 计算两个矩阵的乘积可以使用 MatrixMatrixMultiply 命令, 计算方阵的逆可以使用 MatrixInverse 命令. > B:=<<2,3,4>|<1,3,5>>; B := 3 4 2 1 3 5 > MatrixMatrixMultiply(A, B); 42 48 42 48 45 51 > MatrixInverse(A); 5 3 −29 6 1 −1 3 1 −2 −2 11 3 6 对于其它的矩阵计算命令, 限于篇幅, 我们就不再一一列举, 读者可以通过 Maple 的帮助系 统来学习. 9.4 在 Maple 中调用 Matlab 在 Maple 6 以后的版本中, 我们可以在 Maple 中调用 Matlab. 调用的命令是 with(Matlab);. 当然前提条件是在你的计算机中同时也安装了 Matlab 软件. 如果 with(Matlab) 返回一组函数 名, 就表示 Maple 调用 Matlab 成功. 此时系统会打开一个 Matlab 的窗口. 如果 with(Matlab) 命令没有返回, 或者返回一个错误, 就表示 Maple 和 Matlab 的链接没有建立起来. 链接失败的 原因可能很多, 但大多数情况都是路径设置不正确造成的, 此时你应当检查一下你的路径设置. > with(Matlab); [chol , closelink , defined , det , dimensions , eig , evalM , fft , getvar , inv , lu , ode45 , openlink , qr , setvar , size , square , transpose ] closelink(); > 使用 Matlab 程序包中的 closelink 命令可以切断与 Matlab 的链接, 同时关闭 Matlab. 使 用 Matlab 程序包中的 openlink 命令也可以建立与 Matlab 的链接. 这两个命令在调用时都没 有参数. > openlink(); 在使用 Matlab 程序包时, 可以使用两种类型的矩阵:MatlabMatrix 和 MapleMatrix. MatlabMatrix 是在 Matlab 中定义的矩阵, 在 Maple 中使用时需要将变量名加上双引号. 例 如我们在 Matlab 中定义矩阵 M : 226 M=[1 2 3 4 4231 5637 5 3 4 1] 第九章 向量与矩阵计算 在 Maple 中可以调用 Matlab 中的矩阵 M , 调用的方法如下: > det("M"); −45. > inv("M"); 1.22222222222222121 −1.88888888888888706 −.333333333333333037 .888888888888888064 .0666666666666667630 .133333333333333304 −.199999999999999954 .0666666666666666520 −.799999999999999376 −.111111111111111299 .333333333333333315 .111111111111111202 −.222222222222222071 1.39999999999999880 .399999999999999744 −.799999999999999376 其中 det 计算了矩阵 M 的行列式,inv 计算了矩阵的逆. MapleMatrix 是等价于矩阵的任何 Maple 表达式. 它既可以是用 array, matrix 定义的矩 阵, 也可以是用 Matrix 或 rtable 方式定义的矩阵. 使用 setvar 命令我们可以把 Maple 中定 义的矩阵传送到 Matlab 中. 但是在 Maple 中定义的矩阵必须是数值矩阵. 例如, 我们用 linalg 中的 randmatrix 生成一个 Maple 矩阵 maple a: > maple_a:=linalg[randmatrix](4,4); −91 −47 −61 41 −58 −90 53 −1 maple a := 94 83 −86 23 −84 19 −50 88 使用 setvar 命令将 maple a 定义为 Matlab 中的矩阵 matlab a, 此时在 Matlab 的内存区 中可以找到这个矩阵. > setvar("matlab_a",maple_a); defined命令可以检查 Matlab 中矩阵和函数是否存在, 也可以用来检查矩阵的属性. > defined("matlab_a"); true > defined("maple_a"); false 矩阵的属性有两种:variable, globalvar. 在 Maple 中我们可以用 setvar 命令的 globalvar 选项定义 Matlab 中的全局变量. > setvar("y", 10, ’globalvar’); 然后我们用 defined 命令来检查这个变量的属性. > defined("y", ’globalvar’); true 假设我们在 Matlab 中定义了一个 m 文件”example.m” 如下: function ret=example(x) 9.4 在 Maple 中调用 Matlab global y ret=x*y; 227 使用 defined 命令检查一下 Matlab 是否能找到这个 m 文件, 如果能找到, 我们就可以用 evalM 命令来求值. > defined("example", ’function’); true > defined("y",’variable’); true > evalM("answer = example(2)"); getvar命令可以把求值的结果带回 Maple. > getvar("answer"); 20. size命令和 dimensions 命令的作用是一样的, 都是求返回矩阵的维数. 唯一的区别是 size 是在 Matlab 中计算矩阵的维数, 而 dimensions 则是在 Maple 中计算矩阵的维数. > size("matlab_a"); [4, 4] > dimensions(maple_a); [4, 4] 在 Maple 中还可以使用 Matlab 中矩阵分解命令, 例如对前面定义的 Matlab 中的矩阵 matlab a 可以计算它的 QR 分解和 LU 分解. > qr("matlab_a"); .0527474605239980008 −.747094917654318346 −.371946535635639009 , −.349518108382528168 −.613687697456886894 −.132971723471955377 .695372578305804234 .566460382550993956 .316909533392540964 −.615786585548943166 .446651837232399096 −.506198639726420096 .721230520044219947 .212072124122806460 .422628546439905162 165.942761216028942 94.6290147574282798 −8.47882721541752460 −53.6510296367180786 0. 92.7596332789129862 −99.0587889311483991 73.5335386108606174 0. 0. 80.8793287839685746 −25.9986644451738088 0. 0. 0. 31.5191238036898228 −.548381859703621854 计算 QR 分解返回的结果是正交矩阵 Q 和上三角矩阵 R. 计算 LU 分解返回的结果是下 三角矩阵 L 和上三角矩阵 U , 其中 L 是一个置换 P 与下三角矩阵的乘积. 如果要得到 LU 分 解的三个矩阵 L, P, U 则需要加一个输出选项, 使用的方法是 lu(X, output=LUP). > lu("matlab_a"); 228 第九章 向量与矩阵计算 −.617021276595744684 −.416305092486869200 .534888126508877182 1. , 1. 0. 0. 0. −.893617021276595702 1. 0. 0. 94. 83. −86. 23. 0. 93.1702127659574444 −126.851063829787236 108.553191489361694 0. 0. −98.8479104818451618 24.4084265814112840 0. 0. 0. 45.3269582192651157 Matlab 中的 Cholesky 分解是针对对称正定矩阵的分解, 例如对下面定义的 Maple 中的矩 −.968085106382978734 .357958437999543300 1. 0. 阵 a 就可以进行 Cholesky 分解. > a:= array(1..2, 1..2, [[1,0],[0,3]]); a := 10 03 0. > chol(a); 1. 0. 1.73205080756887720 如果要对不是对称正定矩阵 X 进行 Cholesky 分解, 使用输出选项 output=RP 可以得到两 个输出 R 和 p. 其中 R 是满足 R ∗ R = XX 的最大维数的矩阵, 这里 XX 是 X 的左上角子矩 阵, 维数是 dim(R), 而 p = 1 + dim(R). > > b:=array(1..4,1..4,[[3,1,3,5],[1,6,4,2],[6,7,8,1],[3,3,7,3]]); 3135 1 6 4 2 b := 6 7 8 1 3373 (r,p) := Matlab[chol](b, ’output’=’RP’); 1.73205080756887720 .577350269189625842 1.73205080756887742 r, p := 0. 2.38047614284761666 1.26025207562520892 0. 0. 1.84709629036559764 , 4. 对于调用 Matlab 的其它命令, 我们就不作详细介绍. 第十章 数据处理 对于实际的数学问题, 我们经常需要处理各种各样的数据, 在这一章中, 我们将介绍 Maple 处理数据的各种方法. §10.1 • anova 方差分析程序包. • describ 描述统计程序包. • fit 线形回归程序包. • random 随机数生成程序包. • statevalf 统计分布计算程序包. • statplots 统计图形程序包. • transform 数据处理程序包. • importdate 导入数据文件. 统计程序包 Maple 的统计程序包由 7 个子程序包和一个命令组成. 它们分别是: 下面我们通过例子来说明 stats 的用法. 首先输入一些数据 > with(stats); [anova , describe , fit , importdata , random , statevalf , statplots , transform ] > with(describe); [coefficientofvariation , count , countmissing , covariance , decile , geometricmean , harmonicmean , kurtosis , linearcorrelation , mean , meandeviation , median , mode , moment , percentile , quadraticmean , quantile , quartile , range , skewness , standarddeviation , sumdata , variance ] > data:=[2,3.2,4,Weight(3,5),missing,Weight(1.. 4,3)]; data := [2, 3.2, 4, Weight(3, 5), missing , Weight(1..4, 3)] > mean(data); 2.881818182 在我们输入数据的过程中 missing 代表缺失的数据, Weight 代表分量. 其中 Weight(3,5) 表示 分量 3 重复了 5 次, 而 Weight(1..4, 3) 表示 1 到 4 之间的 3 个数据, 因此是 (1 + 4)/2 × 3. 在实际应用中, 输入数据的方式有许多种, 在 Maple 中, 我们还可以用 importdata 从文件 中导入数据. 例如给定数据文件 statdata.dat, 它包含的数据为 1 2 3.5 1*4 273 9 8 7.5 229 230 第十章 数据处理 在使用 importdata 命令时如果不加参数, 它将把数据按照一个序列读出. > importdata(‘c:\\statdata.dat‘); 1., 2., 3.5, 1., missing , 4., 2., 7., 3., 9., 8., 7.5 如果加上一个参数, 它将把数据按照指定的列数读出. > importdata(‘c:\\statdata.dat‘,3); [1., 1., 2., 9.], [2., missing , 7., 8.], [3.5, 4., 3., 7.5] 对于输入的数据, transform 子程序包提供了许多命令进行处理 > with(transform); [apply , classmark , cumulativefrequency , deletemissing , divideby , frequency , moving , multiapply , scaleweight , split , standardscore , statsort , statvalue , subtractfrom , tally , tallyinto ] > tst:=[Weight(1..3,4),3..4,Weight(4..7,4),8]; tst := [Weight(1..3, 4), 3..4, Weight(4..7, 4), 8] statvalue命令可以把输入的数据中的 Weight 命令移除, 并按照 Weight 的含义进行转换. 使用 classmark 命令可以把所有的表示范围的数据换为它们的平均值.frequency 命令把 Weight 类 型的数据替换为它的频数. cumulativefrequency 命令的作用和 frequency 相似, 但它把频数 进行了累加. > statvalue(tst); [1..3, 3..4, 4..7, 8] > classmark(tst),statvalue(classmark(tst)); 11 7 11 7 , 8] [Weight(2, 4), , Weight( , 4), 8], [2, , 2 2 22 frequency(tst); [4, 1, 4, 1] > > cumulativefrequency(tst); [4, 5, 9, 10] 这里的 apply 作用和 map 的作用是相同的, 但是 Weight 因子不变. multiapply 的作用和 zip 相同, 也是生成一个新的列表. > apply[sin](tst); [Weight(sin(1)..sin(3), 4), sin(3)..sin(4), Weight(sin(4)..sin(7), 4), sin(8)] > multiapply[(x,y)->x*y]([[1,2,3],[4,5,6]]); [4, 10, 18] §10.1 统计程序包 231 scaleweight命令把方括号内的数与 Weight 因子相乘, 在下面的例子中, count 是 describe 子程序包中的命令, 用来对数据列表记数. > scaleweight[1/count(tst)](tst); 2 1 2 1 [Weight(1..3, ), Weight(3..4, ), Weight(4..7, ), Weight(8, )] 5 10 5 10 statsort可以把数据列表进行排序. > statsort([6,missing,3,Weight(4..5,2)]); [3, Weight(4..5, 2), 6, missing ] split命令把数据列表按照方括号内的数字 n 分割为等长的 n 份, 但是数据的次序不变. 如 果数据的个数不能 n 等分,split 将在列表中插入一些数据 (使用 Weight 因子). > split[3]([3,4,5,6,7,8]); [[3, 4], [5, 6], [7, 8]] > split[4]([3,4,5,6,7,8]); 1 1 1 1 [[3, Weight(4, )], [Weight(4, ), 5], [6, Weight(7, )], [Weight(7, ), 8]] 2 2 2 2 tallyinto命令的作用是按照给定的模式对数据进行分类, 并统计数据出现的频数. 返回的 结果是 Weight 类型的数据. 在下面的例子中, 第一项为 Weight(1..3,4) 是因为数据中 1 ≤ x < 3 的 x 共有 4 项. > tallyinto([1,2,2,2,3,4,5,6,6],[1..3,3..4,4..7 ]); [Weight(1..3, 4), 3..4, Weight(4..7, 4)] > tst:=[seq(rand(100)(),n=1..100)]; tst := [81, 70, 97, 63, 76, 38, 85, 68, 21, 9, 55, 63, 57, 60, 74, 85, 16, 61, 7, 49, 86, 98, 66, 9, 73, 81, 74, 66, 73, 42, 91, 93, 0, 11, 38, 13, 20, 44, 65, 91, 95, 74, 9, 60, 82, 92, 13, 77, 49, 35, 61, 48, 3, 23, 95, 73, 89, 37, 57, 99, 94, 28, 15, 55, 7, 51, 62, 97, 88, 42, 97, 98, 27, 27, 74, 25, 7, 82, 29, 52, 4, 85, 45, 98, 38, 76, 75, 74, 23, 0, 19, 1, 49, 47, 13, 65, 44, 11, 36, 59] > bins:=[seq(10*n..10*n+10,n=0..9)]; bins := [0..10, 10..20, 20..30, 30..40, 40..50, 50..60, 60..70, 70..80, 80..90, 90..100] > tallyinto(tst,bins); [Weight(30..40, 6), Weight(40..50, 10), Weight(50..60, 7), Weight(60..70, 12), Weight(70..80, 13), Weight(80..90, 10), Weight(90..100, 14), Weight(0..10, 11), Weight(10..20, 8), Weight(20..30, 9)] 232 > 第十章 数处理 frequency(%); [6, 10, 7, 12, 13, 10, 14, 11, 8, 9] 在统计程序包中, describe 子程序包提供了一些常规的统计计算命令. 例如:count 可 以计算输入数据的个数,countmissing 则能给出输入数据中缺失数据的个数.range 可以求 出数据的范围,mean 给出平均值. > > tst:=[1,2,3,Weight(4..6,3),Weight(missing,2)] : count(tst); 6 > countmissing(tst); 2 > range(tst); 1..6 > mean(tst); 7 2 计算其它平均值的命令还有 quadraticmean, harmonicmean 和 geometricmean. 计算标准差 和方差的命令是 standarddeviation 和 variance, 它们的变种还有 coefficientofvariation 和 meandeviation. > quadraticmean(tst); 1√ 534 6 > standarddeviation(tst); 1√ 93 6 > variance(tst); 31 12 对于多组数据的比较, 可以使用的命令是 linearcorrelation 和 covariance. > > tst:=[1,2,3,4],[2,4,5,6]: linearcorrelation(tst); 13 √ 7 35 > evalf(%); .9827076297 §10.1 统计程序包 > 233 covariance(tst);evalf(%); 13 8 1.625000000 对于其它描述统计的命令读者可以通过 Maple 的帮助学习, 在这里就不做详细的说明了. 下面我们介绍 statplots 子程序包的用法. statplots 的命令主要用来画各种统计图形, 我们首先看一下直方图. > > > > with(stats): with(statplots): with(random): with(describe): f:=normald[5,1]: data:=[f(100)]: with(transform): with(plots): 为了画直方图, 我们首先生成一组数据, 命令 normald[5,1] 定义了一个满足正态分布的随机分 布函数 f , 其中 µ = 5, σ = 1. 下一个命令使用函数 f 生成了 100 个随机数据. 对于其它的随机 分布, 可以用类似的方式得到, 有关细节可参照 Maple 的帮助. > tallyinto(data,[seq(n*0.5..(n+1)*0.5,n=1..20) ]); [Weight(9.0..9.5, 0), Weight(8.5..9.0, 0), Weight(6.0..6.5, 4), 2.5..3.0, Weight(4.0..4.5, 15), Weight(9.5..10.0, 0), Weight(6.5..7.0, 4), Weight(3.0..3.5, 4), Weight(.5..1.0, 0), Weight(10.0..10.5, 0), Weight(7.0..7.5, 4), Weight(4.5..5.0, 21), Weight(3.5..4.0, 14), Weight(1.0..1.5, 0), 7.5..8.0, Weight(5.0..5.5, 12), Weight(1.5..2.0, 0), Weight(8.0..8.5, 0), Weight(5.5..6.0, 20), Weight(2.0..2.5, 0)] > histdata:=scaleweight[1.0/count(")]("); histdata := [Weight(9.0..9.5, 0), Weight(8.5..9.0, 0), Weight(6.0..6.5, .04000000000), Weight(2.5..3.0, .01000000000), Weight(4.0..4.5, .1500000000), Weight(9.5..10.0, 0), Weight(6.5..7.0, .04000000000), Weight(3.0..3.5, .04000000000), Weight(.5..1.0, 0), Weight(10.0..10.5, 0), Weight(7.0..7.5, .04000000000), Weight(4.5..5.0, .2100000000), Weight(3.5..4.0, .1400000000), Weight(1.0..1.5, 0), Weight(7.5..8.0, .01000000000), Weight(5.0..5.5, .1200000000), Weight(1.5..2.0, 0), Weight(8.0..8.5, 0), Weight(5.5..6.0, .2000000000), Weight(2.0..2.5, 0)] 使用 tallyinfo 和 scaleweight 命令对数据进行分类处理以后, 就可以用 histogram 命令 画出直方图. 但是为了同时画出正态分布曲线, 我们先用 p1 记录这个直方图. 画正态分布曲线的方法是使用 statevalf 子程序包, 这个子程序包的功能是计算各种统 计分布函数的值. 它的一般用法是 statevalf[function,distribution](args). 对于连续的分 布,function 可取的值及含义如下: cdf 累积密度函数 icdf 逆累积密度函数 pdf 概率密度函数 对于离散的分布,function 可取的值及含义如下: 234 dcdf 离散累积概率函数 idcdf 逆离散累积概率函数 pf 概率函数 第十章 数据处理 statevalf的第二个参数是 distribution, 它可以是任何一种统计分布函数, 例如我们前面定 义的分布函数 f . 下面的命令 fpdf:=statevalf[pdf,f] 定义了一个函数, 用它可以画出 µ = 5, σ = 1 的正态分布曲线. > > > > p1:=histogram(histdata): fpdf:=statevalf[pdf,f]: p2:=plot(fpdf(x),x=0.5..10): display([p1,p2]); 0.4 0.3 0.2 0.1 0 2 4 x6 8 10 使用 statplots 子程序包中的 scatter2d 和 quantile2 命令可以画二维数据的散点图. 点 的坐标来自于两个列表. 下面我们先建立两个列表,scatter2d 以这两个列表作为参数画出散点图. > > > datax:=[normald[5,1](100)]: datay:=[normald[7,3](100)]: scatter2d(datax,datay); 16 14 12 10 8 6 4 2 02 3 4 5 6 7 quantile2命令的用法和 scatter2d 的用法相同, 唯一的区别是,quantile2 先把两个列表 的数据进行从小到大的排序, 然后在画出散点图. > quantile2(datax,datay); §10.1 统计程序包 235 16 14 12 10 8 6 4 2 02 3 4 5 6 7 对于一维数组, 我们可以用 scatter1d,boxplot,notchedbox 等命令来画图. > scatter1d(datax); 2 1.5 1 0.5 02 3 4 5 6 7 scatter1d命令将所有的点画在 y = 1 的轴上. 当使用 jittered 选项时, scatter1d 会 在 0 到 1 之间随机选择 y 的值来画散点图. > scatter1d[jittered](datax); 0.8 0.6 0.4 0.2 02 3 4 5 6 7 boxplot命令可以用箱式图描述数据的分布情况,notchedbox 的作用和 boxplot 相似, 其方括号内的参数可以指定箱子的纵向位置和宽度. > boxplot(datax); 236 第十章 数据处理 7 6 5 4 3 -0.4 -0.2 2 0 0.2 0.4 > notchedbox[5,2](datax); 7 6 5 4 3 2 4 4.5 5 5.5 在 statplots 子程序包中还有三个命令分别是:xshift,xscale 和 xyexchange 它们都可 以作用到二维图形上.xshift 的作用是在 x 轴方向平移图形,xscale 的作用是对 x 值进行缩 放,xyexchange 命令可以交换 x 和 y 的坐标, 作用到二维图形上就是旋转 90◦ . 下面我们画一个 二维图形, 然后对它进行平移、缩放、旋转. > > > > > P1:=plot(x^2,x=0..3): P2:=xshift[3](P1): P3:=xscale[2](P1): P4:=xyexchange(P1): plots[display]([P1,P2,P3,P4]); 3 2.5 2 1.5 1 0.5 0 1 2 3 x 4 5 §10.2 插值与回归 237 §10.2 插值与回归 插值的含义是给出一个光滑的曲线让它通过给定的点. 在 Maple 中有两个命令处理插值问 题, 它们分别是 interp 和 spline. interp 可以求出一个多项式, 使它恰好通过指定的点. 例如, 我们首先随机给出 10 个点 > datax:=[seq(i,i=1..10)]; datax := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] > datay:=[seq(rand(10)(), i=1..10)]; datay := [1, 0, 7, 3, 6, 8, 5, 8, 1, 9] > dataxy:=zip((x,y)->[x,y],datax,datay); dataxy := [[1, 1], [2, 0], [3, 7], [4, 3], [5, 6], [6, 8], [7, 5], [8, 8], [9, 1], [10, 9]] 使用 interp 命令可以求出一个次数为 9 的多项式 f > f:=interp(datax,datay,x); 17 517 8 11699 7 3719 6 27323 5 176741 4 652577 3 x9 − x+ x− x+ x+ x− x 51840 40320 60480 2880 17280 5760 3240 1816483 2 1669153 + x− x + 293 3360 2520 f := 然后我们画出这条曲线以及 10 个点 > > > p1:=plot(f,x=0.9..10.1): p2:=plot(dataxy,style=point,symbol=circle,color=blue): plots[display]([p1,p2]); 25 20 15 10 5 0 -5 -10 2 4 x6 8 虽然 interp 求出的函数非常光滑, 但是它的次数太高, 要用这个函数进行外推的话偏差太 大. 另外一种插值方法是样条插值, 命令为 spline, 它采用分段插值的方法来得到一个分段 光滑的曲线, 通常每一段都是 2 次或 3 次的曲线. 下面我们就用上面给出的数据进行样条插 值. > readlib(spline): 由于 spline 不是 Maple 中的常用命令, 所以我们需要用 readlib 命令来调用它. spline 的前两个参数分别为 x 和 y 坐标的数据列表, 第三个参数为函数的变量, 第四个参数为分段曲 238 第十章 数据处理 线的次数, 通常我们使用 2 次或 3 次的曲线, 但是 spline 也可以计算更高次数的曲线. 下面我 们给出了 2 次和 3 次样条的函数和图象. > g:=unapply(spline(datax,datay,x,2),x); g := x → piecewise(x < 2, 2 x − x2 , x < 3, 40 − 38 x + 9 x2 , x < 4, −221 + 136 x − 20 x2 , x < 5, 531 − 240 x + 27 x2 , x < 6, −844 + 310 x − 28 x2 , x < 7, 992 − 302 x + 23 x2 , x < 8, −968 + 258 x − 17 x2 , x < 9, 568 − 126 x + 7 x2 , 649 − 144 x + 8 x2 ) > h:=unapply(spline(datax,datay,x,3),x); h := x → piecewise(x < 2, 2 + 207809 124177 2 124177 3 x− x+ x , x < 3, 40545 13515 40545 1148902 968123 717227 2 59305 3 − x+ x− x , x < 4, 13515 8109 13515 8109 828787 649288 1047052 2 291568 3 − + x− x+ x , x < 5, 2703 2385 13515 40545 1012301 9674344 678968 2 139937 3 − x+ x− x , x < 6, 2703 40545 13515 40545 314326 3392569 260183 2 212 3 − x+ x− x , x < 7, 2703 40545 13515 153 17051674 24542387 1291759 2 202477 3 − + x− x+ x , x < 8, 13515 40545 13515 40545 778912 50458483 2279711 2 307733 3 − x+ x− x , x < 9, 255 40545 13515 40545 25348512 81535373 3220033 2 75947 3 − + x− x+ x, 4505 40545 13515 8109 395164 2 197582 3 4282833 58752658 − x+ x− x) 901 40545 2703 40545 > > > p3:=plot(g(x),x=1..10): p4:=plot(h(x),x=1..10,color=green): plots[display]([p2,p3,p4]); 14 12 10 8 6 4 2 0 -2 2 4 x6 8 回归的含义是构造一条近似的曲线, 使它尽可能的接近给定的数据点. 在统计程序包的 fit 子程序包中的 leastsqr 命令可以用来构造回归函数. 下面我们通过例子来说明 leastsqr 的用 法. 首先我们生成一组随机数据, 然后画出它们的散点图 p1. §10.2 插值与回归 > > > > 239 with(stats): with(fit): with(statplots): with(plots): datax:=[seq(rand(100)(),i=1..50)]: datay:=map(x->evalf(sin(x/100*Pi)+rand(30)()/ 100,4),datax): p1:=scatter2d(datax,datay): 我们用一个二次曲线 y = a0 + a1 ∗ x + a2 ∗ x2 来拟合这些数据. 使用的命令是 leastsquare. 它的参数有两组. 在方括号中的第一组参数用来描述拟合曲线, 包括: 变量列表, 拟合函数形式, 以及拟合函数的参数集合. 在圆括号中的第二组参数是数据的列表. 具体用法如下: > leastsquare[[x,y],y=a0+a1*x+a2*x^2,{a0,a1,a2 }]([datax,datay]); y = .1866122788 + .03911177735 x − .0003984139193 x2 > f:=rhs(%); f := .1866122788 + .03911177735 x − .0003984139193 x2 拟合函数 f 的曲线用 p2 表示. 用 display 命令同时显示 p1 和 p2. > > p2:=plot(f,x=0..100): display([p1,p2]); 1.2 1 0.8 0.6 0.4 0.2 0 20 40 x 60 80 100 leastsquare还可以处理多维的简单拟合函数. 例如 y = a1 ∗ x1 + a2 ∗ x2 + · · ·. > > datax1:=[1,2,3]:datax2:=[2,3,4]:datay1:=[3,7, 8]: leastsquare[[x1,x2,y],y=a1*x1+a2*x2,{a1,a2} ]([datax1,datax2,datay1]); 3 y = x1 + x2 2 需要说明的是:leastsquare 命令使用的是最小二乘法. 它对于拟合函数有一些限制. 首先 它要求拟合的参数必须是线性的. 对于非线性的函数, 例如 f = a0 + a1 ∗ sin(a2 ∗ x) leastsquare 命令无法使用. 此外拟合函数本身也必须是比较简单的函数, 通常是多项式函数. 对于复杂的 函数, 例如:f = a0 + a1 ∗ sin(x/10) + a2 ∗ sin(x/20) + a3 ∗ sin(x/40),leastsquare 虽然也可以计 算, 但是经过很长时间以后也未必能返回正确的结果. 这样的问题是从 Maple V Release 4 以后 的版本才出现的. 在 Maple V Release 3 版本中, 还可以求出正确的结果. 不过使用共享库中的 fit 命令, 还是可以解决上述问题的. 下面我们就介绍一下 fit 命令 的用法. 首先我们调入共享库. > > with(share): readshare(fit,numerics): 240 第十章 数据处理 对于第一个例子的数据,fit 命令的用法如下: > f1:=fit(datax,datay,[1,x,x^2],x); f1 := .1866122947 + .03911177634 x − .0003984139092 x2 可以看出计算的结果和 leastsquare 命令是一样的. 对于复杂的非线性函数,fit 同样有效, 例如我们的拟合函数为 f = a0 + a1 ∗ sin(x/10) + a2 ∗ sin(x/20) + a3 ∗ sin(x/40). 计算的结果如下: > f:=fit(datax,datay,a0+a1*sin(x/10)+a2*sin(x/2 0)+a3*sin(x/40),x); f := .0321572783 + .07881337051 sin( > > 1 1 1 x) + .3489868374 sin( x) + 1.007798304 sin( x) 10 20 40 p3:=plot(f,x=0..100): display([p1,p3]); 1.2 1 0.8 0.6 0.4 0.2 0 20 40 x 60 80 100 fit也可以处理多维的情况. 例如: > > > > > datax1:=[seq(rand(100)()/100,i=1..50)]: datax2:=[seq(rand(100)()/100,i=1..50)]: datax12:=zip((x1,x2)->[x1,x2],datax1,datax2): datay:=zip((x1,x2)->x1+2*x2+cos(x1*x2)+rand(1 00)()/1000,datax1,datax2): f:=fit(datax12,datay,a1*x1+a2*x2+a3*cos(x1*x2 )+a4*x1*x2,[x1,x2]); f := 1.058776261 x1 + 2.055120765 x2 + 1.000361369 cos(x1 x2 ) − .05052181835 x1 x2 §10.3 极大与极小 Maple 可以使用 maximize, minimize 和 extrema 命令计一维和多维函数的极大值和极 小值. 这三个命令都需要用 readlib 来调入. 我们首先介绍 maximize 和 minimize 的使用方法. > > readlib(minimize):readlib(maximize): minimize((x+2)/(x^2+1)); 1√ 1− 5 2 §10.3 极大与极小 > 241 minimize(sin(x)); −1 对于多维函数 minimize 命令同样可以使用. > minimize(x^2+y^2+5); 5 对于多维函数, 我们还可以指定对某个变量求极值. > minimize(x^2+3*y^3-1,{x}); 3 y3 − 1 此外还可以在一个指定的范围内求极值. > minimize(x^4+2*y^2*x,{x,y},{x=-10..10,y=2. .5}); 75 RootOf(2 Z 3 + 25, −2.320794417) 2 > evalf(%); −87.02979064 在 Maple V Release 4 版本中,minimize 有一个选项 ’infinite’. 对于那些无法求出极值的 函数, 使用这个选项会返回函数自身. 例如函数 ex 是没有极值的, 因此 minimize 不返回值, 但是 使用了 ’infinite’ 选项后, 它会返回 ex 自身. 不过在 Maple 6 以后的版本中,minimize(exp(x) 得到的结果是 0, 它实际上是 limx→−∞ ex 的极限值. > > minimize(exp(x)); minimize(exp(x),’infinite’); ex 对于 maximize 命令, 它实际上是把表达式加了减号, 然后取极小值, 再对极小值取减号得 到的结果. 因此我们不再举例来说明它的用法. 在 Maple 6 以后的版本中,minimize 增加了一个 location 选项, 这个选项有两个选择:true 和 false. 它的省缺值是 false, 如果将 location 的值设置为 true,minimize 就可以获得极值 点的位置. 例如: > minimize((x+1)/(x^2+1),location=true); √ √ √ 2 2 √ √ − , {[{x = −1 − 2}, − ]} 2+1 (−1 − 2) (−1 − 2)2 + 1 242 第十章 数据处理 minimize和 maximize 命令可以在无约束的条件下求函数的极值, 对于有约束条件, 可以使 用 extrema 命令来求极值. 调用 extrema 的方法有两种. 一种是使用 readlib 命令, 另一种是 使用 student 程序包. extrema 命令的一般用法是: extrema(expr, constraints, vars, ’s’). 其中第二个选项 contraints 是约束条件, 如果没有约束条件, 可以用 { } 来代替. 例如: > > readlib(extrema): extrema(a*x^2+b*x+c,{},x); {− 1 b2 − 4 c a } 4 a 约束条件通常是等式, 例如: > extrema(x*y*z,x^2+y^2+z^2=1,{x,y,z}); 1 1 {max(− RootOf(3 Z 2 − 1), RootOf(3 Z 2 − 1), 0), 3 3 1 1 2 min(− RootOf(3 Z − 1), RootOf(3 Z 2 − 1), 0)} 3 3 第四个选项是一个未赋值的变量, 它可以带回达到极值点的位置. > extrema(x*y,x+y=1,{x,y},’s’); 1 {} 4 > s; {{x = 1 1 , y = }} 2 2 §10.4 线性优化 Maple 对于线性优化问题提供了专门的程序包 simplex. 它包括一些处理线性优化的命令, 其中最重要的命令是 minimize 和 maximize. 这两个命令的基本用法是:minimize(f,C), 其中 f 是一个线性表达式, 它是目标函数, C 是一系列线性约束条件的集合, 这些约束条件必须是带有 ≤ 或 ≥ 的线性不等式, simplex 程序包不能处理严格不等式. > with(simplex); Warning, new definition for maximize Warning, new definition for minimize [basis , convexhull , cterm , define zero , display , dual , feasible , maximize , minimize , pivot , pivoteqn , pivotvar , ratio , setup , standardize ] §10.4 线性优化 243 需要注意的是, 当我们调用 simplex 时, 它给出了 maximize 和 minimize 的新定义. 此时 maximize 和 minimize 是 simplex 程序包中的命令, 而不再是求函数极值的命令. maximize 的用法如下: > maximize(300*x1+400*x2,{2*x1+4*x2<=170,2*x1+ 2*x2<=150,6*x2<=180}); {x1 = 65, x2 = 10} maximize还有其它的参数, 第三个参数说明变量的类型, 可用的值为 NONNEGATIVE 或 UNRESTRICTED. 省缺参数为 UNRESTRICTED, 它的含义是对所有变量都是无约束的. NONNEGATIVE 的含义是所有 的变量都是非负的. 第四个参数必须是一个未赋值的变量, 它用来返回对最优化的描述, 第五个 参数可用来返回变换的情况. > maximize(6*x1+10*x2+8*x3+12*x4,{3*x1+2*x2+2* x4<=60, x1+x2+2*x3+x4<=45, 2*x1+4*x4<=90, 4*x1+4*x3+3*x4<=120,x1+2*x2+2*x3+2*x4<=30},NONNEGATIVE, ’eqsys’); {x3 = 0, x2 = 0, x4 = > 15 , x1 = 15} 2 eqsys; 3 15 1 3 45 1 1 3 + SL1 − x2 − x3 , SL2 = + SL1 + SL5 − x3 , SL5 + 4 2 4 2 2 4 4 2 75 5 1 7 SL3 = 30 + 4 x2 + 2 SL5 + 4 x3 , SL4 = + SL1 + 3 x2 + SL5 − x3 , 2 4 4 2 1 1 x1 = − SL1 + 15 + SL5 + x3 } 2 2 {x4 = − 需要说明的是 maximize 在上例中仅得到了一个最优解, 还有一个最优解 x1 = x2 = x3 = 0, x4 = 15 并没有求出来. 第十一章 是一种完善的程序语言. Maple编程 Maple用户的数量很多, 许多用户仅仅是交互式的使用Maple系统. 他们并没有意识到Maple也 编写Maple程序实际上是非常简单的, 只要在你每天使用的一系列命令前后分别加上proc( )及end即可. Maple系统中含有数以千计的命令, 其中的百分之八十自身就是Maple程序, 用户 完全可以尽情地检测和修改他们, 以适应自己的需要, 或者扩充它们来解决新的问题. 一些在 其他语言中需要花费若干天或者若干星期编写的应用程序, 在Maple中只需几个小时便可完成. Maple之所以有如此高效率, 主要因为Maple是交互式语言, 语言的可交互性大大方便了程序的 测试与修改. Maple编程不需要专门的技巧. 区别于传统语言的是, Maple含有丰富的功能强大的命令, 他 们将一些复杂的, 通常需要写数页编码的任务浓缩为一个命令. 例如, solve命令可用于求方程 组的解. Maple还带有一个巨大的程式库, 其中包括图形处理程序. 因此, 利用Maple自带的程序 构造应用程序, 是一件极为容易的事情. 下面我们通过一个简单的程序来说明Maple程序的基本结构: > > > > > > > > > > > test:=proc(X::list) local n,i,total; n:=nops(X); if n=0 then ERROR(‘empty list‘) end if; total:=0; for i from 1 to n do total :=total+X[i]; end do; total/n; end proc: test([1,2,a]); 1+ test([a,b,c]); 1 1 1 a+ b+ c 3 3 3 1 a 3 > > X:=[1.3,5.3,11.2,2.1,2.1]; X := [1.3, 5.3, 11.2, 2.1, 2.1] > test(X); 4.400000000 这个程序计算一个列表的平均值. 在其中我们用到了条件语句、循环语句以及局部变量的 声明等. 这些都是Maple程序的基本组成部分. 244 §11.1 程序的基本结构 245 §11.1 程序的基本结构 Maple以proc命令开始一个过程.以end proc结束这个过程.一个过程的基本结构如下: name:=proc(P) local L; global G; options O; description D; B end proc; 其中name是用户定义的过程的名字, P是过程的形式参数, local L是局部变量, global G是过 程的全局变量, options是过程的选项, description是过程的描述域, B是过程的体, 最后用end proc语句结束一个过程. 象其他Maple对象一样, 我们定义过程时通常都赋给它一个名字. 而且通常都是用proc开 始, 以end proc结束. 但是也有一些特殊情况. 例如我们经常要定义函数, 定义函数实际上就是 定义一个过程. 例如下列定义的函数 > F := (x,y) -> x^2 + y^2; F := (x, y ) → x2 + y 2 实际上就是一个最简单的过程. 用过程语言来描述就是: > F :=proc(x,y) x^2+y^2 end proc; F := proc(x, y ) x2 + y 2 endproc 这两个定义是等价的. 在这个过程的定义中, 我们省略了局部变量与全局变量, 选项与描述体等 部分, 仅有一个过程体. 调用过程的方式和调用函数的方式是一样的, > F(2,3); 13 当Maple执行过程体内的语句时, 它用函数调用时提供的实际参数A代替形式参数P . 值得注意 的是: Maple在进行代换之前将计算实际参数A的值. 通常, 过程执行后返回的结果是过程体中 最后一个可执行语句计算的结果. 无名过程 过程的定义是合法的Maple表达式. 可以建立、使用、调用过程, 而不必为过程指定一个名 字. 不过定义一个无名过程通常是和其它过程组合在一起使用的. > (x)-> x^2; x → x2 可以用下列方式调用无名过程 > ( x -> x^2)(t); t2 246 > 第十一章 Maple编程 proc(x,y) x^2+y^2 end proc(u,v); u2 + v 2 无名过程的常用方式是与map命令联合使用. > map(x->x^2, [1,2,3,4]); [1, 4, 9, 16] 可以把几个无名过程一起使用, 如果合适, 也可以与其它命令一起使用, 例如微分算子D. > D(x -> x^2); x → 2x > F := D(exp+2*ln); 1 F := exp + 2 (a → ) a 可以把结果F直接作为变量使用. 过程简化 当建立一个过程时, Maple并不执行这个过程, 但是对过程体进行简化. > > > > proc(x) local t; t := x*x*x + 0*2; if true then sqrt(t); else t^2 end if; end proc; proc(x) local t; t := x3 ; sqrt(t) end proc 过程简化是程序优化的一个简单形式. 条件语句 与其它程序语言相同的,Maple的过程体也是由赋值语句,条件语句,循环语句组成的. 在这 里我们首先说明一下条件语句的用法. 条件语句有四种形式.前两种形式的语法如下 if expr then statseq end if if expr then statseq1 else statseq2 end if Maple执行条件语句的过程是:首先求expr的值,如果结果是布尔值true 就执行then以后的语句statseq,然 后结束.如果expr的值是false或FAIL,则Maple执行else以后的语句.例如 > > x:=-2: if x<0 then 0 else 1 end if; 0 §11.1 程序的基本结构 e xpr必须具有布尔值true, false或者FAIL,否则将会出现错误. > 247 if x, then 0, else 1 end if; Syntax error, reserved word ‘then‘ unexpected 如果对条件不成立时不要作任何动作, 则可以省略else语句. > > if x>0 then x:=x-1 end if; x; −2 条件语句可以嵌套.即then部分及else部分的语句序列中可以是任何语句, 包括if语句. 下面的例子计算一个数的符号. > > > if x>1 then 1 else if x=0 then 0 else -1 end if; end if; 这个例子说明了FAIL的用法. > > > > > > > > > > r:=FAIL: if r then print(1) else if not r then print(0) else print(-1) end if end if; −1 如果Maple有许多情形需要考虑,仅使用if和else语句会变的杂乱难读. Maple提供了下述两 种形式的语句. if expr then statseq elif expr then statseq end if 和 if expr then statseq elif expr then statseq else statseq end if 248 第十一章 Maple编程 其中elif expr then statseq 结构可以多次出现. 下面我们用elif来实现符号函数. > x:=-2; x := −2 > > > > if x<0 then -1 elif x=0 then 0 else 1 end if; −1 由于循环语句的情况较为复杂,我们在下一节中单独说明. §11.2 循环语句 象其它程序语言一样, Maple也提供两种循环语句. 分别为for循环和while循环. 但是在Maple中, for循环与while循环本质上是统一的, 因此, 我们主要讨论for循环的用法. for循环 循环结构主要是用来处理相似的计算.例如,求前五个自然数的和可以如下计算. > > > > > > total:=0; total:=total+1; total:=total+2; total:=total+3; total:=total+4; total:=total+5; 这个问题也可以用for循环处理. > > > > total:=0: for i from 1 to 5 do total:=total+i; end do; total total total total total := 1 := 3 := 6 := 10 := 15 每轮循环, Maple使i的值加1并检查i的值是否大于5, 如果不大于5, Maple 则再次执行循环体. 循环结束后total的值为15. > total; 15 §11.2 循环语句 249 下述过程使用for循环计算前n个自然数的和. > > > > > > > > SUM:=proc(n) local i, total; total :=0; for i from 1 to n do total:=total+i; end do; total; end proc: SUM最后的total语句是为了保证SUM返回total的值. 例如计算前100个数的和的命令为: > SUM(100); 5050 上面我们给出了for循环常见的使用方法, for循环的一般语法结构如下: for name from expr1 by expr2 to expr3 while condition do commands end do; 其中expr1是name的初始值, expr3是name的终值. expr2是步长. condition则是name所满足的条 件. 在for循环的结构中, 可以省略下列任何一部分for name, from expr1, by expr2, to expr3或 者while expr. commands也可以省略. 除了for部分必须放置于首位外, 其余的部分可以按任意 的顺序放置. 省略的部分有自己的缺省值, 见下表: 各部分及其缺省值 部分 for 缺省值 虚拟变元 from 1 by 1 to infinity while true 其中while condition是用来判断循环变量是否满足条件, 在省缺情况下, condition为恒真. 有 时我们可以通过给定一个条件来终止循环. 例如我们要找出大于107 的第一个素数可以编写如下 程序 > for i from 10^7 while not isprime(i) do end do; 现在i是第一个大于107 的素数. > i; 10000019 注意到循环体是空的. Maple容许空语句. 下面是重复n次动作的例子.掷五次筛子. > die:=rand(1..6): 250 > 第十一章 Maple编程 to 5 do die(); end do; 4 3 4 6 5 在这个例子中我们省略了循环变量,仅给出了循环的次数.有时也可以省略掉所有的循环语句,仅 有 do commands end do; 此时的含义是作无限循环. while循环 while循环也是一类非常重要的循环结构,实际上while循环是for 循环中省略了除while部 分外的其它部分, 它的结构为 while condition do commands end do; 表达式expr称为while条件. 它必须是布尔值表达式, 也就是说:它的值必须是true, false, 或 者FAIL. 例如 > x:=256; x := 256 > while x>1 do x:=x/4 end do; x := 64 x := 16 x := 4 x := 1 while循环的工作方式如下. 首先Maple计算while条件. 如果值为true, Maple运行循环体. 重复 循环直到Maple条件的值为false或FAIL. 注意, Maple在执行循环体之前计算条件. while条件不 是true, false或FAIL三者之一时就是出现错误. 例如 > > > x:=1/2: while x>1 do x:=x/2 end do; x; 1 2 while x do x:=x/2 end do; > Error, invalid boolean expression §11.2 循环语句 for-in循环 for循环还有一种形式, 我们称为for-in循环, 它语法结构是 for name in expr while condition do commands end do; 251 这种循环的含义是循环变量name遍历对象expr的每一个分支. 这个对象可以是列表、集合, 也 可以是一个和式的项或乘积的项. 例如: > L:=[7,2,5,3,5,8]; L := [7, 2, 5, 3, 5, 8] > > > for i in L do if i<=7 then print(i) end if; end do; 7 2 5 3 5 > A:=x^3+3*x^2-2*x+y^2; A := x3 + 3 x2 − 2 x + y 2 > > > for i in A do print(i) end do; x3 3 x2 −2 x y2 break和next Maple语言中还有另外两个循环结构的控制语句: break和next. 当Maple执行break时, 其 结果是退出它所处的最里面的循环体. 继续运行循环体后面的第一个语句. 例如 > L:=[2,5,7,8,9]; L := [2, 5, 7, 8, 9] > > > > for i in L do print(i); if i=7 then break end if; end do; 2 5 7 252 第十一章 Maple编程 当Maple遇到next时, 它会结束本次循环, 即跳过循环体种下面尚未执行的语句, 接着进行 下一次是否执行循环的判定. 例如, 要跳过列表中等于7的元素. > L:=[7,2,5,8,7,9]; L := [7, 2, 5, 8, 7, 9] > > > > for i in L do if i=7 then next end if; print(i); end do; 2 5 8 9 如果不在循环体中使用break和next语句, 会引起错误. > next; Error, break or next not in loop 其它循环结构 Maple语言中除了上述循环结构以外还提供了许多简明而有效的循环结构. 这种类型的循 环命令有7种,可以分为三类: 1. map, select, remove 2. zip 3. seq, add, mul map, select和remove命令 map命令将一个函数施加于一个集体目标的每一个元素. 最简单的map命令形式是map(f,x).其 中f 是一个函数, x是一个表达式.map命令用f(x i)代替表达式x中的x i项1 . 例如: > map(f, [a, b, c]); [f(a), f(b), f(c)] 对于一列整数, 用map命令可以生成它们的绝对值列和平方列. > L:=[-1, 2, -3, -4, 5]; L := [−1, 2, −3, −4, 5] > map(abs, L); [1, 2, 3, 4, 5] > map(x->x^2, L); [1, 4, 9, 16, 25] 1 例外: 对于图表和阵列, Maple把函数施行于图表与阵列的元素, 而不是它们的项或下标函数. §11.2 循环语句 253 更一般形式的map命令形式为map(f,x,y1,...,yn). 这里f 是一个函数,x是任意表达式,y 1, · · · , yn是 表达式, 它的作用是以f(x i, y1, ..., yn) 代替x中的x i. > map(f, [a,b,c], x, y); [f(a, x, y ), f(b, x, y ), f(c, x, y )] > L:=[seq(x^i, i=0..5)]; L := [1, x, x2 , x3 , x4 , x5 ] > map((x,y)->x^2+y, L, 1); [2, x2 + 1, x4 + 1, x6 + 1, x8 + 1, x10 + 1] 需要注意的是, 由于化简的缘故, 对于代数类型的输入, 它的结果类型不一定也是相同的代 数类型. 考虑下面的例子. > a:=2-3*x+I; a := 2 − 3 x + I > map(z->z+x, a); 2+I 对a的每一项加上x, 产生2+x-3*x+x+I+x = 2+I. 结果是一个复数. > type(%, complex); true 但是,a却不是复数. > type(a, complex); false select及remove命令与map命令具有相同的语法形式, 它们的使用也有类似之处. 其语法形 式为select(f, x)和remove(f, x). 其中f 是一布尔值函数, x是下列表达式中的一种:和、积、 列表、 集合, 或者为下标名. zip命令 它具有两种形式zip(f, u, v)或zip(f,u,v,d). 其 中f 是二元函数, u, v 同为列表或者向量. d是一个值. zip的作用是, 对每一对项u i, v i生成 元素f(u i, v i)得到新的列或向量. 下面就是一例. > zip命令用于粘接两个列表或向量. zip((x,y)->x.y, [a,b,c,d,e,f], [1,2,3,4,5,6]); [a1 , b2 , c3 , d4 , e5 , f6 ] 如果列表或向量不等长时, 结果的长度依赖于是否提供d的值. 如果没有指定d的值, 结果的 长度将与u, v中的较短者相等. > zip((x,y)->x+y, [a,b,c,d,e,f], [1,2,3]); [a + 1, b + 2, c + 3] 254 第十一章 Maple编程 一旦d的值被指定, zip命令的结果将与二者中较长的等长. Maple以d弥补所缺的值. > zip((x,y)->x+y, [a,b,c,d,e,f], [1,2,3], xi); [a + 1, b + 2, c + 3, d + ξ, e + ξ, f + ξ ] 注意: zip不能象map命令那样将多余的项xi传递给函数f . seq, add及mul命令 seq, add和mul命令分别形成序列, 和, 以及积. seq,add和mul通常的用法是seq(f, i=a..b)、 add(f, i=a..b)和mul(f, i=a..b). 其中f, a, b是表达式, i是名字. 表达式a, b的计算结果须为数字常量. seq的结果是一个序列, 它是通过依次给下标名i赋值a, a + 1, ..., b, 计算f 而得到的. 与seq相 类似的, add的结果是这个序列的和, 而mul的结果则是这个序列的积. 如果a的值大于b,它们的结 果分别为NULL序列, 0, 1. > seq(i^2, i=1..4); 1, 4, 9, 16 > mul(i^2, i=1..4); 576 > add(x[i], i=1..4); x1 + x2 + x3 + x4 > mul(i^2, i=4..1); 1 > seq(i, i=4.123...6.1); 4.123, 5.123 seq, add和mul还可有下列的形式seq(f, i=X)、 add(f, i=X)和mul(f, i=X). 其中f 和X 是 表达式, i是名字. 这种形式下seq的结果仍然为一个序列. 它是通过依次将X 的项赋值给i计算f 的值而得到. add结果是这个序列的和, mul的结果是此序列元素的积. > a:=x^3+3*x^2+3*x+1; a := x3 + 3 x2 + 3 x + 1 > seq(degree(i,x), i=a); 3, 2, 1, 0 > add(degree(i,x),i=a); 6 > a:=[23,-42,11,-3]; a := [23, −42, 11, −3] §11.3 变量 > 255 mul(abs(i), i=a); 31878 > add(i^2, i=a); 2423 §11.3 变量 在前面的使用中, 我们经常遇到的现象是, 前面定义的变量对后面的计算有影响. 事实上, 在交互方式下所使用的变量都不在一个过程体中, 我们把这种变量称为全局变量. 对于过程的 情形, 我们希望Maple对一些变量只在过程内部识别它们, 这种变量就称为局部变量. 过程内部的变量既可以是局部变量, 也可以是全局变量. 过程外部的变量都是全局变量. Maple认为在不同的过程中的局部变量是不同的变量, 即使它们有相同的名字. 于是, 过程可以 改变局部变量的值, 而不影响其它过程中同名的局部变量或同名的全局变量的值. 用下列方式 可以声明局部变量和全局变量. local L1, L2, ... , Ln; global G1, G2, ... , Gm; 在下列过程中, i和m是局部变量. > > > > > > > > > MAX := proc() local i,m; if nargs = 0 then RETURN(0) end if; m := args[1]; for i from 2 to nargs do if args[i] > m then m := args[i] end if; end do; m; end proc: 如何一个变量既不声明为局部变量, 也不声明为全局变量, Maple将按照下列规则确认其类 型. 如果满足下列两条, 则为局部变量 • 变量出现在赋值语句的左边, 例如句A := y和A[1] := y中的A. • 变量是for循环中的指标变量, 或者在seq, add和mul命令中出现. 如何不满足这两条规则, 则为全局变量. > > > > > > > > MAX := proc() if nargs = 0 then RETURN(0) end if; m := args[1]; for i from 2 to nargs do if args[i] > m then m := args[i] end if; end do; m; end proc: 256 第十一章 Maple编程 Warning, ‘m‘ is implicitly declared local Warning, ‘i‘ is implicitly declared local Maple声明m为局部变量, 原因是它在赋值语句m:=args[1]的左边. Maple声明i为局部变量, 原 因是:它是for循环的指标变量. 在编写过程时不要依赖于这种便利的方式声明局部变量, 而应当明确的声明所有的局部变 量. 不过这些警告信息帮助我们发现拼写错误的变量和没有声明的变量. 下列newname过程创造了在序列C 1, C 2, . . .中下一个没有使用的名字. 由于上述两个规则没 有应用于cat(C, N),于是newname过程创造的名字是全局变量, > > > > > > > > > newname := proc() global N; N := N+1; while not assigned(cat(C,N)) do N := N+1; end do; cat(C,N); end proc: N := 0; N := 0 newname过程不需要任何自变量. > newname()*sin(x)+newname()*cos(x); C1 sin(x) + C2 cos(x) 在过程中给一个全局变量赋值是一个愚蠢的想法. 有时是在你没有意识到的情况下, 全局 变量的任何变化都会影响这个变量的使用. 因此应当审慎的使用这一技术. 变量的作用域 变量的作用域通常是指涉及这些变量值的语句和过程的全体. 在Maple中只有两种可能. 或 者一个名字的值在任何地方都是可行的(即为全局的), 或者它仅对构成某个特殊过程定义的语 句有效(即是局部的). 为了说明二者的区别, 先对全局变量名b赋值. > b:=2; b := 2 其次, 定义两个几乎等同的过程. 过程g以b为局部变量, 过程h 以b为全局变量. > > > > > g:=proc() local b; b:=103993/33102; evalf(b/2); end proc: 以及 §11.3 变量 > > > > > 257 h:=proc() global b; b:=103993/33102; evalf(b/2); end proc: 定义过程时不会影响全局变量b的值. 事实上, 运行过程g(因为它使用b作为局部变量)也不会影 响b的值. > g(); 1.570796327 所以, 全局变量b的值仍旧是2. 过程g赋予局部变量b一个值, 它与作为全局变量的b值不同. > b; 2 过程h的效果就完全不同了, 因为它使用全局变量. > h(); 1.570796327 h改变了b的值, 所以它不再是2. 当h被激活时, 其副作用, 是把b的值改变了. > b; 103993 33102 局部变量的求值 在Maple编程中, 局部变量的求值具有特殊性. 在过程体的执行过程中, 局部变量的求值只 进行一层, 而对于全局变量Maple则进行完全求值, 即使在过程体内也是这样. 我们首先在Maple的交互环境下考虑下列例子: > f := x+y; f := x + y > x :=z^2/y; z2 x := y z := y^3+3; z := y 3 + 3 > 在这种情形, Maple完全递归地求值如下 > f; (y 3 + 3)2 +y y 258 第十一章 Maple编程 你可以通过使用eval命令来控制求值的层次. 下面的一系列命令, 就是进行一层、二层和三层 求值. > eval(f,1); x+y > eval(f,2); z2 +y y eval(f,3); (y 3 + 3)2 +y y > 然而, 在Maple编程的过程中, 对于局部变量, 我们不必担心完全求值的问题. Maple 对于局 部变量总是进行一层求值2 .这种作法对于提高Maple程序的有效性是非常重要的. 由于在编程 时, 总是按照顺序执行的方式编写代码, 因此它对程序的执行只有非常小的影响. 只有在很少的 情况下, 过程体需要对局部变量完全递归的求值, 这时可以使用eval命令. 例如 > > > > > > F := proc() local x,y,z; x := y^2; y :=z; z:=3; eval(x) end proc: F(); 9 此外也可以把局部变量作为未知量来使用. 例如, 在下列过程中, 局部变量x没有赋予任何 值. 过程把它作为多项式xn − 1中的变量. > > > > > RootsOfUnity := proc(n) local x; [solve( x^n - 1=0, x)]; end proc: RootsOfUnity(5); √ √ 1√ 1 1√ 1√ 1 1√ 5 − + I 2 5 + 5, − 5 − + I 2 5 − 5, 4 44 4 44 √ 1√ √ 1√ 1 1√ 1 1√ − 5 − − I 2 5 − 5, 5 − − I 2 5 + 5] 4 44 4 44 [1, 过程参数 下面我们考察Maple调用一个函数和过程时都做了些什么工作? 当调用一个函数 F(ArgumentSequence)时, Maple首先对F 求值, 然后计算ArgumentSequence 的值, 如果其中某个 参数求值的结果是序列的话, Maple把这些参数产生的序列展开成一个单一的序列, 就是实际参 数序列. 如果Maple发现F 的值是一个过程 2 一层求值的概念是Maple编程中特有的概念,在传统的程序语言中没有分层求值的概念. §11.3 变量 proc(FormalParamenters) body; end; 259 那么Maple就执行过程体中的语句, 并且用实际参数代替形式参数. 考察下列例子. > > > s := a,b: t := c: F := proc(x,y,z) x+y+z end proc: F(s,t); a+b+c 这里, s,t是变量序列, a,b,c是实际参数序列, x,y,z 是形式参数序列. 实际参数的个数n可以不同于形式参数的个数. 如果实际参数的个数少, 则只有当缺少的实 际参数在过程体的执行过程中用到时, 才出现错误. 如果实际参数的个数多, Maple忽略多余的 参数. 例如 > > f := proc(x,y,z) if x>y then x else z end if end proc: f(1,2,3,4); 3 > f(1,2); Error, (in f) f uses a 3rd argument, z, which is missing > f(2,1); 2 参数的声明 通过声明形式参数, 可以让过程只对特定类型的输入工作, 当输入的类型不正确时, Maple将 给出错误信息提示. 类型声明的语法如下 parameter::type 这里parameter是形式参数的名字, type是一个类型. Maple知道许多表达式的类型;见?type. 当执行一个过程时, Maple首先从左到右的检测实际参数的类型, 然后执行过程体. 任何类 型检测都可能生成错误信息. 如果没有类型错误出现, 过程将被执行. 例如 > > > > MAX := proc(x::numeric, y::numeric) if x>y then x else y end if end proc: MAX(Pi,3); Error, MAX expects its 1st argument, x, to be of type numeric, but received Pi 如果没有对参数进行声明, 则参数可以是任意类型. 于是, proc(x)等价于proc(x::anything). 如果这确实是你希望的, 你应该用后一种方法告知其他用户, 你的过程可以对任何输入工作. 260 第十一章 Maple编程 参数的序列 在过程中, 并不需要对每个形式参数指定名字. 使用args, 过程内部可以接受实际参数序 列. 下列过程生成实际输入参数的列表. > f := proc() [args] end proc; f := proc() [args] endproc > f(a,b,c); [a, b, c] > f(c); [c] > f(); 第i个参数简记为args[i]. 于是, 下列两个过程是等价的, 当调用它们时, 至少要有两个numeric类 型的实际参数. > > > MAX := proc(x::numeric,y::numeric) if x>y then x else y end if; end proc; MAX := proc(x::numeric , y ::numeric ) if y < x then x else y endif endproc > > > MAX := proc() if args[1]> ags[2] then args[1] else ars[2] end if; end proc; MAX := proc() if ags 2 < args1 then args1 else ars 2 endif endproc nargs命令提供了实际参数的个数. 这使我们很容易的编写一个过程, MAX, 可以找到任意多 个参数的最大值. > > > > > > > > > MAX := proc() local i,m; if nargs = 0 then RETURN(FAIL) end if; m := args[1]; for i from 2 to nargs do if args[i] > m then m := args[i] end if; end do; m; end proc: 三个值2/3, 1/2和4/7的最大值是 > MAX(2/3,1/2,4/7); 2 3 §11.4 过程选项和描述域 261 §11.4 过程选项 过程选项和描述域 过程可以有一个或多个选项. 在过程定义中可以用options语句说明选项. options O1, O2, ..., Om 除了下列选项有特殊含义以外, 可以用任何字符串作为选项. Maple的特殊选项包括remember, system, operator, arrow, Copyright, builtin. 关于remember和system选 项, 我们将在第??页递归过程一节中介绍. 其它几个选项的含义如下: operator和arrow选项 operator选项容许Maple 对过程作额外的化简工作,而arrow选项使Maple用 箭头的方式显示过程. > > > > proc(x) option operator, arrow; x^2; end proc; x → x2 Copyright选项 Maple把任何以单词Copyright开始的选项作为Copyright选项. 使用Copyright选 项时Maple不显示过程体的内容, 除非interface变量verboseproc的值大于等于2, 在省确情况 下, 这个值为1. > > > > f := proc(expr::anything, x::name) option ‘Copyright 1684 by G. W. Leibniz‘; Diff(expr, x); end proc; f := proc(expr ::anything , x::name ) . . . endproc builtin选项 Maple过程分为两类, 一类是Maple核心部分, 另一类是用Maple语言定义的. builtin选 项说明核心过程. 当对内建的过程完全求值时就可以看到builtin选项. > eval(type); proc() optionbuiltin ; 161 end 每个内建过程用一个数字唯一标识. 用户当然不能建立自己的内建过程. 描述域 过程头的最后部分是描述域. 它只能出现在任何local语句, global语句, options语句以后, 过程体以前. 它的表达方式如下. description strings; 描述域对过程的执行没有影响.使用它的目的是对程序进行说明.不象注释, 当Maple显示过程 时,将丢弃它们,而描述域提供了在过程中附加一行注释的方法. 举例如下: > > > > f := proc(x) description ‘computes the square of x‘; x^2; # compute x^2 end proc: 262 > 第十一章 Maple编程 print(f); proc(x) description‘computes the square of x ‘ x2 endproc 即使由于使用Copyright选项而不显示过程体的内容, Maple也会显示描述域. > > > > > > f := proc(x) option ‘Copyrighted ? ‘; description ‘computes the square of x‘; x^2; # compute x^2 end proc: print(f); proc(x) description‘computes the square of x ‘ . . . endproc §11.5 递归过程 与其它语言相同, Maple也可以对过程进行递归的调用.例如我们考虑Fibonacci数的定义. fn = fn−1 + fn−2 n≥2 其中f0 = 0, f1 = 1.下面的过程可以就计算fn .它是一个典型的递归过程. > > > > > > > fib:=proc(n::nonnegint) if n<2 then RETURN(n); else fib(n-1)+fib(n-2); end if; end proc: 下面是Fibonacci数列的前10个数. > seq(fib(i),i=0..10); 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 使用time命令可以得到计算所花费的时间. > time(fib(20)); 2.157 上面我们定义的过程在Maple中并不是最优的过程. 原因是在计算f20 的时候, 我们还要首先找 到f19 和f18 以及前面所有的值. 在常用的程序设计语言中没有办法解决这个问题. 在Maple中使 用过程选项remember可以很好的解决递归过程的效率问题. remember选项的作用是让Maple记 住以前计算过的结果, 并把它们存储在记录表中. 我们把刚才的程序改写为下面的过程. > > > > fib := proc(n::nonnegint) option remember; fib(n-1)+fib(n-2); end proc; fib := proc(n::nonnegint ) optionremember ; fib(n − 1) + fib(n − 2) end §11.5 递归过程 263 调用一个有remember 选项的过程时, Maple将计算的结果存储在这个过程的记录表中. 不论什 么时候调用这个过程, Maple将检查以前是否用相同的参数调用过这个过程, 如果是, Maple将使 用记录表中以前的计算结果, 而不再执行过程. 可以用直接赋值的方法在记录表中写值, 这个方法对于没有remember选项的过程也有效. > fib(0) :=0; fib(0) := 0 > fib(1) := 1; fib(1) := 1 下面就是fib过程的记录表. table([ 0=0 1=1 ]) 由于fib有remember选项, 执行它会把新的值写入记录表它的中. > fib(9); 34 下面就是它的新记录表. table([ 0=0 4=3 8 = 21 1=1 5=5 9 = 34 2=1 6=8 3=2 7 = 13 ]) 记录表的使用可以极大的改进递归过程的效率. 测试一下刚才的计算就可以看出这一点. > time(fib(20)); 0 264 第十一章 Maple编程 Maple还有一个过程选项system可以抹掉记录表中的内容. 这种选择一般出现在内存垃圾 收集的过程中, 内存垃圾收集是Maple内存管理的一个重要部分. 特别重要的是, 在象fib在样依 赖于记录表的过程中不要使用system选项. §11.6 过程的返回值 当调用过程时, Maple通常返回过程体中最后一个语句产生的值. 其它三种类型的返回是通 过使用参数、 直接返回和错误返回. 参数赋值 有时, 过程的返回值可以通过参数得到. 考察下列布尔过程MEMBER, 它可用来确定表达式x是 否在列表L中. 更进一步, 如果调MEMBER时有第三个参数p, 则MEMBER将把x在L中的位置赋值给p. > > > > > > > > > MEMBER := proc(x::anything, L::list, p::evaln) local i; for i to nops(L) do if x=L[i] then if nargs>2 then p := i end if; RETURN(true) end if end do; false end proc: 如果用两个参数调用MEMBER,nargs是2, 过程体没有引用形式参数p. 因此Maple不会抱怨缺少参 数. > MEMBER( x, [a,b,c,d] ); false 如果用三个参数调用MEMBER, 类型声明p::evaln保证Maple把第三个参数作为名字3 来使用,而不 是用它的值. > q := 78; q := 78 > MEMBER( c, [a,b,c,d], q); true > q; 3 3 如果第三个参数没有被声明为evaln,则应该把名字q用单引号括起来(’q’)以保证把q的名字而不是q的值传 送给过程 §11.6 过程的返回值 265 Maple对形式参数仅赋值一次, 这意味这在过程体中不能象局部变量那样自由的使用形式 参数. 当给形式参数赋值后,就不能再引用这个参数. 给形式参数赋值的唯一目的是当过程返回 时, 相应的实际参数能返回一个值. 下列过程把−13赋值给它的参数, 然后返回这个参数的名字. > > > > > f := proc(x::evaln) x :=-13; x; end proc: f(q); q q 的值现在是−13. > q; −13 下面的count过程是刻画上述特点的更复杂例子. count将确定一个乘积的因子p是否包含 表达式x. 如果p包含x, 则count用第三个参数n返回包含x的因子个数. > > > > > > > > count := proc(p::‘*‘, x::name, n::evaln) local f; n := 0; for f in p do if has(f,x) then n:= n+1 end if; end do; evalb( n>0 ); end proc: count并没有按照预想的方式工作. > count(2*x^2*exp(x)*y, x, m); −m < 0 在过程内形式参数n已经是m, 而当调用过程时Maple确定的实际参数只赋一次值, 于是当执 行到evalb语句时n的值是名字m而不是m的值. 更糟的是, n:=n+1语句把名字m+1赋值给m, 对m一 次求值就可以看到: > eval(m, 1); m+1 在上面的结果中,m的值依然是m+1. > eval(m, 2); m+2 于是如果要求m的完全值, 将使Maple进入无限循环中. 对这类问题的一般解决方法是使用局部变量, 只有当过程返回值时再把局部变量的值赋给 形式参数. > count := proc(p::‘*‘, x::name, n::evaln) 266 > > > > > > > > 第十一章 Maple编程 local f,m; m := 0; for f in p do if has(f,x) then m := m + 1 end if; end do; n := m; evalb( m>0 ); end proc: 新的count过程按照我们预期的要求工作. > count(2*x^2*exp(x)*y, x, m); true > m; 2 直接返回 直接返回出现在使用RETURN命令时, 语法如下: RETURN sequence; RETURN命令使得过程立即返回, 序列的值就成为了过程的返回值. 例如, 下列过程计算x在列表L中第一个位置的值i. 如果x不在列表L中出现, 过程返回0. > > > > > > > POSITION := proc(x::anything, L::list) local i; for i to nops(L) do if x=L[i] then RETURN(i) end if; end do; 0; end proc: 在应用RETURN命令的大多数情况, 用它返回单一的表达式. 然而返回一个序列, 包括空序 列也是合法的. 例如,下面的GCD过程计算两个整数a和b的最大公因子g . 它返回序列g, a/g, b/g . GCD必需单独处理a = b = 0的情况, 这是因为此时g 为0. > > > > > > > GCD := proc(a::integer, b::integer) local g; if a=0 and b=0 then RETURN(0,0,0) end if; g := igcd(a,b); g, iquo(a,g), iquo(b,g); end proc: GCD(0,0); 0, 0, 0 > GCD(12,8); 4, 3, 2 §11.6 过程的返回值 267 当然,除了返回序列,也可以返回列表或集合. 错误返回 错误返回出现在使用ERROR命令时, 语法如下. ERROR sequence; ERROR命令使得目前的过程立即退回到Maple对话区.Maple显示下列错误信息. Error,(in procname),sequence 这里,sequence是ERROR的参数,而procname是错误发生时的过程的名字.如果过程没有名字,Maple显 示Error, (in unknown) .... ERROR命令通常用在需要检测实际参数的类型,但是参数说明又无法解决的时侯.下列的pairup过 程以形如[x1 , y1 , x2 , y2 , . . . , xn , yn ]的列表为输入,由它产生形如[[x1 , y1 ], [x2 , y2 ], . . . , [xn , yn ]]的列表 的列表.简单的类型检测无法决定列表L是否有偶数个元素,因此需要直接检查. > > > > > > > > > pairup := proc(L::list) local i, n; n := nops(L); if irem(n,2)=1 then ERROR( ‘L must have an even number of entries‘ ); end if; [seq( [L[2*i-1],L[2*i]], i=1..n/2 )]; end proc: pairup([1, 2, 3, 4, 5]); Error, (in pairup) L must have an even number of entries > pairup([1, 2, 3, 4, 5, 6]); [[1, 2], [3, 4], [5, 6]] 陷阱错误 全局变量lasterror存储最后一个错误的值. 前面的pairup导致一个错误. > lasterror; L must have an even number of entries traperror命令用来陷阱错误. traperror对它的参数求值,如果没有错误出现, 就返回参数 的值. > > x := 0: result := traperror( 1/(x+1) ); result := 1 当Maple对参数求值出现错误时,traperror返回对应错误的字符串. > result := traperror(1/x); result := division by zero 268 第十一章 Maple编程 此时,lasterror的值发生了变化. > lasterror; division by zero 通过比较traperror和lasterror的结果,可以检测是否有错误出现. 如果有错误出现, 则lasterror和traperror的 返回值相同. > evalb( result = lasterror ); true traperror和ERROR常用来尽可能迅速的中断计算.例如, 假设你试图用几种方法计算积分,在 第一种方法的计算过程中,发现它不可能成功. 此时你希望中断这种方法尝试其它方法.实现这 一目的的代码如下. > > > > > > > > > > result := traperror( MethodA(f,x) ); if result=lasterror then #An error occurred if lasterror=FAIL then # Method A failed, try Method B result := MethodB(f,x); else # some other kind of error occurred ERROR(lasterror); # propagate that error end if else # Method A succeeded RETURN(result); end if: 通过执行命令ERROR(FAIL),MethodA可以在任何时间中断. 未求值返回 Maple通常用一种特殊方式的返回作为失败返回, 当它不能完成计算时, 以未计算的函数调 用作为结果返回.下列的过程MAX计算两个数x和y 的最大值. > MAX := proc(x,y) if x>y then x else y end if end proc: 对于符号计算系统来讲, 上述的MAX过程是无法接受的, 因为这个过程要求它的参数是数值类型, 以便Maple能决定是否x > y . > MAX(3.2, 2); 3.2 > MAX(x, 2*y); Error, (in MAX) cannot evaluate boolean 由于MAX缺乏符号计算能力, 当试图画出包含MAX的表达式时就出现了问题. > plot( MAX(x, 1/x), x=1/2..2 ); Error, (in MAX) cannot evaluate boolean §11.6 过程的返回值 269 在调用plot命令之前, Maple求MAX(x,1/x)的值时出现了错误. 解决的方法是,当参数x和y 不是数值类型时, MAX未求值返回. 也就是说, 此时MAX应当返 回’MAX’(x,y). > > > > > > > MAX := proc(x, y) if type(x, numeric) and type(y, numeric) then if x>y then x else y end if; else ’MAX’(x,y); end if; end proc: 新的MAX可以处理数值和非数值的输入. > MAX(3.2, 2); 3.2 > MAX(x, 2*y); MAX(x, 2 y ) > plot( MAX(x, 1/x), x=1/2..2 ); 2 1.8 1.6 1.4 1.2 1 0.6 0.8 1 1.2 x 1.4 1.6 1.8 2 对MAX做进一步的改进,就可以使它能求出任意多个参数的最大值.在下面的过程里面,args是 实际参数序列,nargs是实际参数的个数,procname是过程名字. > > > > > > > > > > > > MAX := proc() local m,i; m := -infinity; for i in (args) do if not type(i, numeric) then RETURN(’procname’(args)); end if; if i>m then m := i end if; end do; m; end proc: MAX(3,1,4); 4 270 > 第十一章 Maple编程 MAX(3,x,1,4); MAX(3, x, 1, 4) §11.7 过程对象 这一节描述过程对象, 它的类型和运算, 它的特殊求值规则, 以及如何把它存为文件并从文 件中恢复. 最后名求值 于普通的表达式,Maple以完全递归的方式求值.对于赋了值的名字,所有进一步的引用都 返回它的值来代替名字. > f := g; f := g > g := h; g := h > h := x^2; h := x2 现在f的值为x2 . > f; x2 过程和表的名字是例外的.对这样的名字,Maple采用最后名字求值模型.这种模型避免显示 过程定义的细节内容. > F := G; F := G > G := H; G := H > H := proc(x) x^2 end proc; H := proc(x) x2 end 现在F的值是H,这是因为H是实际过程前的最后名字. > F; H 可以用eval命令对过程完全求值. > eval(F); proc(x) x2 end §11.7 过程对象 271 过程的类型和运算域 Maple能辨认所有过程(包括由映射创造的过程)的类型为过程类型,以及赋给过程的任何名 字. > type(F,name); true > type(F,procedure); true > type(eval(F),procedure); true 于是,可以用下列测试保证F 是过程的名字. > if type(F, name) and type(F, procedure) then ... end if 过程有六个运算域: 1. 形式参数序列 2. 局部变量序列 3. 选项序列 4. 记录表 5. 描述串 6. 全局变量序列 作为一个过程结构的例子,考虑下列过程 > > > > > > > f := proc(x::name, n::posint) local i; global y; option Copyright; description ‘a summation‘; sum( x[i] + y[i], i=1..n ); end proc: 在过程的记录表中写入一项 > f(t,3) := 12; f(t, 3) := 12 下面可以看到f 的各部分. 过程的名字: > f; f 272 第十一章 Maple编程 过程自己: > eval(f); proc(x::name , n::posint ) description‘a summation ‘ . . . end 形式参数: > op(1, eval(f)); x::name , n::posint 局部参数: > op(2, eval(f)); i 选项: > op(3, eval(f)); Copyright 记录表: > op(4, eval(f)); table([ (t, 3) = 12 ]) 描述域: > op(5, eval(f)); a summation 全局变量: > op(6, eval(f)); y 过程体不是它的运算域,因此不能用op命令进入过程体.如果需要操作过程体,见?hackware. 保存和恢复过程 当你编写了一个新过程时,你可以通过保存整个工作区来保存你的工作;当你对过程的工作 感到满意时,可以把它存为.m文件.这样的文件使用Maple的内部表达方式,这样可以使Maple能快 速有效的恢复它存储的对象. > > > > > > CMAX := proc(x::complex(numeric), y::complex(numeric)) if abs(x)>abs(y) then x; else y; end if; §11.7 过程对象 > 273 end proc: 使用save命令保存过程,同样的方式也可以保存任何其他Maple对象. > save( CMAX, ‘CMAX.m‘ ); read命令恢复存储在.m文件中的对象. > read( ‘CMAX.m‘ ); 某些Maple用户更愿意用他们喜欢的编辑器编写Maple过程.你也可以用read 命令从文件中 读出数据,Maple象在会话区中键入一样执行文件中的每一行. 如果你编写了一批相关的过程,你可能希望把它们保存为Maple程序包.作成一个程序包后,你 可以用with命令调用这些过程.关于Maple程序包的具体做法将在下一章中说明. ...
View Full Document

This note was uploaded on 10/29/2010 for the course ENGR 131 taught by Professor Purzer during the Spring '10 term at Purdue University.

Ask a homework question - tutors are online