(Matrices and Magic Squares)
在 MATLAB 环境中,矩阵是一个由许多数字组成的矩形数组。1乘1矩阵是特殊的矩阵,它是标量。而只有一行或一列的矩阵,它是向量。MATLAB 还有其他存储数值和非数值数据的方法,但刚开始接触软件时,最好将所有东西都看作一个矩阵。我们把 MATLAB 中的操作设计得尽可能符合你的习惯。当其他编程语言一次只能处理一个数字时,MATLAB 帮助您快速轻松地处理整个矩阵。这本书中使用一个很好的例子矩阵,它是出于德国艺术家和业余数学家 Albrecht Durer之手的文艺复兴时期 Melencolia I 这幅画中。
这幅图是用数学符号表示的,如果你仔细看,你会在右上角看到一个矩阵。这个矩阵被称为方阵,在 Dürer 的时代,很多人认为它具有真正的魔法属性。它确实有一些迷人的特征值得探索。
快速入门 MATLAB 的最好方法是学习处理矩阵,并学习每个例子。
你可以用几种不同的方法在MATLAB中输入矩阵:
首先输入 Dürer 矩阵的元素列表。你只需要遵循一些基本的惯例:
要输入 Dürer 矩阵,只需在命令窗口中键入以下一串代码即可:
A = [16 3 2 13; 5 10 11 8; 9 6 7 12; 4 15 14 1]
MATLAB 显示您刚刚输入的矩阵:
A =
16 3 2 13
5 10 11 8
9 6 7 12
4 15 14 1
这个矩阵与 Dürer 矩阵的数字相匹配。一旦您输入了矩阵,它就会自动地被存储在 MATLAB 工作空间中。你可以简单地标记它为 A。
现在您已经在工作区中有了一个 A,接下来看看是什么让它如此有趣,为什么是方阵(magic)?
(sum, transpose 和 diag)
您可能已经意识到方阵与求和元素的各种方法有关的特殊性质。如果沿着任意行或列、或沿着两个对角线中的任意一个方向求和,总是会得到相同的数。
让我们用 MATLAB 来验证一下。要尝试的第一个语句是:
sum(A)
MATLAB 返回结果:
ans =
34 34 34 34
当不指定输出变量时,MATLAB 默认使用变量 ans (answer 的缩写)存储计算结果。您已经计算了一个行向量,其中包含 a 的列的和。每一列都有相同的和,即方阵的和:34。
如果对行求和呢? MATLAB 更偏好处理矩阵的列,所以对行求和一种方法是转置矩阵,对计算转置矩阵的列求,然后再转置结果得到答案。
MATLAB 有两个转置运算符号:
“撇号”运算符(例如,A’ )执行复共轭换位。它使一个矩阵围绕主对角线翻转,并且改变了矩阵中任意一个复杂元素的虚分量的符号。
点撇号运算符(例如 A.’ )在不影响复杂元素符号的情况下进行转置。对于包含所有实数元素的矩阵,这两个操作符返回相同的结果。
因此当输入:
A'
结果是:
ans =
16 5 9 4
3 10 6 15
2 11 7 14
13 8 12 1
当输入:
sum(A')'
结果是只有一行的列向量:
ans =
34
34
34
34
要想避免两次转置的行来求和的另一种方法是使用 sum 函数的多维参数方法:
sum(A,2)
结果是:
ans =
34
34
34
34
主对角线上元素的求和方法由 sum 和 diag 函数组合使用得到的:
diag(A)
结果是:
ans =
16
10
7
1
然后:
sum(diag(A))
结果是:
ans =
34
您已经验证了 Dürer 版画中的矩阵确实是一个方阵(a magic square),并且在此过程中,已经练习了一些 MATLAB 的矩阵运算。下面的部分将继续使用这个矩阵来说明 MATLAB 的其他功能。
MATLAB 实际上有一个内置函数,可以创建几乎任何大小的方阵,因此,这个函数被命名为 magic :
B = magic(4)
B =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
这个矩阵几乎和 Dürer 版画上的一样,而且都有“ magic ”(方阵)的性质;唯一的区别是中间的两列交换了位置。
你可以把 B 方阵的中间两列互换位置,变成 Dürer A 方阵。方法是:把 B 方阵的每一行按 1、3、2、4 的顺序重新排列列:
A = B(:,[1 3 2 4])
A =
16 3 2 13
5 10 11 8
9 6 7 12
4 15 14 1
MATLAB 软件提供了四个生成基本矩阵的函数。
zeros 所有元素都为 0 ;
ones 所有元素都为 1 ;
rand 均匀分布随机元素;
randn 正态分布随机元素。
下面举一些例子:
Z = zeros(2,4)
Z =
0 0 0 0
0 0 0 0
F = 5*ones(3,3)
F =
5 5 5
5 5 5
5 5 5
N = fix(10*rand(1,10))
N =
9 2 6 4 8 7 4 0 8 4
R = randn(4,4)
R =
0.6353 0.0860 -0.3210 -1.2316
-0.6014 -2.0046 1.2366 1.0556
0.5512 -0.4931 -0.6313 -0.1132
-1.0998 0.4620 -2.3252 0.3792
与大多数其他编程语言一样,MATLAB 语言提供数学“表达式”,但与大多数编程语言不同的是,这些表达式是对整个矩阵进行运算的。
MATLAB 不需要任何类型声明或指明多少维度语句。当 MATLAB 遇到一个新的变量名时,它会自动创建该变量并分配适当的存储空间。如果变量已经存在,MATLAB将更改其内容,并在必要时分配新的存储空间。例如:
num_students = 25
这句表达式的作用是创建一个名为 num_students 的 1×1 矩阵,并将值 25 存储在其单个元素中。要查看分配给任何变量的矩阵,只需输入变量名即可。
变量名由字母加任意个数的字母、数字或下划线构成,区分大小写。例如:A 和 a 不是同一个变量。
虽然变量名可以是任意长度,MATLAB 只使用名称的前 N 个字符(其中 N 是函数 namelengthmax 返回的数字),而忽略了其余的字符。因此,每个变量名的前 N 个字符构成的名称必须是是唯一的,以便 MATLAB 能够区分变量。
N = namelengthmax
N =
63
MATLAB 使用十进制记数法,数字可以是小数,也可以是负数。科学计数法表示方法是使用字母 e 接数字来表示10的多少次方。虚数可以用 i 或 j 作为后缀。下面一些有效的数字例子:
3 -99 0.0001
9.6397238 1.60210e-20 6.02252e23
1i -3.14159j 3e5i
MATLAB 使用 IEEE® 指定的浮点标准,即使用 long (长数据)格式存储所有数字。浮点数的最大精度约为 16 位十进制数字,数据范围约为 10-308 到 10+308。
以 double (双精度)格式表示的数字的最大精度为 52 位(bit)。任何超过 52 位的双精度浮点数都会丢失一些精度。例如,下面的代码判定了两个不相等的值相等,因为它们都被截断了:
x = 36028797018963968;
y = 36028797018963972;
x == y
ans =
1
Integers (整数)具有 8 位、16 位、32 位和 64 位精度。存储与 64 位整数相同的数字可以一直保持精度:
x = uint64(36028797018963968);
y = uint64(36028797018963972);
x == y
ans =
0
MATLAB 软件存储复数的实部和虚部。它根据不同情况以不同的方式处理实部和虚部。例如,sort 函数根据大小进行排序,并根据相位角确定大小关系:
sort([3+4i, 4+3i])
ans =
4.0000 + 3.0000i 3.0000 + 4.0000i
这是因为相位角不同:
angle(3+4i)
ans =
0.9273
angle(4+3i)
ans =
0.6435
“等号”关系运算符 == 要求实部和虚部相等。其他二进制关系运算符 > <、>= 和 <= 忽略数字的虚数部分,只考虑实数部分。
使用常见的算术运算符和优先级规则完成表达式。
+ | 加 |
---|---|
- | 减 |
* | 乘 |
/ | 除 |
\ | 左除 |
^ | 幂 |
’ | 转置共轭复数 |
( ) | 指定求值顺序 |
当不再使用线性代数的矩阵方式表示有规律的数据集合时,它们就变成了二维的数值数组。数组的算术运算是逐元素进行的,这意味着数组和矩阵的加法和减法运算是相同的,但是乘法运算是不同的。MATLAB 使用一个点或小数点作为数组的乘法运算符。
具体操作符如下:
+ | 加 |
---|---|
- | 减 |
.* | 逐元素相乘 |
./ | 逐元素相除 |
.\ | 逐元素左除 |
.^ | 逐元素幂 |
.' | 非结合的数组转置 |
如果 Dürer 方阵与数组乘法相乘
A.*A
结果是一个数组,包含从 1 到 16 的整数的平方,但以不一样的方式排列:
ans =
256 9 4 169
25 100 121 64
81 36 49 144
16 225 196 1
数组操作对于构建表格非常有用。假设n是列向量
n = (0:9)';
然后,
pows = [n n.^2 2.^n]
构建一个由2的平方和幂组成的表:
pows =
0 0 1
1 1 2
2 4 4
3 9 8
4 16 16
5 25 32
6 36 64
7 49 128
8 64 256
9 81 512
初等数学函数逐元素对数组进行运算。因此
format short g
x = (1:0.1:2)';
logs = [x log10(x)]
builds a table of logarithms.
logs =
1.0 0
1.1 0.04139
1.2 0.07918
1.3 0.11394
1.4 0.14613
1.5 0.17609
1.6 0.20412
1.7 0.23045
1.8 0.25527
1.9 0.27875
2.0 0.30103
MATLAB 提供了大量的标准初等数学函数,包括 abs、sqrt、exp和sin。对负数开平方根或取对数不会出现错误,MATLAB 会显示其复数结果。
MATLAB 还提供了许多更高级的数学函数,包括贝塞尔函数和伽玛函数。这些函数中的大多数都接受复数参数。基本数学函数列表,键入
help elfun
对于更高级的数学和矩阵函数列表,键入
help specfun
help elmat
有些函数是内置的,比如 sqrt 和 sin ,它们是 MATLAB 内核的一部分,所以它们运行非常高效,但计算细节不容易获得。其他函数是用 MATLAB 编程语言实现的,因此可以访问它们的计算细节。
内置函数和其他函数之间有一些区别。例如,对于内置函数,您不能看到它们的代码。对于其他函数,您不仅可以查看代码,甚至可以修改它。
以下是几个特殊函数,它们提供常用的常数的值。
pi | 3.14159265… |
---|---|
i | 虚数单位, −1 |
j | 与 i 相同 |
eps | 浮点相对精度, ε = 2−52 |
realmin | 最小的浮点数, 2−1022 |
realmax | 最大的浮点数, (2 − ε)21023 |
Inf | 无穷大 |
NaN | 无定义 |
Infinity 是通过将一个非零值除以零,或者计算经过专门设计的数学表达式溢出(overflow,即超过 realmax)时生成的。Not -a-number 是通过尝试计算像 0/0 或 Inf-Inf 这样的表达式来生成的,这些表达式没有定义数学值。
MATLAB 并未保留函数名。我们可以用一个新值覆盖它们,比如
eps = 1.e-6
然后在后面的计算中使用这个值。原来的功能可以用以下表达式恢复:
clear eps
在前面的讲解中您已经看到了 MATLAB 表达式的几个示例。下面是其他一些例子以及它们的运算的结果:
rho = (1+sqrt(5))/2
rho =
1.6180
a = abs(3+4i)
a =
5
z = sqrt(besselk(4/3,rho-i))
z =
0.3730+ 0.3214i
huge = exp(log(realmax))
huge =
1.7977e+308
toobig = pi*huge
toobig =
Inf
format 函数控制数据的显示格式。该函数只影响数字的显示方式,而不影响 MATLAB 软件计算或保存数字的方式。下面是不同的输出格式,以及由带有不同数量级值的矢量 x 产生的输出结果。
注意,为了确保适当的间距,请使用 fixed-width 字体,例如 Courier。
x = [4/3 1.2345e-6]
format short
1.3333 0.0000
format short e
1.3333e+000 1.2345e-006
format short g
1.3333 1.2345e-006
format long
1.33333333333333 0.00000123450000
format long e
1.333333333333333e+000 1.234500000000000e-006
format long g
1.33333333333333 1.2345e-006
format bank
1.33 0.00
format rat
4/3 1/810045
format hex
3ff5555555555555 3eb4b6231abfd271
如果矩阵的最大元素大于103或小于10-3,MATLAB 对短格式和长格式使用一个通用的比例因子。
除了上面显示的 format 函数之外,format compact 还会减少输出结果中出现的许多空白行。这便于您在显示屏幕或窗口上查看更多信息。如果希望对输出格式有更多的控制,请使用 sprintf 和 fprintf 函数。
如果您只是简单地输入一个表达式并按 Return 或 Enter 键, MATLAB 会自动在屏幕上显示结果。但是,如果以分号结束行,MATLAB 将执行计算,但不显示任何输出。这在生成大型矩阵时特别好用。例如:
A = magic(100);
如果表达式不在一行上,使用省略号(三个点),…,然后返回或回车,以指示语句在下一行。例如:
s = 1 -1/2 + 1/3 -1/4 + 1/5 - 1/6 + 1/7 ...
- 1/8 + 1/9 - 1/10 + 1/11 - 1/12;
= 、+ 和 - 号周围的空格是可有可无的,但是它们让程序整齐、便于阅读。
键盘上的各种箭头和控制键帮助您回忆、编辑和重新使用前面输入的语句。例如,假设您输入发生了错误:
rho = (1 + sqt(5))/2
你把 sqrt 拼写错了,MATLAB 会说明错误:输入参数类型为 “double” 的函数 “sqt” 是未定义的。
不需要再重新键入整行,只需要按 ↑ 键即可重新显示您键入的语句。使用 ← 键将光标移到上面并插入缺失的字母 r 。重复使用 ↑ 键会继续显示更早输入的表达式。输入几个字符,然后按下 ↑ 键,可以找到那些以这些字符开头的表达式。还可以从命令历史记录中复制以前执行的语句。
的第 i 行和第 j 列中的元素用 A(i,j) 表示。例如,A(4,2) 是第 4 行和第 2 列中的数字。对于方阵,A(4,2) 等于 15。要计算 A 的第四列元素的和,输入:
A(1,4) + A(2,4) + A(3,4) + A(4,4)
这个下标结果:
ans =
34
但这并不是对单个列求和便捷的方式。
也可以用一个下标 A(k) 表示矩阵的元素。单下标是引用行和列向量的常用方法。然而,它也可以应用于一个完全二维的矩阵,在这种情况下,原矩阵被看成由其列组成的一个长列向量。对于方阵,A(8) 是指向存储在 A(4,2) 中的值 15 的另一种方式。
如果你试图使用矩阵外的一个元素的值,MATLAB 会提示错误:
t = A(4,5)
Index exceeds matrix dimensions.
相反地,如果你将一个值存储在矩阵外的一个元素中,那么这个矩阵的大小就会增加,以便于填充新的元素:
X = A;
X(4,5) = 17
X =
16 3 2 13 0
5 10 11 8 0
9 6 7 12 0
4 15 14 1 17
英文冒号 : 是 MATLAB 中最重要的运算符之一,它以几种不同的形式出现。下面一个表达式:
1:10
是一个包含整数从 1 到 10 的行向量:
1 2 3 4 5 6 7 8 9 10
如果要使用其他间距(默认是 1),请指定增量。例如:
100:-7:50
即
100 93 86 79 72 65 58 51
还有,
0:pi/4:pi
即
0 0.7854 1.5708 2.3562 3.1416
包含冒号的下标指的是矩阵的一部分:
A(1:k,j)
这个表达式指的是 A 矩阵的第 j 列的前 k个元素。因此,
sum(A(1:4,4))
计算第四列的和。然而我们有一种更好的方法来执行这个计算。冒号本身指矩阵行或列中的所有元素,关键词 end 指最后一行或最后一列。因此,
sum(A(:,end))
计算 A 矩阵最后一列元素的和:
ans =
34
为什么 4×4 的方阵和等于34? 如果整数从 1 到 16 被分成4组,并且它们的和相等,那么它们的和必须是:
sum(1:16)/4
它的结果当然是:
ans =
34
联结运算符是一个将小矩阵连接成大矩阵的过程。实际上,你通过把它的各个元素连接起来得到了你的第一个矩阵。方括号 [ ] 是连接操作符。例如,从 4×4 的方阵、A 和表单开始
B = [A A+32; A+48 A+16]
结果得到一个 8×8 矩阵,由四个子矩阵连接得到:
B =
16 3 2 13 48 35 34 45
5 10 11 8 37 42 43 40
9 6 7 12 41 38 39 44
4 15 14 1 36 47 46 33
64 51 50 61 32 19 18 29
53 58 59 56 21 26 27 24
57 54 55 60 25 22 23 28
52 63 62 49 20 31 30 17
这个矩阵可以继续联结为更大的矩阵。它的元素是在整数范围 1:64 重新排列得到的。对它列求和的确是一个 8×8 方阵的值:
sum(B)
ans =
260 260 260 260 260 260 260 260
但对它的行求和 sum(B’) 并不是统一的值。这就需要进一步的处理,使这成为一个有效的 8×8 方阵。
您可以使用一对方括号从矩阵中删除行和列。例如,
X = A;
然后,要删除 X 的第二列,表达式如下,
X(:,2) = []
把矩阵 X 变成
X =
16 2 13
5 11 8
9 7 12
4 14 1
如果从矩阵中删除某一个元素,结果就不再是矩阵了。因此,表达式如
X(1,2) = []
这就会导致错误。然而,使用某个下标删除对应的个元素或元素序列,并将其余元素重新组合成行向量,这样的变换是可以的。所以,
X(2:2:10) = []
结果则为
X =
16 9 2 7 13 12 1
矩阵和标量可以用几种不同的方式结合。例如,从矩阵中减去一个标量是通过从矩阵的每个元素中减去它。方阵中元素的平均值是 8.5 ,因此
B = A - 8.5
结果是一个列的和为零的矩阵:
B =
7.5 -5.5 -6.5 4.5
-3.5 1.5 2.5 -0.5
0.5 -2.5 -1.5 3.5
-4.5 6.5 5.5 -7.5
sum(B)
ans =
0 0 0 0
通过扩展标量,MATLAB 为矩阵内的所有元素分配一个指定的标量。例如,
B(1:2,2:3) = 0
把 B 的一部分归零:
B =
7.5 0 0 4.5
-3.5 0 0 -0.5
0.5 -2.5 -1.5 3.5
-4.5 6.5 5.5 -7.5
由逻辑和关系操作创建的逻辑向量可用于引用子数组。假设 X 是一个普通矩阵而 L 是一个相同大小的矩阵这是一些逻辑运算的结果。然后 X(L) 指定 X 的元素其中 L 的元素是非零的。
这种下标可以通过将逻辑操作指定为下标表达式一步完成。假设您有以下数据集:
x = [2.1 1.7 1.6 1.5 NaN 1.9 1.8 1.5 5.1 1.8 1.4 2.2 1.6 1.8];
NaN 标记表示的是缺省,例如未能对问卷上的一个项目作出响应。使用 isfinite(x) 来删除逻辑索引中缺失的数据,它适用于所有有限数值,而不适用于 NaN 和 Inf :
x = x(isfinite(x))
x =
2.1 1.7 1.6 1.5 1.9 1.8 1.5 5.1 1.8 1.4 2.2 1.6 1.8
这里有一个观察值:5.1,与其他数字相比看起来很不一样。这是一个异常值。下面的语句删除偏离的元素,这些元素与均值相比偏离了超过三个标准差:
x = x(abs(x-mean(x)) <= 3*std(x))
x =
2.1 1.7 1.6 1.5 1.9 1.8 1.5 1.8 1.4 2.2 1.6 1.8
在上文中有一个例子,通过使用逻辑下标和标量扩展将非素数设置为0,突出显示出质数在 Dürer 方阵中的位置。
A(~isprime(A)) = 0
A =
0 3 2 13
5 0 11 0
0 0 7 0
0 0 0 0
find 函数确定满足给定逻辑条件的数组元素的索引。在其最简单的形式中,find 返回一个索引的列向量。转置这个向量得到一个列向量的指标。例如,再以 Dürer 方阵为例。
k = find(isprime(A))'
利用一维索引,在方阵中找出质数的位置:
k =
2 5 9 10 11 13
将这些质数整理表示为行向量,其顺序由 k 决定,
A(k)
ans =
5 3 2 11 7 13
当你在赋值语句中使用 k 作为左边索引时,并且会保留矩阵结构:
A(k) = NaN
A =
16 NaN NaN NaN
NaN 10 NaN 8
9 6 NaN 12
4 15 14 1
MATLAB 环境中的多维数组是具有两个以上下标的数组。创建多维数组的一种方法是调用包含两个以上参数的 0、1、rand 或 randn 函数。例如,
R = randn(3,4,5);
创建一个3×4×5的数组,其中包含 345 = 60 个正态分布随机元素。
三维数组可能表示三维物理数据,例如在矩形网格上采样的房间温度。或者它可以表示一个矩阵序列,A(k) 或者一个时间相关矩阵的样本, A(t) 在后一种情况下,第 k 个矩阵 (i,j) 的第 (i,j) 个元素,或者说第 tk 个矩阵,用 A(i,j,k) 表示。
MATLAB 和 Dürer 版本的 4 阶方阵的不同之处在于两个列的互换。通过交换列可以生成许多不同的方阵。
下面表达式:
p = perms(1:4);
生成 4!= 24 个排列,其元素范围取1:4。第 k 个排列是行向量 p(k,? 。然后,
A = magic(4);
M = zeros(4,4,24);
for k = 1:24
M(:,:,k) = A(:,p(k,:));
end
将 24 个方阵的序列存储在三维数组中 M 中, M 的大小为
size(M)
ans =
4 4 24
注意,图中所示矩阵的顺序可能与结果不同。 perms 函数总是返回输入向量的所有排列,但是对于不同的 MATLAB 版本,排列的顺序可能不同。
表达式:
sum(M,d)
通过改变第 d 个下标来计算求。所以
sum(M,1)
是一个包含具有 24 个行向量副本的 1×4×24 的数组
34 34 34 34
然后,
sum(M,2)
是一个包含具有 24 个列向量副本的 1×4×24 的数组
34
34
34
34
最后,
S = sum(M,3)
将序列中的 24 个矩阵相加。结果是一个大小为 4×4×1 的矩阵,所以看起来像一个 4×4 的矩阵:
S =
204 204 204 204
204 204 204 204
204 204 204 204
204 204 204 204
MATLAB 中的单元数组是多维数组,其元素是从其他数组中复制而来。我们可以使用函数:cell 创建空矩阵的单元格数组。但是,更常用的是将单元格数组的杂项集合放在花括号{}中。花括号还与下标一起用于访问各种单元格的内容。例如,
C = {A sum(A) prod(prod(A))}