第4章 Matlab程序设计_第1页
第4章 Matlab程序设计_第2页
第4章 Matlab程序设计_第3页
第4章 Matlab程序设计_第4页
第4章 Matlab程序设计_第5页
已阅读5页,还剩48页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

2023/1/31MATLAB程序设计吴鹏(rocwoods)rocwoods@126.comMATLAB从零到进阶2023/1/31主要内容

M文件——脚本文件和函数文件

MATLAB程序流程控制与调试程序流程控制示例

匿名函数、子函数与嵌套函数

匿名函数、子函数与嵌套函数应用案例

编写高效的MATLAB程序

养成良好的编程风格

2023/1/31第一节M文件——脚本文件和函数文件

2023/1/31一、脚本文件1.什么时候适合用脚本文件2.什么是脚本文件

指令的增加并且需要频繁重复计算时,直接从指令窗进行计算很麻烦。这时候使用脚本文件最为适宜

所谓脚本文件,就是一些按用户意图排列的MATLAB指令(包括流程控制指令在内)组成的M文件。3.脚本文件和函数文件的区别

脚本文件运行后,产生的所有变量都驻留在MATLAB基本工作空间(baseworkspace)中。只要用户不使用clear指令加以清除,或MATLAB不关闭,这些变量将一直保存在基本工作空间。2023/1/31二、函数文件其中out1,out2,…为输出参数列表,in1,in2,…为输入参数列表,funname为函数名。输入参数和输出参数个数根据问题的需要可以为0个到多个。函数文件也需要保存到MATLAB的搜索路径下才能被调用。

格式:每个函数都由“function”引导,如下:function[out1,out2,…]=funname(in1,in2,…)注释说明部分(%号引导的行)函数体

2023/1/31第二节MATLAB程序流程控制与调试

2023/1/31一、MATLAB程序流程控制if-else-end结构switch-case-otherwise-end结构for循环while循环try-catch结构其他中断、暂停语句2023/1/31二、MATLAB程序调试

2.设置断点进行调试

1.语法错误和运行结果错误

语法错误即所书写的代码不符合MATLAB语法规范所造成的错误,比如由于粗心造成的拼写错误,不十分了解某个函数的调用方法而造成的调用错误等

。运行结果错误是一类非常难以检查的错误,程序能正常运行,只是运行结果与期望的不一样,这类错误大多是由于算法错误引起的。2023/1/31在程序编辑窗口中编写的M代码的每一行的前面都标有行号,在可执行的命令行的行号后面都有一个小短横“-”,单击某一行的“-”,就可在该行设置一个断点,此时的“-”变成了红色的圆点,表示设置断点成功。将光标放到M代码的某一行上,然后按快捷键F12,或者单击程序编辑窗口工具栏上的图标,也可以通过菜单项“Debug→Set/ClearBreakpoint”设置断点。用函数dbstop设置断点。2023/1/31第三节程序流程控制示例

2023/1/31一、概述

要通过一个完整的MATLAB程序来展示各种流程控制的用法,该程序来源于如下一个有趣的问题。一只失明的小猫不幸掉进山洞里,山洞有三个门,其中第一个门进去后走两个小时后可以回到地面,第二个门进去后走四个小时又回到原始出发点,不幸的是第三个门进去后走六个小时还是回到原始出发点。小猫由于眼睛失明,每次都是随机的选择其中一个门走。问题是这只可怜的小猫走出山洞的期望时间是多少?2023/1/31二、问题分析

设小猫走出山洞的期望时间为t,如果小猫不幸进了第二个或第三个门,那么它过四个小时或是六个小时后又和进门之前面临状况一样了,只不过这两种不幸的情况发生的概率都为1/3,而万幸一次性走出去的概率也是1/3。于是我们可以得到下面的方程:

t=2*1/3+(4+t)*1/3+(6+t)*1/3

很简单的一元一次方程,解得t=12。1.解析分析方法2.计算机模拟的思路2023/1/31输入正整数n作为模拟小猫出洞的次数,生成一个1*n的数组T用来记录每次小猫出洞时间,初始值为0。k从1循环到n,T(k)用来记录每次实际出洞的时间。随机等概率的生成{1,2,3}之间的一个数c,如果c=1,T(k)=T(k)+2,小猫走出山洞,开始下一次模拟。否则根据c=2或者c=3决定T(k)=T(k)+4或T(k)=T(k)+6,并继续随机生成c,直到c=1。模拟完n次后,计算T的均值得到小猫走出山洞的期望时间的近似值2023/1/31三、程序代码

functionT=cat_in_holl(varargin)%varargin,使函数可以接受参数个数不定的输入if~isempty(varargin)%输入参数非空n=varargin{1};%varargin为cell型数组,取其第一个元素赋给nend%try-catch结构用法示例try%如果n是正整数下面语句不会发生错误,进而执行try-catch结构之后的语句

%否则会发生错误,执行由catch引导的语句。

ifn>0&&mod(n,1)==0;%n为正整数的判断条件

%空语句,不会报错

else%n不是正整数,报错

error;end2023/1/31catchME%ME,用来记录发生错误的一些信息

disp('函数没有输入参数或者输入参数不是正整数标量');T=[];%给T赋空值

return;%函数返回,后面语句不再执行,没有return会接着执行后面的语句end%switch-case-end结构用法示例switchnargin%nargin,函数输入参数的个数

case1%case1的情况是我们程序的核心部分,即整个模拟出洞的过程

T=zeros(1,n);fork=1:n%for循环用法示例

c=unidrnd(3,1);%等概率随机生成{1,2,3}中某个数字2023/1/31whilec~=1%while循环用法示例

ifc==2T(k)=T(k)+4;elseT(k)=T(k)+6;endc=unidrnd(3,1);endT(k)=T(k)+2;endcase2T=[];disp('函数只能有一个输入参数,且为正整数');otherwiseT=[];disp('函数输入参数个数不能大于1,参数需为正整数');end2023/1/31四、程序验证

在Editor中新建一个m文件,将上述程序复制过去,并以cat_in_holl.m文件名保存到MATLAB的搜索路径下,对于n=10^4以及n=10^5,我们可以用如下指令得到近似期望时间T:

>>n=1e4; >>T=cat_in_holl(n); >>mean(T) ans= 11.8888 >>T=cat_in_holl(n); >>mean(T) ans= 12.02222023/1/31cat_in_holl.m是一个完整的m函数文件,里面用到了几乎各种流程控制方法。读者朋友可以试运行如下指令,观看程序结果,体会各流程控制的功能:

T=cat_in_holl;%没有输入参数

T=cat_in_holl(10.1);%输入参数为小数

T=cat_in_holl(-10000);%输入参数为负数

T=cat_in_holl({1000});%输入为元胞数组

T=cat_in_holl(1000,100);%输入参数为2个

T=cat_in_holl(1000,100,100);%输入参数大于2个2023/1/31第四节匿名函数、子函数与嵌套函数

2023/1/31一、匿名函数1. 匿名函数定义

fhandle=@(arglist)expr

其中,expr是具体的函数表达式,arglist是指定的函数的自变量。 匿名函数按照不同的分类方法可以分为不同的种类。按照自变量的个数以及层数可以分为以下几种:单变量匿名函数,多变量匿名函数,单重匿名函数,多重匿名函数。2. 单变量匿名函数

f=@(x)x.^2;3. 多变量匿名函数

g=@(x,y)x.^2+y.^2;2023/1/314. 单重匿名函数 无论单变量还是多变量匿名函数,都属于单重匿名函数。这类匿名函数的特点是,只有一个“@”符号引导,“@”之后就是具体的函数表达式。自变量输入单重匿名函数后,得到的是具体的数值。除了单重匿名函数外,还有双层、乃至多重匿名函数。这些多重匿名函数在参数传递方面非常方便。5. 多重匿名函数

f=@(a,b)@(x)a*x+b f= @(a,b)@(x)a*x+b

其中“a,b”是外层变量,“x”是内层变量。可以这样理解这个表达式:每个“@”符号后面括号里的变量的作用域一直到表达式的结尾。这样,“a,b”的作用域就是“@(x)a*x+b”,而“x”的作用域就是“a*x+b”。因此,对于给定的“a,b”,“gab=f(a,b)”是一个单层以x为变量的单变量匿名函数。2023/1/31二、子函数函数M文件中,我们称第一个“function”引导的函数为主函数,外部调用函数文件时总是从主函数开始执行。如果整个函数文件只有一个函数,那这个函数就是主函数。在设计比较复杂的程序的时候,为了能够使程序之间逻辑关系清楚,易于阅读和维护,通常会采用模块化的设计方式,这时就会涉及子函数的使用。从函数编写格式以及形态上来说,子函数和主函数并无区别,区别仅在于在函数文件中的位置以及调用关系。2023/1/31子函数是在同一函数文件中,主函数之后的由“function”引导的函数,一个函数文件可以有多个子函数。这些子函数在函数文件中的排列顺序可以随意,前提是都要位于主函数后。子函数可以被其所在的函数文件中的主函数和其他子函数调用,子函数也可以调用主函数,在设计子函数调用主函数的程序时一定要小心,避免形成“死”的调用循环。

2023/1/31三、嵌套函数在程序编写中,有的数学表达式比较长,不方便用一行来表示,而且这个表达式里还有其他的参数。这时候我们就需要将其写成函数,用嵌套函数可以方便的解决参数共享问题。当求解微分方程或者进行优化问题求解时,一些复杂的表达式往往涉及参数传递过程,这个时候,比较适合将其表达式写成嵌套函数。在编写GUI用户界面的回调函数时,参数传递往往是个棘手的问题。利用嵌套函数,可以方便解决这个问题。2023/1/311. 嵌套函数定义

嵌套函数,即nestedfunction,顾名思义,是嵌套在函数体内部的函数。嵌套函数以function声明,结束的时候加上end。需要说明的是,包含有嵌套函数的函数,无论他是主函数、子函数或者嵌套函数,都应该在末尾的地方加上end。下面代码是一个简单的嵌套函数的例子:functionr=MyTestNestedFun(input)a=5;c=sin(input)+tan(input);functiony=nestedfun(b)y=a*c+b;endr=nestedfun(5);end

2023/1/312. 嵌套函数种类嵌套函数可以分为单重嵌套函数和多重嵌套函数。单重嵌套函数,这样的函数嵌套在别的函数体内,自己内部不再有嵌套的函数,例如上面介绍的是单重嵌套函数。多重嵌套函数嵌套在别的函数体内,同时自己内部又嵌套着别的另一层或几层函数。如下面类型的函数:2023/1/31functionx=A(p1,p2)...functiony1=B1(p3)...functionz1=C1(p4)...end...end...functiony2=B2(p5)...functionz2=C2(p6)...functionw=D(p7)...endendendend

2023/1/313. 嵌套函数的变量作用域变量的作用域指的变量能够被程序访问、修改、设置等的代码范围。

父函数和嵌套在其内的函数,他们各自的变量是可以互相访问的。但是必须注意的是,嵌套函数访问父函数的变量,可以在函数定义里直接拿过来用,而父函数访问嵌套在其内的函数里的变量则必须要经过调用之后。彼此没有嵌套关系的嵌套函数间是不能简单得共享变量的。如果非要共享,则只能通过它们所在的父函数来进行。2023/1/31四、嵌套函数彼此调用关系将主函数看成“父亲”,嵌套函数依据嵌套深度可以看成“儿子”,“孙子”,“重孙”等等。这样最外层的主函数可以认为是所有内部函数的“祖先”,位于不同嵌套函数里的函数之间的关系类似于一个家族之间的“亲属关系”。嵌套函数之间的调用关系可以总结成下面几句话:父亲可以求助儿子,儿子可以求助父亲,也即父子可以互相求助。一个人不能求助孙子、重孙等后代,但是可以求助自己的直系祖宗(祖父、曾祖父等等)以及和其直系祖宗是亲兄弟的先人。一个人可以求助自己的亲兄弟,或者亲叔叔、亲伯伯但不能求助侄儿。

2023/1/31第五节匿名函数、子函数与嵌套函数应用案例2023/1/31一、匿名函数应用实例1. 匿名函数在求解方程中的应用【例4.5-1】求下列方程的根:求解代码如下:>>f=@(x)exp(x)+x^2+x^(sqrt(x))-100%构造方程的匿名函数形式f=@(x)exp(x)+x^2+x^(sqrt(x))-100>>formatlong>>x0=fzero(f,3)%求方程的根,初始值为3x0=4.163549956946139%求出的解>>f(x0)%代入原方程验证ans=2.842170943040401e-0142023/1/31一、匿名函数应用实例【例4.5-1序】对于 ,求下列方程相应的值,并画出和相应的的图像。

求解代码如下:f=@(a)@(x)exp(x)+x^a+x^(sqrt(x))-100;%构造函数句柄formatlongaa=0:0.01:2;plot(aa,arrayfun(@(a)fzero(f(a),4),aa),'*-')%利用arrayfun求解不同的a对应的xxlabel('$a$','interpreter','latex','fontsize',15)%标注x、y坐标轴,按照latex语法ylabel('$x$','interpreter','latex','fontsize',15)title('$\mathrm{e}^{x}+x^{\sqrt{x}}+x^a-100$','interpreter','latex',...'fontsize',15)

2023/1/31【例4.5-1序】得到的图形2023/1/31一、匿名函数应用实例2. 匿名函数在显式表示隐函数方面的应用 隐函数一般无法在数学上显式的表达。这里说的显式表示指的是构造一个MATLAB函数来表达隐函数,具体思路是对于给定的隐函数的自变量x,通过数值方法求解出因变量y,这样就相当于显式表达隐函数。请看下面的例子:

【例4.5-2】显式表示下列y关于x的隐函数: 利用匿名函数,我们可以在MATLAB中显式得写出y和x的关系式如下:

y=@(x)fzero(@(y)(exp(y)+x^y)^(1/y)-x^2*y,1);2023/1/31一、匿名函数应用实例 【例4.5-3】显式表示下列z关于x,y的隐函数:

z=@(x,y)fzero(@(z)z-sin((z*x-0.5)^2+x*2*y^2-z/10)*...exp(-((x-0.5-exp(-y+z))^2+y^2-z/5+3)),rand);z(1,2)ans=7.369600885222808e-004z(0,0.5)ans=0.0028075073342922023/1/31一、匿名函数应用实例3. 匿名函数在求积分区域方面的应用

有的时候,我们需要根据已知的积分值和被积函数求对应的积分区域,当被积函数表达式不是很复杂的时候,用匿名函数比较适合。请看下例:

【例4.5-4】要使sin(x)^2./(x^2)这个式子的积分值为0.99*pi,求其关于0对称的积分区域。

u0=fzero(@(u)0.99*pi/2-quadl(@(x)sin(x).^2./(x.^2),0,u),1) u0= 32.3138103582174102023/1/31一、匿名函数应用实例4. 匿名函数和符号计算的结合

【例4.5-5】求下面函数三阶导数在[0,1]区间的图像:

本例如果用手算的话,比较繁琐。如果用符号运算得到三阶导数的解析表达式,然后再转化成匿名函数,则比较方便。本例展示了符号计算和数值计算结算结合的一种途径。代码如下:symsxf=(x+tan(x))^(sin(x));c=diff(f,3);f3=eval(['@(x)'vectorize(c)]);x=linspace(0,1,100);plot(x,f3(x))title('(x+tan(x))\^(sin(x))三阶导数图像')2023/1/312023/1/31一、匿名函数应用实例5. 匿名函数在优化中的应用 匿名函数在优化中的应用主要是以表示目标函数的形式出现的。

【例4.5-6】求下面函数的最小值 用匿名函数来表示目标函数如下:

f=@(x)3*x(1)^2+2*x(1)*x(2)+x(2)^2;

进一步求解:

x0=[1,1];%初始值

[x,fval]=fminunc(f,x0) x= 1.0e-006* 0.2541-0.2029 fval= 1.3173e-0132023/1/31二、子函数和嵌套函数应用实例在求解积分上限中的应用【例4.5-7】如下述积分表达式,如何已知和,求得?2023/1/31functionsol=exampleIntLimit1(a,e,l)%用嵌套函数表示被积表达式

functionf=fun1(beta)f=a.*(1-e.^2)./(1-e.^2.*sin(beta).^2).^(3/2);end%调用fzero求满足条件的beta0值sol=fzero(@(beta0)fun2(beta0,l,@fun1),3);end%用子函数表示积分functiong=fun2(beta0,l,fhdle)g=quadl(fhdle,0,beta0)-l;end2023/1/31二、子函数和嵌套函数应用实例2. 在GUI中的应用(三角界面,见书中代码)在3D作图中的应用【例4.5-9】画出下列函数的图像:其中2023/1/31function[m,n,TT]=plot3dnmT(N,L)%N:inf的近似,L:[0,2]区间的剖分个数C=zeros(N,1);%nested-function:Tmn=calcT(mm,nn)中用来存储计算结果m=linspace(0,2,L);[m,n]=meshgrid(m,m);TT=zeros(size(n));%和网格数据m,n对应的计算出来的T(m,n)网格数据forii=1:Lforjj=1:LTT(ii,jj)=calcT(m(ii,jj),n(ii,jj));endend%=====计算T(m,n)的nest-functionfunctionTmn=calcT(mm,nn)forN1=1:NC(N1)=(mm^N1/gamma(N1+1))*sum(nn.^(0:N1-1)./gamma(1:N1));Tmn=1.0-exp(-mm-nn)*sum(C);endendmesh(n,m,TT);end

2023/1/31嵌套函数表示待优化的目标函数【例4.5-10】已知求下面表达式的最小值。其中,m在[0,2]这个区间范围内

2023/1/31functionm=Findmw=[pi/2,pi,pi*1.5];N=[pi/2-1,-2,-1.5*pi-1];functiony=ObjecFun(m)y=(quadl(@(t)t.^m.*cos(t),0,w(1))-N(1))^2+(quadl(@(t)t.^m.*cos(t),0,...w(2))-N(2))^2+(quadl(@(t)t.^m.*cos(t),0,w(3))-N(3))^2;endm=fminbnd(@ObjecFun,0,2);end2023/1/31二、子函数和嵌套函数应用实例5. 嵌套函数在表示微分方程方面的应用(求解微分方程,见书中代码)2023/1/31第六节编写高效的MATLAB程序2023/1/31一、重新认识循环1. 高版本MATLAB对循环结构的优化

从MATLAB6.5版开始,MATLAB引入了JIT(justintime)技术和加速器(accelerator),并在后续版本中不断优化。到了R2009a,很多情况下,循环体本身已经不是程序性能提高的瓶颈了,瓶颈更多的来源于循环体内部的代码实现方式,以及使用循环的方式。(详见例【例4.6-1】)选择循环还是向量化凡是涉及到矩阵运算的时候,则尽量用向量化如果向量化可能导致超大型矩阵的产生,使用前要慎重向量化的使用要灵活,多分析其运行机制,并与循环做对比MATLAB初学者要尽量多用向量化思路编程高效的编程包括高效的开发和高效的运行

2023/1/31二、提高代码效率的方法

1. 预分配内存 当矩阵尺寸较大时,预分配内存(preallocation),是MATLAB高效编程中最应该注意,同时也是很容易把握的一个技巧(详见例【例4.6-5】)选用恰当的函数类型

MATLAB有多种函数类型,不同的函数类型调用效率差别相当大,为了写出高效的MATLAB代码,我们就要选用恰当的函数类型来实现我们的任务。(详见例【例4.6-6】【例4.6-7】)选用恰当的数据类型

MATLAB同样有多种数据类型,不同的数据类型在存储和访问效率上也不尽相同,选用恰当的数据类型对写出高效的MATLAB代码同样具有重要的意义。(详见例【例4.6-8】)2023/1/31二、提高代码效率的方法

4. 减少减少无谓损耗——给一些函数“瘦身”

MATLAB自身以及工具箱里的很多函数具有较

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论