大概记录的是 MATLAB 的入门之路。反正《数值分析》课程也得用。

数据类型与基本操作

数值数组

MATLAB 中数组的存储方式是 column-major order. 因此,数组的第一个维度是列数。

数组的创建

等距数组

% Comment
% 在代码行的后加分号,运行结果不会显示在命令行窗口;
% 不加分号,变量的值会随着运行显示在命令行窗口。 
start = 1; % Variable assignment  
finish = 11; % Closed this way
stride = 2;

a = start:stride:finish % [1, 3, 5, 7, 9, 11]
b = finish:-stride:start % [11, 9, 7, 5, 3, 1]
c = start:finish % The default value for stride is 1

xstart = 1; xfinish = 100; xpoints = 5;
x = linspace(xstart, xfinish, xpoints)
% [1, 25.75, 50.5, 75.25, 100]

ystart = 1; yfinish = 5; ypoints = 5;
y = logspace(ystart, yfinish, ypoints)
% [10, 100, 1000, 10000, 100000]

逐个元素输入

a = [1, 2, 3, 4] % Shaped 1 x 4
b = [1 2 3 4] % Also shaped 1 x 4
c = [1, 2; 3, 4]
% Shaped 2 x 2
% 1 2
% 3 4

随机数产生

rng(1); % Random seed

a = rand(2, 5) % Uniformly distribution between 0 and 1, shaped 2 x 5
b = randn(2, 6) % Normal distribution, shaped 2 x 6

upperbound = 10;
lowerbound = 1;
c = randi([lowerbound,upperbound], 2, 5)  % Random Interger

其他初始化

a = ones(3, 4)
b = zeros(3, 4)
c = eye(5)

v = [5, 8, 10]
d = diag(v) % Diagonal

元素的访问和寻找

数组的维度和大小

rng(2); % Seed 2
a = randn(3, 4)

v = size(a) % [3, 4], shaped 1 x 2
v = size(a, 1) % ans = 3, get the value of the 1st dim
v = size(a, 2) % ans = 4

数组的循秩访问

rng(3);
a = randi([1, 20], 3, 4)
b = a(2, 3) % namely a[2][3]
c = a(2, :) % namely the second row of a
d = a(:, 3)
f = a(2, 1:3)
g = a(10) % NOTICE: column-major order!

idx = 12:-1:1
h = a(idx) % Inversed order of a, shaped 1 x 12

k = a(3, end) % [end] could be viewed as [-1] in Python
			  % end-1, end-2, ... , are also supported

逻辑寻访

rng(4);
a = randi([1,50], 6, 8)

b = a > 10 % Boolean, shaped 6 x 8
a(b) = 100 % Set all those greater than 10 to 100

c = find( a > 50 ) 
% Column vector shaped k x 1, consisting the index of elements != 0
a(c) = NaN

数组的扩充、收缩与重排

扩充

a = 1:5; b = 6:10; c = 11:15;
a_b = [a, b] % 1 x 10
abc = [a; b; c] % 3 x 5

AB1 = repmat(a_b, [1, 2]) % 1 x 20
AB2 = repmat(a_b, [2, 1]) % 2 x 10

收缩

a = randi([1,20],6,8)

b = a;
b([2, 3, 4, 5], :) = []

c = a;
c(:, 1:3) = []

变形

a = 1 : 24
b = reshape(a, 3, 8) % 3 x 8, column major
c = reshape(a, 2, 3, 4)

c(:,:,1) =

     1     3     5
     2     4     6


c(:,:,2) =

     7     9    11
     8    10    12


c(:,:,3) =

    13    15    17
    14    16    18


c(:,:,4) =

    19    21    23
    20    22    24
    

翻转

a = reshape(1:24,2,3,4)
b = flipud(a)    % 将数组从上向下翻转
c = fliplr(a)    % 将数组从左向右翻转
d = flipdim(a,1) % 将数组沿第一个维度翻转
f = flipdim(a,2) % 将数组沿第二个维度翻转

g = a(1,:)
h = g'             % 转置

旋转

a = reshape(1:24,2,3,4);
b = rot90(a)     % 将 a 旋转 90 度
c = rot90(a,2)    %将 a 旋转 180 度
% 还有 rotx, roty, rotz, rot45 等函数可供探索

排序

rng(6);
a = randn(3,6);
[b, idx] = sort(a)        % 对 a 的每一列按升序排序,idx 为索引。
c = sort(a,2)             % 对 a 的每一行按升序排序
d = sort(a,'ascend')      % 对 a 的每一列按升序排序
f = sort(a,2,'descend')   % 对 a 的第 2 个维度按降序排序
g = sort(a,'ComparisonMethod','abs')
%还有 cat,circshift,horzcat,vercat,permute,ipermute,shiftdim,sortrows,squeeze 等函数可供探索

数组运算

四则运算

MATLAB 中的运算都是矩阵与矩阵的运算,或者标量与矩阵的运算。也就是说,要满足矩阵运算的规则。

% 矩阵运算
a = [1,2;3,4;5,6]
b = a'            %共轭转置
c = b*a           %矩阵乘法,注意维度要符合
d = [1,0;1,1]     
f = a/d           %对d求逆,再左乘a
g = d\b           %对d求逆,再右乘b
h = 3*a           %3是标量,与矩阵a相乘
k = a/3           %矩阵a除以标量3
l = 3\a           %含义同上
m = 4^d           %4的d次幂
n = d^4           %d的4次幂

% Elementwise
a = [1,2,3;4,5,6]
b = a.'             %非共轭转置
c = [0,1,0;1,0,1];  
d = a.*c            %按元素乘,要求矩阵形状完全相同
f = c./a            %按元素除,c除以a
g = a.\c            %按元素除,a除以c
h = 4.^a           
l = a.^3    

上文用到了左除(\)和右除(/)。左除是 MATLAB 中独有的,其速度、准确度都远高于右除。此外,使用矩阵除法时应该注意,对于奇异矩阵或近似于奇异的矩阵,MATLAB 会给出警告。

数据类型

MATLAB 的数据类型包括 double,uint8,uint16,boolean,char,str

前 3 种的转换函数分别为 double(),uint8(),uint16()
char 是字符,char(x) 会将 x 按 ASCII 值转变成相应的字符。str 是字符串,被看做一个行向量。具体示例见字符串部分。

MATLAB 中还有一些保留关键字,列举如下:

  • eps:浮点数相对精度
  • i 或 j:复数单位
  • inf 或 Inf:无穷大
  • intmax:最大正整数
  • intmin:最小负整数
  • NaN 或 nan:非数。注意,不能用 a==nan 判断 a 是否为 nan。
  • pi:π
  • realmax:最大正实数
  • realmin:最小正实数
% Complex numbers
a = 1+2j
a_real = real(a)    %a的实部
a_imag = imag(a)    %a的虚部
b = conj(a)         %b是a的共轭
b_abs = abs(b)      %b的模长
b_angle = angle(b)  %b的辅角

% NaN
rng(5);
a = randi([1,20],4,5);
a(a>10) = NaN;
idx1 = isnan(a);
% a==NaN 不能正确判别 NaN

% 不同进制的转化
% MATLAB 支持 2 进制(bin),8 进制(oct),10 进制(dec)和 16 进制(hex)
a = 1:10;
b = dec2bin(a)
c = bin2dec(b)

特殊运算

% 和与积
a = reshape(1:10,2,5);
b = cumsum(a)            %沿 a 的第一个 length 不为 1 的维度求累积和
c = cumsum(a,2)          %沿a的第二个维度求累积和
d = cumsum(a,'reverse')  %沿a的第一个不为1的维度,反向求累积和
f = sum(a)               %沿a的第一个不为1的维度求和,其他用法同cumsum
g = sum(a,'all')         %对a的所有元素求和
s3 = cumprod(a)          %累积积,用法同上
s4 = prod(a)             %求积,用法同上

% 最大值
rng(6);
a = randi([0,50],3,6,2)
b = max(a)                    %沿a的第一个不为1的维度计算
c = max(a,[],2)               %沿a的第二个维度计算
[d,idx] = max(a)              %idx是每个最大值的位置
f = max(a,[],'all')           %在a的所有元素中找最大值
g = max(a,[],[1,3])           %在a的第1个和第3个维度中求最大值
[h,idx] = max(a,[],'linear')  %idx是最大值的线性索引
k = randi([0,50],3,6,2)              
l = max(a,k)                  %求a,k中的最大值

% 逻辑操作
a = [-2,-1,0,0,1,2,3];
L1 = ~(a>1);          %按位取非
L2 = (a>0)&(a<2);     %与
L3 = (a>0)&&(a<2);    %与,第一项为false时,不再计算第二项
L4 = (a<0)|(a>2);     %或
L5 = (a<0)||(a>2);    %或,第一项为true时,不再计算第二项
b=a;
c = xor(a,b);         %异或
d = all(a);           %a中元素全不为0时,d为真
e = any(a);           %a中元素至少有一个非0时,e为真
idx = find(a>10);
f = isempty(idx);     %检查idx是否为空矩阵

% MATLAB还可以计算矩阵的特征值,对矩阵做奇异值分解、QR分解等。

字符串

a = 'Three-six-nine, hon, the goose drank wine.';
%如果字符串中有单引号,用两个连续的单引号表示。
b = size(a)     %可以看出,MATLAB将字符串数组视为行矩阵

a = 'May you long day and pleasant nights.';
a14 = a(1:4)       %取出字符串数组的前4个元素   
ra = a(end:-1:1)   %翻转字符串数组

a = 'May you have twice the number.';
Ua = double(a)          %查看a的ASCII码值
c = char(Ua)            %将ASCII转化回字符串
w = find(a>='a'&a<='z') %找出所有的小写字母
Ua(w) = Ua(w) -32       %将小写字母变为大写字母
d = char(Ua)            %将ASCII转化回字符串

控制流语句

MATLAB 很适宜向量化编程,也就是说,编写 MATLAB 程序的时候要尽量用矩阵和数组代替循环,尤其是 2 层及以上的循环。

在 MATLAB 中,二维矩阵的运算速度要远快于 2 层循环。因此,本节介绍的所有数据流控制语句都要尽量少用,除非万不得已。

% if/else/elseif
a = 5;
if a<0
    a = -a;
elseif a>10
    a = a-3;
else 
    a = a+1;
end

% while
sum = 0;
while sum < 10
    sum = sum + 1;
end

% for
sum = 0;
for ii = 1:10
    sum = sum + ii;
end

% switch
a = 10;
switch a
    case 1
        a = 3;
    case 3
        a = 1;
    otherwise
        a = 4;
end

% try/catch
a = 1:10;
try 
    b = a*a;
catch ME
    b = a.*a;
end

实用技巧

% 读写 mat 文件
clear
a = 3;b = 4; c = 5;
save file1;                %把工作区中全部变量保存到名为file1.mat的文件中。
                           %如果file1.mat原先存在,它的原始内容会被删除。
save file2 a b;            %把变量a和b保存到file2.mat文件中。
save file2 c -append       %把变量c添加到file2.mat中,不会删除file2.mat中原有的值
save file3 -ascii          %把变量保存到8位ascii文件file3.mat中
save file4 -ascii -double  %把变量保存到16位ascii文件file4.mat中
clear
load file1                 %把file1.mat中的全部变量加载到工作区
clear                      
load file1 a b             %把file1.mat中的变量a和b加载到工作区
clear
load file3 a -ascii        %把ascii文件file3.mat中的变量a加载到工作区

% 科学计数法
a = 1.4e3                  %a=1400

% 输出格式
a = format                %返回当前的format
format long               %将输出格式设定为long           
format short              %将输出格式设定为short
% format的参数有多种选择,可自行探索

% disp
name = 'Jake';   
chassit = 99;
X = [name,', son of Elm, ka-tet of ',num2str(chassit)];
disp(X)
name = 'Edward';   
chassit = 19;
X = sprintf('%s Cantor Dean, ka-tet of %d',name,chassit);
disp(X)

数据结构

字符数组与字符串数组

在 R2016b 版本之后,Matlab引入了字符串数组 string。只有一个字符串的字符串数组也可称为字符串标量。

%% 创建
char_array = 'THU EESAST' % 字符数组
string_array = "THU EESAST" % 字符串数组

%% 互相转换

% 字符串数组 -> 字符数组
string_array2 = "string to char"
strlength(string_array2) % 14
char_array2 = char(string_array2)
size(char_array2) % [1, 14]

% 字符串列向量 -> 字符数组
string_array3 = ["string1";"string2";"string3"] % 3 x 1
char_array3 = char(string_array3)

% char_array3 =  % 3 x 7 char
%     'string1'
%     'string2'
%     'string3'

% 字符串行向量 -> 字符数组
string_array4 = ["string1","string2","string3"]
char_array4 = char(string_array4)
% 1 x 7 x 3 char, every page contains a "string\d"

% 字符串矩阵 -> 字符数组
string_array5 = ["string1","string2","string3";"string4","string5","string6"]
char_array5 = char(string_array5)
% 2 x 7 x 3 char, every page contains a column

% 字符数组行向量 -> 字符串数组
char_array6 = 'char' % 完全等价于 char_array6 = ['c','h','a','r']
string_array6 = string(char_array6) % "char"

% 字符数组列向量 -> 字符串数组
char_array7 = ['a';'b';'c';'d']
string_array7 = string(char_array7) % 4 x 1 string

%% 两者的行为区别
% 减去一个字符
char_array8 = 'abcdefg';
string_array8 = "abcdefg";
char_array8 - 'a'
char_array8 - 97
% 可以逐条尝试执行下面几行代码
% char_array8 - "a"  
% string_array8 - 'a'
% string_array8 - "a"
% string_array8 - 97 % string 类型的操作数不支持 '-',string 作为 arg[1] 还是 arg[2] 都报错

% 将字符串数组或字符数组相加
% 字符数组会得到Unicode码相加的结果:
char_array9 = 'THU EESAST';
char_array10 = '!';
char_array9 + char_array10 % ans 1 x 10 [117, 105, ...]

% 字符串数组会将两端字符串拼接起来:
string_array9 = "THU EESAST";
string_array10 = "!";
string_array9 + string_array10 % 拼接

% 将内容为数字的文本转换为 double 等类型
% 字符数组会得到对应字符的Unicode编码:
char_array11 = ['123';'1e2';'1.2'];
char_array12 = '123 456 789';
double(char_array11)
double(char_array12)
% 字符串数组则直接得到文本表示的数字:
string_array11 = ["123";"1e2";"1.2"];
string_array12 = "123 456 789";
double(string_array11) % [123; 100; 1.2]
double(string_array12) % 123456789

% 欲将字符向量转换为文本表示的数字,请使用str2double函数
% 此函数的输入可以为字符向量、字符向量元胞数组(之后会学习到)或字符串数组:
str2double('123')
str2double('1e5')
%str2double(string_array11) % 3 x 1 ans
%str2double(string_array12) % NaN
%str2double(char_array11) % NaN
%str2double(char_array12) % NaN
% 可以尝试执行最后四行代码,看看会得到什么结果
% 关于str2double函数的详细内容请参考MATLAB说明文档
% 另有str2num函数可供选择

%% 将数字转换为文本
double_array1 = [28165 21326 22823 23398 30005 23376 31185 21327 23398 22521 37096];
% 字符数组会按照Unicode编码进行转换:
char(double_array1)
% 字符串数组则直接转换为文本:
string(double_array1)
% 欲将数字转换为对应的字符数组类型文本,请使用num2str函数:
num2str(double_array1) % '28165 21326 ...'
num2str(double_array1') % 11x5 char

%% 索引操作
% 字符数组可以像数值数组一样正常索引
% 而单个字符串标量则无法索引内部的某个字符
char_array13 = '123456789';
string_array13 = "123456789";
char_array13(3:6)
% string_array13(3:6) % length == 1, error

元胞数组

元胞数组是包含称为元胞的索引数据容器的数据类型。每个元胞可以包含任意类型的数据。

元胞数组创建

C1 = {'a','char',"str";1.2,[1,2,3],1>0}

% C1 = 
%     {'a'     }    {'char' }    {["str"]}
%     {[1.2000]}    {[1 2 3]}    {[    1]}


C2{2,2} = 'char'
C3(2,2) = {'char'}      % 注意()与{}的区别

% 使用cell函数预分配内存:在需要频繁改变元胞数组大小的情况下
% 推荐提前预估需要的最大尺寸,预分配内存以提高运行效率
C4 = cell(3)
C5 = cell(2,3,4)
C6 = cell([2,3,4])    

元胞数组的索引

理解元胞数组的索引是理解元胞数组的关键。

与一般的数组不同,元胞数组有两种形式的索引:

使用圆括号 () 的索引与使用花括号 {} 的索引。

  • 使用圆括号 () 的元胞索引
    • 此形式的索引引用的是元胞本身,最直观的理解是,返回值是 cell 类型
  • 使用花括号 {} 的内容索引
    • 此形式的索引引用的是元胞中的内容,返回值的类型取决于该内容
C = {'char',"str",1};
C(1,1)
class(C(1,1)) % cell
C(1,1:3)

C = {'char',"str",1};
C{1,3}
class(C{1,3}) % double
C{1,1:3}

C = {'c', "s"; 1, [1,2]}
C{2,3} = (1>2)
% C(3,3) = "s" % Cannot convert string to cell
  • 删除元胞数组的数据
C1 = {1,2,3;4,5,6;7,8,9};
C1(2,:) = []
C2 = {1,2,3;4,5,6;7,8,9};
C2{2,2} = []        % 这样不能删除元胞本身,只能将其中的内容赋值为空
% 如果是下面这样呢?
% C3 = {1,2,3;4,5,6;7,8,9};
% C3(2,2) = []    % err
  • 多级索引
Num = [1,2,3;4,5,6;7,8,9];
char1 = 'char';
C1 = {1,2,3};
C2 = {[1,2,3],[4,5,6],[7,8,9]};
C = {Num,char1;C1,C2}
% C = 
%     {3×3 double}    {'char'  }
%     {1×3 cell  }    {1×3 cell}

C{1,1}(2,2)
C{1,2}(1,:)
C{2,1}(1,1)
C{2,1}{1,1}
C{2,2}{1,2}(1,2)
  • 数组合并

与一般的数组一样,使用方括号 [] 合并数组。

如果使用花括号 {},原数组将被当作一个元胞的内容。

% 元胞数组与元胞数组合并
C1 = {1,2,3;4,5,6};
C2 = {7,8,9};
C = [C1;C2] % 3 x 3 cell
C = {C1;C2} % 2 x 1 cell

% 元胞数组与非元胞数组合并
C1 = {1,2,3};
char1 = 'char';
num = [1,2,3];
C = [C1,char1,num] % 1 x 5 cell
C = {C1,char1,num} % 1 x 3 cell
  • 类型转换
% cell2mat
C1 = {[1,2],3;[4,5],6;[7,8],9} % 3 x 2 cell
A1 = cell2mat(C1) % 3 x 3 number
C2 = {'THU';' EE'} % 2 x 1 cell
A2 = cell2mat(C2) % 2 x 3 char

% mat2cell
A = reshape(1:60,[3,4,5]);
C = mat2cell(A,[1,2],[2,2],[2,3])

结构体数组

结构体数组是使用名为字段的数据容器将相关数据组合在一起的数据类型,每个字段都可以包含任意类型的数据。使用 structName.fieldName 格式的圆点表示法来访问结构体中的数据。

创建

%% 直接赋值
% 此方式只能创建结构体标量,之后可以通过索引的方式添加元素
clear;
element.Atomic_Number = 1;
element.Symbol = 'H';
element % struct

element(2).Atomic_Number = 2;
element(2).Symbol = 'He';
element % 1 x 2 struct

%% 使用struct函数
% 此方式可创建非标量结构体
% 此函数一般重载为 struct(field1,value1,..,fieldN,valueN) 的形式:
% 若所有 value 都不是元胞数组或为标量元胞数组,则创建结构体标量;
% 若任一 value 为元胞数组,则创建结构体数组,维度同元胞数组;
% 若有两个及以上 value 为非标量元胞数组,则其维度必须相同。
clear;
element = struct('Atomic_Number',1,'Symbol','H') % struct
element = struct('Atomic_Number',{1},'Symbol',{'H'}) % struct
element = struct('Atomic_Number',{1,2,3},'Symbol',{'H','He','Li'}) % 1 x 3 struct
element = struct('Atomic_Number',{{1,2,3}},'Symbol',{{'H','He','Li'}}) % struct
element = struct('Atomic_Number',{1,2,3},'Symbol',{'H'}) % broadcasting
%element = struct('Atomic_Number',{1,2,3},'Symbol',{'H','He'}) % err
% 注:同一字段对应的值可以是不同类型的数据。

% 添加新的字段:通过圆点表示法添加
clear;
element = struct('Atomic_Number',{1,2,3},'Symbol',{'H','He','Li'})
element(1).Ar = 1.008;
element

[element(2:3).Ar] = deal(4.003,6.941); % Copy Input to Output
element

访问

clear;
[field1,field2,field3,field4] = deal('A','B','C','D');
[value1,value2,value3,value4] = deal({1,'char',"str",struct('a',1)},{struct('b',2),2,'char',"str"},...
    {"str",struct('c',3),3,'char'},{'char',"str",struct('c',4),4});
%以上部分涉及"逗号分隔的列表"内容,不在本次教程要求之内
s = struct(field1,value1,field2,value2,field3,value3,field4,value4)
s(1:2).A
{s(1:2).A}
s(2).A(2)
%s(1).A(2)
s(3).D.c 

串联

% 使用方括号[]串联结构体数组
% 要串联结构体,他们必须具有相同的字段集
% 但这些字段无需包含相同的大小或数据类型
clear;
s1.x = 1;
s1.y = 1;
s2.x = 2;
s2.y = 2;
s = [s1,s2]

函数与函数句柄

命名函数

MATLAB 中的函数通常按照如下形式定义:

function [y1,...,yN] = myfun(x1,...,xM)
	command
end

其中 y1,…,yN 是输出,x1,…,xM 是输入,myfun 是函数名称。可以将命名函数保存在以下位置:

  • 只包含函数定义的函数文件中。文件的名称须与文件中第一个函数的名称一致;文件中的其余函数可以作为局部函数,仅能被该文件主函数调用。
  • 包含命令和函数定义的脚本文件中。函数必须位于该文件的末尾。脚本文件不能与文件中的函数具有相同的名称。R2016b 或更高版本的脚本中支持函数。

例如:

clear;
x = [0,1,0.001,1.111,-2,1.3,0.51,3.96,1.22];
y = fmax(x)  % 函数保存在 ./fmax.m 中


% In ./fmax.m
function y = fmax(x)
    B = x.*exp(x) + 1./(x.^2 + x + 5) - x.^3;
    y = max(B);
end
clear;
x = 10;
[y,f] = fun1(x)

% In ./fun1.m
function [y,f] = fun1(x)
    temp = fact(x);
    y = log(temp);
    f = @fact;          %返回表示函数fact的函数句柄f
end

function y = fact(x)
    y = 1;
    for index = 2:1:x
        y = y * index;
    end
end

函数句柄

函数句柄是一种表示函数的数据类型,可以表示命名函数或匿名函数。使用 @ 运算符创建函数句柄。函数句柄的典型用法是将一个函数传递给另一个函数、从主函数外调用局部函数等。函数句柄不能组成一般数组,但可以收集到一个元胞数组或结构体数组中。

% 例1:表示命名函数
clear;
f1 = @fmax;
x1 = [0,1,0.001,1.111,-2,1.3,0.51,3.96,1.22];
y1 = f1(x1)

% 例2:表示匿名函数
clear;
f2 = @(x) x.^3;     %以x为自变量,y = x^3
x2 = [1,2,3,4,5];
y2 = f2(x2)

% 例3:将一个函数传递给另一个函数
clear;
f3 = @(x) x.^2;         %以x为自变量,y = x^2
integral(f3,0,10)       %f3作为函数integral()的参数

% 例4:从主函数外调用局部函数(1)
clear;
x4 = 10;
[y4,f4] = fun1(x4)
y4_1 = f4(x4)       %调用了局部函数fact()
log(y4_1)
log(y4_1) - y4

% 例5:从主函数外调用局部函数(2)/函数句柄组成的数组
clear;
[f5,f6] = circle;
f5.Area(10)
f5.Cirumference(10)
f5.Curvature(10)
f6{1}(10)
f6{2}(10)
f6{3}(10)
% In circle.m
function [f,f1] = circle
    f.Curvature = @Curvature;
    f.Area = @Area;
    f.Cirumference = @Circumference;            %f以结构体形式返回
    f1 = {@Curvature,@Area,@Circumference};     %f1以元胞数组形式返回
    enda

%计算圆的曲率
function y = Curvature(R)
    y = 1./R;
end

%计算圆的面积
function y = Area(R)
    y = pi .* R.^2;
end

%计算圆的周长
function y = Circumference(R)
    y = 2 .* pi .* R;
end

匿名函数

匿名函数是不存储在程序文件中、但与数据类型是函数句柄的变量相关的函数。匿名函数可以接受多个输入并返回一个输出。

% 例1:多个输入的匿名函数
clear;
f = @(x,y) x.^2 + y.^2;
f(2,3)

% 例2:参数化函数
clear;
a = 10;
b = 5;
f = @(x) x.^2 + a .* x + b;
f(2)
clear a b
f(3)            %可以看到,函数句柄f也保存了a和b的值,即便清除掉a和b也没有影响

% 例3:匿名函数的嵌套,以积分函数为例
F = @(x) integral(@(t) t.^2,0,x);
F(2)

% 例4:对于多输入函数,在创建函数句柄时可以只将部分输入作为自变量,其余输入作为参数
f = @(x,y,z) x.^2+y.^2+z.^2;
f1 = @(x) f(x,1,1);
f1([1,2,3])

符号变量

创建

% 用syms函数创建符号标量、数组、矩阵
clear;
syms a
syms b c
syms e [1,4]
syms 'd_%da' [1,4]
syms g h [1,4]
syms f 4

% 用syms函数创建符号函数
clear;
syms f(x) g(x)
syms m(x)
syms a(x,y) b(s,t)

% 用sym函数创建符号标量、数组、矩阵
clear;
a = sym('a')
b = sym('x')
c = sym('c',[1,4])
d = sym('d_%da',[1,4])
e = sym('e',4)
f = sym('f_a_%d_%d',4)
g = sym('g_a%d%d',4)

运算

% 例1:标量运算
syms a b
f = a + b
g = f^2
h = g/a
F = [a^2,b;g,a-b]
G = F.*F

% 例2:矩阵运算
clear;
A = sym('a',[2,2]);
B = sym('b',[2,2]);
A + B
A*B
A/B
diag(A)
A.'
A'

% 例3:符号函数
clear;
syms f(x) g(x,y) a b
f(x) = x^2
g(x,y) = x + y^3
f(a + 2)
f(3)
f(g)
f(g(a,b))
g(f,a)
cos(f)
exp(g)
exp(g(1,1))
%double(exp(g(1,1)))

% 例4:数值计算
clear;
syms a b f(x) g(x,y)
h = a + b^2;
A = subs(h,[a,b],[1.5,1])
double(A)
B = subs(h,{a,b},{[1.5,1;2.1,2],[1,1;3.25,4]})
double(B)
%请对比
1/sym(1234567)    %精确计算
sym(1/1234567)    %非精确计算

% 例5:一些函数
clear;
syms a b f(x)
f(a) = a^3 + 2*b*a^2 + 3
D1 = diff(f)                %一阶导数
D2 = diff(f,2)              %二阶导数
D1(2)                       %代入a=2
D1 = subs(D1,b,3)           %将参数b替换为3
double(D1(2))               %类型转换
e = subs(f,a,b)
g = fourier(cos(a))         %Fourier变换
f(x) = exp(-x)              
LF = laplace(f)             %Laplace变化

文件读写

交互方式

MATLAB选项卡 —— 主页 —— 变量 —— 导入数据/保存工作区

使用函数

% save:将工作区变量保存为MAT文件
clear;
A = [1,2,3,4];
B = [5,6,7,8];
save('./data/data1.mat',"A")
save('./data/data1.mat',"B",'-append')
%save('./data/data1.mat',"B")

% load:读取MAT文件(或ASCII文件,请自行查阅说明文档)中保存的变量
clear;
load('./data/data1.mat','A');
%load('./data/data1.mat');
b = load('./data/data1.mat','B')

% fileread:以文本格式读取内容
clear;
charA = fileread('./data/data2.txt')

% writetable(选学):将表(table)写入有分隔符的文本文件
clear;
Age = [19;20;21;22];
Name = ["Alice";"Bob";"Carol";"Dave"];
Weight = [71;69;64;67];
Height = [176;163;181;173];
T = table(Name,Age,Height,Weight);
writetable(T,'./data/data3.xlsx');

% readtable:基于扩展名确定文件格式;从带分隔符的文本文件(如txt,csv,dat)或电子表格文件(如xls,xlsx)读取列向数据来创建表(table)
clear;
T = readtable("data\data3.xlsx")
Name = T.Name
Age = T.Age
Height = T.Height(1:4)
Weight = T.Weight

% imread:从指定文件读取图像数据,由于文件格式较多,在此不一一列出,仅以bmp格式为例,更详细的说明参阅说明文档“imread”页面。
clear;
image1 = imread("./data/image1.bmp");
image(image1)

% imwrite:将数组保存为图像(灰度图或RGB彩色图像),数据类型为uint8或uint16。
clear;
load("./data/image2.mat");
imwrite(image2,"./data/image2.jpg");    %如果image2.jpg已经存在,可以删除后重新运行

数据处理入门

cftool工具

  • 进入工具箱界面:MATLAB选项栏 —— APP —— Curve Fitting(或直接在命令行输入 cftool)
  • 加载数据:在一个打开的 .m 文件中导入数据,例如:
clear;
x = [447.1, 471.3, 492.2, 501.6, 587.6, 667.8, 706.6];
y = [1.6694, 1.6638, 1.6604, 1.6577, 1.6477, 1.6411, 1.6389].^2;
  • 选择拟合曲线类型(在此选择线性拟合,基函数为 $1, x, x^2$)

image-20220223213053229

  • 查看拟合结果(Result栏)
  • 导出拟合结果:cftool界面选项栏 —— 文件(F) —— Generate Code 即可导出代码文件。

直方图

clear;
data = readtable("./data/data4.csv");
data = data.Close;

subplot(2,2,1);
histogram(data)

subplot(2,2,2);
%用名称-值对组参数设置直方图的各种属性,如bin的颜色、透明度、归一化方式等
%在此将'Normalization'设置为'probability'进行归一化,使得各bin的高度为概率值,各bin高度之和为1
%更详细的说明请参阅说明文档页面"histogram"
histogram(data,10,'Normalization','probability')     %指定标量nbins,确定直方图的bin数

edges = 15:2:38;
subplot(2,2,3);
%在此将'FaceColor'设置为[0.9,0.5,0.2]
histogram(data,edges,'FaceColor',[0.9,0.5,0.2])  %指定向量edges,确定直方图bin的边界(左闭右开)

subplot(2,2,4);

%在此将'Normalization'设置为'pdf'进行归一化,使得bin的高度为概率密度的估计值,使得各bin面积和为1
histogram(data,10,'Normalization','pdf')    %指定标量nbins,确定直方图的bin数

可视化与优化问题

图形对象

图形对象是用来创建可视化数据的组件。每个对象在图形显示中都具有特定角色。例如,一个线图包含一个图窗对象、一个坐标区对象和一个图形线条对象。可以通过设置它们的属性来自定义图形对象。 要设置属性,可以通过创建该对象的函数将其以输出参数的形式返回。例如,plot 函数返回图形线条对象。然后,使用圆点表示法查看和设置属性,或者使用 get()set() 函数查看和设置属性。

Line

x = 0:0.1:10;
y = x.^2;
p = plot(x, y);
get(p)

% 可使用 get() 函数查询特定属性
get(p, 'LineStyle')

% 可用原点表示法修改设置属性
p.LineWidth = 3

% 也可用 set() 函数设置属性
set(p, 'LineWidth', 4)

% 还可以在创建对象时使用名称-值对组参数设置属性。例如
plot(x, y, 'LineWidth', 20);

Axes

Axes 属性控制 Axes 对象的外观和行为。通过更改属性值,您可以修改坐标区的特定方面。使用圆点表示法查询和设置属性。

ax = gca % get current axes
ax.Color = 'yellow';
ax.XLim = [1 9];     % 修改坐标轴范围

% 可以更改 Axes 属性给绘图增加标签
ax.Title.String = 'My Title';
ax.Title.FontSize = 20;
ax.XLabel.String = 'My x-Axis Label';
ax.YLabel.String = 'My y-Axis Label';

% 也可以直接使用函数添加标签
title('My Title', 'FontSize', 20);
xlabel('My x-Axis Label');
ylabel('My y-Axis Label');

矩阵可视化

meshgrid

meshgrid 函数可用于创建二维表格。

x = -2:0.1:2;
y = x;
[X,Y] = meshgrid(x);
F = X.*exp(-X.^2-Y.^2);
surf(X,Y,F)

contour

使用 contour 函数绘制等高线。

contour(X, Y, F);

streamline

使用 streamline 函数绘制流线。

[x,y] = meshgrid(0:0.1:1,0:0.1:1);
u = x;    % x方向速度
v = -y;   % y方向速度
startx = 0.1:0.1:1;  % x起始点
starty = ones(size(startx));  % y起始点
figure;
streamline(x,y,u,v,startx,starty);

最优化问题

安装 Optimization Toolbox,在实时脚本中,选择插入——任务——优化或者实时编辑器——任务——优化

下面给出一个求解有约束非线性问题的示例:

问题为在单位圆盘上最小化 Rosenbrock 函数 f(x,y)=100(y−x^2)^2+(1−x)^2,约束条件为 x^2+y^2<=1

首先执行下面一行代码把初值写入工作区。

x0 = [0;0]; % 迭代初始点(x, y) = (0, 0)

下面使用求解器求解该问题,结果放入 solution 和 objectiveValue 变量中。对于不同的问题,可参照 objectiveFcn 和 unitdisk 函数自行修改。

function f = objectiveFcn(optimInput)
    % 示例:
    % 最小化 Rosenbrock 函数
    % f = 100*(y - x^2)^2 + (1 - x)^2

    % 编辑以下行以添加您的计算方法
    x = optimInput(1);
    y = optimInput(2);
    f = 100*(y - x^2)^2 + (1 - x)^2;
end

function [c,ceq] = unitdisk(x)
    % 示例:
    % 单位圆盘

    % 编辑以下行以添加您的计算方法
    % 注意,如果没有不等式约束,请指定 c = []
    % 注意,如果没有等式约束,请指定 ceq = []
    c = x(1)^2 + x(2)^2 - 1;
    ceq = [ ];
end
% 使用 disp函数查看运行结果
disp(solution)
disp(objectiveValue)

更多应用

求解黄金分割比

% 解方程
p = [1 -1 -1]; % 降次排列
r = roots(p)

% 解析字符串后解方程
r = solve('x-1=1/x')
phi = r(1)

% 符号变量解方程
syms x;
r = solve(x-1 == 1/x)
phi = r(2)

% 数值求解非线性方程
f = @(x) x-1-1./x
ezplot(f, [0, 4])
phi = fzero(f, 1)
hold on
plot(phi, 0, 'o')

% .m 的两种功能:脚本或函数
% In goldrect.m
plot(x,y,'b',u,v,'b--')
text(phi/2, 1.05, '\phi')
…
axis equal
axis off
set(gcf, 'color', 'white') % get current figure

Fibonacci

f = zeros(n, 1)
tic, fibnum(35),  toc % tic toc 计时命令

Reference

  • 无系 2021-2022 冬 MatPyFly 培训的 MATLAB 部分讲义
  • 《数值分析》MATLAB 简介及入门.pptx