0%

数学建模-MATLAB语法基础

MATLAB程序设计语言基础

一、变量与常量

1.1 常见数据类型

1.1.1 数值型数据

应当意识到,数值型数据概念的提出是与符号型数据想对应的,matlab一般存储数据为双浮点数,就势必造成存储和运算的误差,比如将 $\frac{1}{3}$ 存为 0.333333,更不要提 0 的模糊表示了。如果是整数的话,其实也会造成运算的误差,比如 5/2=2 的取整操作。所以准确的来说,运用数值型数据只能得到近似解,而不能得到解析解。而用符号型数据可以避免这个漏洞。

一般的赋值操作就会把值默认为 double 类型,但是可以用数据类型转换函数进行转化。有如下语句。

a=1.1; %construct a numeric variable called 'a'
a=uint(a); %Convert to unsigned 8-bit integer

另外,如果一句话之后不加分号,会会在命令窗显示这次运算的运算结果。

如果赋值语句(赋值变量=赋值表达式),那么如果省略了赋值变量和等号,结果就会被存储在ans中。

1.1.2 符号型数据

正是由于数值型数据的弊端,matlab提供了符号型数据,数据会完好无损的存在于声明的变量中,同时还可以利用一些函数来对声明出的符号型数据变量进行一些限制。有如下语句

syms a b real %a,b都是声明的变量,real说明他们是纯实数,与此类似的还有positive 整数、complex 复数、integer 整数
assumptions(a) %可以用这个语句看出a是什么类型的符号型数据
assume(a>=-1);assumeAlso(a<5); %可以用这两个语句对声明的变量进行进一步限制

但是需要注意,这样声明出的变量是没有办法直接赋值的,因为matlab应该属于弱类型格式转换,直接赋值会将符号型数据变成数值型数据 如:

b=1.1;

这样b就变成数值型了,是因为1.1数值型,所以我们应该借用数据转换的思想,用类型转换来进行赋值,有语句

b=sym(1.1) %注意不是syms,而是sym
b=sym('1.1')

则两句话都可以把b赋成1.1 ,但是第一句话的1.1在很多位以后会变得不精确,这是因为1.1是一个数值型数据,所以不准,但是第二句话会更好一些。

当我们有了一个符号型数据的时候,我们可以用下面的命令来控制以任意精度显示这个变量

vpa(b,n) %这条语句会把b以n精度现实

对于这小节出现的不加括号的函数(我也不确定syms是不是函数),有一种解释是说,如果加了括号,他会解析里面的内容,而不加括号,只会解析输入的字符串,没有验证过。

1.1.3 字符串数据

字符串类型,注意不是字符类型,与C语言不同,是用单引号控制的,在传参隐函数部分有很多应用。

1.1.4 函数句柄

也被称为函数指针(应该跟C语言类似),多在传参的时候应用,因为我没有接触太多,不知道具体用处,声明用法如下

f=@(x,y)sun(x.^a+y.^b) %即f=@(变量列表)函数计算表达式

函数里面的参数的值(a,b)取的是声明之前的值,如果之后a,b在发生变化,函数不会发生改变。这种函数被称为匿名函数,f就是函数句柄。

1.1.5 单元数组

单元数组有点类似于广义表,不要求数组内的元素具有相同性质,可以第一个元素是标量,第二个是一个二维坐标,第三个是个矩阵。单元数组用 {} 来声明。由于只学了一点,还没有见过他的应用。

1.1.6 类与对象

1.2 常见常量

1.2.1 小量eps

默认值为 $2.2204\times 10^{-16}$ 当某个值的绝对值小于 eps 时,就会被matlab判定为 0。

1.2.2 虚数单位i、j

i 和 j:表示虚数单位,如果表示一个虚数单位,一般用 1j 而不是 j。

1.2.3 无穷 Inf

无穷大量,适用于有限数除零等运算。

1.2.4 不定式 NaN

不定式(not a number) 用于 0/0,Inf/Inf 等运算。

1.2.5 圆周率

pi:圆周率 $\pi$ 的双精度浮点表示。

1.2.6 警告错误信息

lasterr和lastwarn:存放最新一次的错误或者警告信息,为字符串类型。


二、基本操作

2.1 生成矩阵

2.1.1 矩阵总论

应该明确的是,矩阵不是一种特殊的数据类型。他就是一个普通的代数结构。这种思想其实在高等代数就体现出来了,标量和向量和矩阵没有什么明显的区别,只不过是数学家为了描述世界提出的结构。只是因为我们先接触标量,才觉得矩阵那么奇怪。

矩阵不单独作为一种数据类型的原因是因为所有的数据类型都有矩阵形式,矩阵更像是一种状态,这种状态可以不断叠加。

在matlab里,很多东西都是用矩阵表示的,比如函数可以有多个返回值,这时就需要用矩阵作为返回值,当传入参数的时候,也可以用矩阵的形式,当画图的时候,因为没有办法做到每个点都计算,所以会选取网格上的点计算,这时网格也是用矩阵来表示的。这些都是由于矩阵在数学上有极好的抽象能力决定的。

最后说一点闲话,matlab是没有数组这个数据结构的,所以是否暗示了矩阵数组是可以相互替代的。我认为是可以的,两者都有有序集合的特点。

2.1.2 [ ] 声明法

用 [ ] 可以声明矩阵,矩阵的横行之间用 ;来分割,横行里每个元素用逗号或者空格分隔(这两种分隔方式应该是完全等价的)。有如下声明语句

a=[1,2,3;4,5,6;7,8,9];

当然,因为矩阵可以嵌套表达,所以有这样的形式,注意一下行和列的区别

a1=[a;[1 2 3]];
a2=[a [1;2;3]];

2.2.3 冒号表达式

冒号表达式可以生成一个行向量,他有两种写法

v1=0:0.2:pi; %从0开始,步距为0.2,直至不超过pi的最大值
v2=0:pi %步距如果不声明的话,就是1

冒号表达式完全等价于一个行向量,所以可以有如下合理语句:

a=[1,2,3;4,5,6;7,8,0];
a1=[a;1:3];

2.2.4 子矩阵提取

提取子矩阵的具体方法如下

$v1$ 向量表示子矩阵要保留的行号构成的向量,$v_2$ 表示要保留的列号(注意行列都是从1而不是0开始计数的)。其中,$v_1$ 表示子矩阵要保留的行号构成的向量,$v_2$ 表示要保留的列号构成的向量。可以这么说,$B$ 矩阵的(x,y)位置的元素是原矩阵 $A$ 的($v{1x},v_{2y}$)位置的元素。若 $v_1$ 为,则表示要提取所有的行,$v_2$ 亦有相应的处理结果。关键词 $end$ 表示最后一行。

有如下实例

a=[1,2,3;4,5,6;7,8,9];
a1=a(:,end:-1:1); %将矩阵左右翻转
a2=a(1:2:end,:); %提取由所有奇数行和所有列交叉形成的部分

另一种提取子矩阵是利用逻辑类型(上一种是利用正整数类型),即布尔值类型,在这里,置 1 的元素所在的行列会被保留,如

a=[1,2,3];  
b1=isprime(a);
b2=[0,1,1]; 
b3=logical(b2); % 初始一些条件
c1=a(b1);  %可以运行
c2=a(b2);  %不可以运行,因为不是逻辑类型
c3=a(b3);  %可以运行,因为是逻辑类型

2.2 查询

2.2.1 基本操作

只要按如下语句查询,就有

help something

2.2.2 英语

因为解释都是英文的,所以有一个好的英文词汇积累是很重要的,故录于下

  • scalar 标量
  • symbolic variable 符号型变量
  • norm 范数
  • permutation 排列
  • interval 区间
  • piecewise 分段
  • numerator 分子
  • denominator 分母
  • sparse 稀疏的
  • syntax 句法
  • gradient 斜率

2.3 数学运算

2.3.1 矩阵的代数运算

B=A.'  % B是A的转置
c=A-B  % C是A、B相减的结果
C=A*B  % C是A、B相乘的结果
X=A\B  % C是A_{-1}B的结果
X=B/A  % C是BA_{-1}的结果
F=A^x  % F是A连乘x次的结果
B=fliplr(A)  % B是A左右翻转的结果
C=flipud(A)  % C是A上下翻转的结果
D=rot90(A)   % D是A逆时针旋转90度的结果
E=rot90(A,k)  % E是A逆时针旋转90k度的结果
C=A.*B  % 点运算就是对相应元素的直接运算,因为矩阵的普遍性,这个运算形式使得很多
e=norm(A)  % 即求A的范数

2.3.2 矩阵的逻辑运算

这个好像在离散数学的关系运算中用得到。

C=A&B
C=A|B
C=~A
C=xor(A,B)  % C均为布尔值矩阵

2.3.3 矩阵的布尔运算

这一节涉及了多个函数,其中函数的参量基本上都是一个布尔表达式。所以尽管节名不太严谨,但很形象。但是还是需要意识到,这里的布尔值也是个矩阵。

C=A>B  % 维持了逻辑运算的逻辑

find函数可以查找出满足某关系的矩阵下标,当返回值为一个值时(单下标情况),find函数将整个矩阵按列排列成新的列向量,然后再返回满足要求的元素的下标。如

a=[1,2,3;4,5,6;7,8,0]
i=find(a>=5)

也可以采用双下标的形式,那么就不需要进行矩阵的重新排列,i,j 分别代表横坐标和纵坐标。

另外还有 all 和 any 也是很实用的查询函数,有实例(具体的应用懒得写了,help里可以查询)

a1=all(a>=5);
a2=any(a>=5);

2.3.4 解析结果的化简和变换

syms; sP=(s+3)^2*(s^2+3*s+2)*(s^3+12*s^2+48*s+64); %初始化一个冗杂的多项式
P1=simplify(P)  % 返回值是一个符号型数据,代表化简的结果
P2=expand(P)  % 返回值是一个符号型数据,代表多项式的展开结果
P3=factor(P)  % 返回值是一个符号型数据矩阵,矩阵中的元素就是因式
P4=prod(P)  % 返回值是一个符号型数据,是因式分解后因式的乘积形式
P5=latex(P1)  % 返回值是一个符号型数据,为LaTeX格式

2.3.5 离散数学运算

n=floor(x)  % 向下取整
n=ceil(x)  % 向上取整
n=round(x)  % 四舍五入
n=fix(x)  % 向零取整
[n,d]=rat(x)  % 将x中元素换位最简有理数,n和d分别为分子和分母矩阵
B=rem(A,C)  %  A中元素对C中元素求模得到的余数
k=gcd(n,m)  % 求最大公约数
k=lcm(n,m)  % 求最小公倍数
v=factor(n)  % 分解因式,得到一个向量
v1=isprime(v)  % 判断v中的向量各个元素是否为质数,如果是的话,得到一个
V=perms(v)  % 对向量v中的元素进行全排列,结果由矩阵V返回

三、高级操作

3.1 流程结构

3.1.1 复杂结构总论

我将流程结构函数结构称为复杂结构,具体表现为一个语句没有办法实现功能,而是需要一个语句块来实现一个基础功能,这种特点导致了最大的困难是语法,因为语句块设计到将多个语句组织到一起,那么组织形式就是重中之重。

对于组织形式,可以说跟C还是很不一样的。首先没有了 { } 代表的语句块分割结构,而变成了以end结尾的结构。此外,由于 ;分号 不再是一个语句结束的必要条件,而变成回车,所以初学很容易犹豫担心完成不了一个语句块就结束了,但是其实不用担心,当写下如 while 或者 for 或者 if 等起始句以后,回车就不会产生执行语句的功能,知道对应的end,才会有执行结果,,而且还可以自动缩进

3.1.2 循环结构

有 for 和 while 两种结构,对于后面加不加括号,加不加分号,加不加逗号,其实都可以,所以我就选用了一种我最舒服的结构,如下

%{
for(i=v)
	循环结构体;
end;
%}

for(i=1:10000)
    s=s+i;
end;

%{
while(条件式)
	循环结构体;
end;
%}

while(i<=10000)
	s=s+i;
	i=i+1;
end;

3.1.3 条件转移结构

if(条件1)
	语句组1;
elseif(条件2)
	语句组2;
else
	语句组3;
end;

3.1.4 开关结构

switch(开关表达式)
case 表达式1
	语句段1;
case 表达式2
	语句段2;
otherwise
	语句段3;
end;

3.1.5 试探结构

本语句结构会试探性的执行语句段1,如果报错,就执行语句段2。

try
	语句段1;
catch
	语句段2;
end;

3.2 函数

3.2.1 函数总论

matlab跟C语言在函数方面的处理不同,可能是解释型语言的特性,函数没有办法定义在主文件中的,想要定义函数,必须新开一个文件,然后在里面写函数。对于具体的机理,我看到一种说法是这么说的,matlab会将文件进行识别成两类文件,非函数文件函数文件。当文件开头就开始定义函数时,matlab将此文件识别为函数,那么文中的第一个函数将被视为该文件对应的函数(主体函数,优先运行),而文中定义的其他函数,此时视为可在文中互相调用的附属函数。这个“函数调用文中函数”机制允许我们在文中定义函数。当文件开头不是以“function”定义的函数时,文件被识别做非函数,则逐行解析运行,若下文有函数定义,则报错。

对于附属函数,所谓的“文中定义”,可能是在函数内部再次进行函数声明,而不是两个函数呈现并联关系。

3.2.2 函数的格式

虽然教材中在函数的结尾没有用end标识符,但是我在进行附属函数定义时,还是用到了end标识符才能顺利定义,所以还是加上吧。而且好像还不能加分号结尾。

此外,用help+函数名的格式,就可以显示函数中的注释内容。

%函数的格式
function [返回变量列表]=函数名(输入变量列表)
	%注释说明语段
	输入、返回变量格式检测,输入变量存储在nargin中,是自动生成的。
	函数体语句;
end

%单纯的函数
function [m, s]=findsum(k)
	% 为了寻找求和
	s=0;
	m=0;
	
	while(s<=k)
    	m=m+1;
    	s=s+m;
	end
end

%嵌套附属函数
function [m, s]=findsum(k)
% 为了寻找求和
	s=0;
	m=0;
	
	function n=Add(k)
		n=k+1;
	end
	
	while(s<=k)
    	m=Add(m);
    	s=s+m;
	end
end

3.2.3 可变输入输出个数的处理

这是单元数组的一个重要应用。有下例:

matlab提供的cover() 函数可以用来求解两个多项式的乘积,我们可以编写一个函数来计算任意项多项式的乘积,需要明悉的是,用向量来表示多项式。

function a=convs(varargin)
	a=1;
	for(i=1:nargin)
		a=conv(a,varargin{i});
	end
end

3.2.4 匿名函数

匿名函数好像只适合描述数学函数,就是那种公式型的,简单的函数,其基本的结构为:

f=@(变量列表)函数计算表达式

其中 f 就是函数句柄。但其实这个是有误区的,匿名函数既有数值的,又有非数值形式的。匿名的意思是没有名字

3.2.5 符号函数

我一直都没有搞懂他与匿名函数的区别,还有与符号型变量的区别。现在看上去,应该是符号型函数描述的是数值型函数,匿名函数的范围更广一些,对于某些工具函数,两者可以相互替换,但是对于某些函数,两者不能替换。所以顺其自然就好了。

我现在想来应该是这样的,符号变量进行运算的时候,遵循的应该是符号运算(具体的我也不清楚,但应该是那种能得到解析解的运算),所以显然这种运算并不能很好的解决所有的问题,尤其是在需要求解数值解的时候,显然符号函数就远远不够进行这类运算的,这也是在fsolvefminunc函数这种求解数值解的函数中使用的是匿名函数了。

3.2.6 分段函数的表示

可以利用布尔值表达式巧妙的解决这一问题。例子如下

z=0.5457*exp(-0.75*y.^2-3.75*x.^2-1.5*x).*(x+y>1)+...
  0.7575*exp(-y.^2-6*x.^2).*( (x+y>-1) & (x+y<=1) )+...
  0.5457*exp(-0.75*y.^2-3.75*x.^2+1.5*x).*(x+y<=-1); %...是换行的需要,matlab不能忽视换行

3.3 绘图

3.3.1 二维曲线绘制

plot(x,y,选项);  % 注意这里x是向量,y既可以是向量,又可以是矩阵
plot(x1,y1,选项,x2,y2,选项,x3,y3,选项); % 可以用这个来绘制多个曲线在一张图上
plotyy(x,y1,x,y2); % 用于解决幅值差悬殊的情况
ezplot('隐函数表达式',定义域);
ezplot('x^2*sin(x+y^2)+y^2*exp(x+y)+5*cos(x^2+y)',[-10,10]);

3.3.2 三维曲线绘制

t=0:0.1:2*pi;
x=t.^3.*exp(-t).*sin(3*t);
y=t.^3.*exp(-t).*cos(3*t);
z=t.^2;                    % 初始化曲线
plot3(x,y,z)
grid % grid是加网格线的意思,但是这样是没有办法添加成功的
plot3(x,y,z),grid % 这样才是正确的添加方式,可能是解释型语言的弊病吧
plot3(x,y,z),hold on,stem3(x,y,z) % hold on 是将两个图像显示在一种图上的方法
plot3(x,y,z),hold on,stem3(x,y,z),grid % 最全的图像

3.3.3 三维曲面绘制

不同于曲面绘制曲面绘制需要先生成网格数据,然后才能生成图片。

[x y]=meshgrid(-1:0.04:1,-2:0.04:2); % 生成网格数据
z=0.5457*exp(-0.75*y.^2-3.75*x.^2-1.5*x).*(x+y>1)+...
  0.7575*exp(-y.^2-6*x.^2).*( (x+y>-1) & (x+y<=1) )+...
  0.5457*exp(-0.75*y.^2-3.75*x.^2+1.5*x).*(x+y<=-1);
surf(x,y,z)