在上一节介绍关系运算时,我们提到过:MATLAB中使用逻辑值表示布尔变量,逻辑值1代表真(true),逻辑值0代表假(false)。而逻辑运算就是对逻辑值进行的运算。大部分的编程语言都会涵盖四个最基本的逻辑运算方法:逻辑与、逻辑或、逻辑非和逻辑异或。
下表给出了MATLAB中的定义(注意:下表中的1和0是逻辑值1和0):
注意,上表中函数名和对应的运算符可以执行相同的功能,除了“逻辑异或”没有相应的运算符外,剩下三个运算方法都有对应的运算符。
这四个运算方法的使用方法较为相似,以“逻辑与”为例,大家可以查看“逻辑与”的帮助文档:A & B 对数组 A 和 B 执行逻辑 AND 操作,并返回包含设置为逻辑值 1 (true) 或逻辑值 0 (false) 的元素的数组。如果 A 和 B 在相同的数组位置都包含非零元素,则返回的数组中对应位置的元素将设置为逻辑值 1 (true)。如果不是,则将数组元素设置为 0。and(A,B) 是执行 A & B 的替代方法,但很少使用。(注:该帮助文档来自于MATLAB2023之前的版本,新版本增加对表格类型数据的计算)
从帮助文档可以得知:(1)MATLAB推荐大家直接使用运算符进行计算,因此&、|和~这三个符号的功能大家要牢记,我们主要使用这三个符号而不是对应的函数进行计算。(2)“逻辑与&”是对数组A和B进行计算的,计算时会比较A和B对应位置的元素。数组A和B的大小不一定要完全相同,只需要符合算术运算中介绍的五种兼容模式即可。(3)“逻辑与&”不仅可以作用在逻辑值0和1上,还可以用于普通的数值上,这时候,MATLAB会将非零数值视为逻辑1,将数值零视为逻辑0进行运算。例如:3&5返回逻辑值1,-4&0返回逻辑值0。我们来看两个例子:
有同学可能会有疑问,为什么MATLAB会将非零数值视为逻辑1,将数值零视为逻辑0?这是因为MATLAB在进行逻辑运算之前,在计算机内部自动将数值转换成了逻辑值。我们也可以使用logical函数手动进行转换:
L = logical(A) 将 A 转换为一个逻辑值数组。A 中的任意非零元素都将转换为逻辑值 1 (true),零则转换为逻辑值 0 (false)。复数值和 NaN 不能转换为逻辑值。
举个例子,我们随机生成一个4行3列的矩阵,将矩阵中非0位置的元素转换成逻辑值1,等于0的位置的元素转换成逻辑值0:
特别地,我们还可以使用true和false函数分别创建全为逻辑1和逻辑0的逻辑矩阵。以true函数为例,它的主要用法有两种:(1)true(n)可以生成一个n行n列全为逻辑值1的方阵,特别地,当n等于1时可以简写为true,此时表示一个常量;(2)true(m,n)可以生成一个m行n列全为逻辑值1的矩阵。
除了“逻辑与&”外,剩下的三个逻辑运算函数也可以用于普通的数值上,MATLAB也会将非零数值视为逻辑1,将数值零视为逻辑0进行运算,下面我们举几个例子:
另外,我们有时候也会使用“逻辑与 &”和“逻辑或 |”进行连续运算,例如1 & 2 & 3和0 | 3 | 0的结果都是逻辑1,下面我们再看几个例子:
下面我们再次谈谈运算优先级的问题,MATLAB中的运算符有不同的计算优先级,优先级高的先进行计算,例如3+4*2等于11而不是14,这是因为乘法的优先级高于加法。类似的,关系运算符(例如>、==、~=)的优先级要高于逻辑运算符&和|,例如3 > 4 & 2 > -1的返回结果是逻辑0。大家可以去MATLAB官网查看运算符优先级的帮助文档,但没有必要刻意去记,我们只需要养成一个好的习惯:使用小括号来指定计算的先后顺序,例如我们可以将上面的代码改成(3 > 4) & (2 > -1),这样计算的先后顺序看起来会清楚很多。另外有一个特殊的优先级顺序需要大家了解, & 运算符的优先级要高于 | 运算符。尽管 MATLAB 通常按从左到右的顺序计算表达式,但表达式 a|b&c 按 a|(b&c) 形式计算,因此,对于同时包含 & 和 | 的语句,比较好的做法是使用小括号显式地指定期望的语句优先级。
我们来做一个小练习:随机生成一个具有20个元素的向量,用来表示20名同学的成绩(假设成绩为满分100分的整数制)。请通过代码计算得到一个具有20个元素的逻辑向量,向量中对应位置的元素为逻辑值1时表示该同学的成绩在区间[60,80)内,为逻辑值0时表示成绩在区间[60,80)外。
答案如下:
A = randi([0,100],1,20)
res = (A >= 60) & (A < 80)
% 一定要注意:不能写成:60 <= A < 80哦!
如果要找出[0 , 60) ∪ [80 , 100]分的呢?
答案如下(下面两种方法都可以):
方法1:(A < 60) | (A >= 80)
方法2:~((A >= 60) & (A < 80))
下面我们再来介绍MATLAB中另外两个使用频率很高的逻辑运算符:&&和||.
这两个运算符和“逻辑与&”和“逻辑或|”作用相同,但它们有两个非常重要的区别:
(1)&&和||只能对标量(只有一个元素)进行逻辑运算,不能对有多个元素的向量或者矩阵进行运算,而&和|可以。比如我们上面那个练习题,你只能使用&和|进行运算。
(2)&&和||进行逻辑运算时具有短路功能,可以提高运行效率:
Ø 计算A && B时,如果A为逻辑0,则B不会被判断,因为最后的结果一定是逻辑0;
Ø 计算A || B时,如果A为逻辑1,则B不会被判断,因为最后的结果一定是逻辑1。
举个例子:假设a等于10,b等于3,现在要计算:(a+b < 10) && (a/b > 1),那么MATLAB首先会判断前面一项:(a+b < 10),因为这一项计算的结果为逻辑0,所以后面的(a/b > 1)这一项不会被计算,MATLAB会直接返回逻辑0;如果你使用的是:(a+b < 10) & (a/b > 1),那么这两项都会被计算,这样的话效率会低一点。在下一章中,我们会介绍if判断语句和while循环语句,和&、|相比,&&和||在if和while语句中使用频率更高。
思考题:前面我们介绍过logical函数,它能将数值转换为逻辑值,但如果输入的是NaN,那么MATLAB会报错,当分别运行(10 > 3) | logical(NaN)和(10 > 3) || logical(NaN)的结果是什么?
答案如下:
运行 (10 > 3) | logical(NaN) 会报错,因为前后两项都会被计算,所以当MATLAB运行到后面的logical(NaN)时会报错;运行 (10 > 3) || logical(NaN) 会返回逻辑1,因为前面一项(10>3)返回逻辑1,此时会触发||的短路机制,这时候就会直接返回逻辑1。
前面我们学过MATLAB中的向量和矩阵,并介绍过如何通过元素所处的位置索引来引用(提取)所需的元素。例如:若a是一个向量,则a(1:2:end)表示提取a中所有奇数位置的元素;若A是一个矩阵,则A(1:2:end, :)表示提取A中所有奇数行的元素。
本小节我们介绍引用矩阵的元素的另一种方式:利用逻辑值。
假设有一个m行n列的矩阵A,我们要提取其指定位置的元素,那么我们可以生成一个和A同样大小的逻辑矩阵L,L中的元素要么是逻辑值1,要么是逻辑值0,其中:等于逻辑值1的元素所处的位置是我们所需要的。接着我们只需要使用命令A(L),就能够在A中提取出指定位置的元素。通常逻辑矩阵L是由一系列的逻辑运算或者条件运算得来的。
举个例子,我们要提取A中所有小于等于3的元素,这里的A是我随机生成的一个矩阵:
这里的L就是通过关系运算得到的一个逻辑矩阵,它的大小和A相同,L中为逻辑值1的位置就是矩阵A中要引用的位置。
注意:命令A(L)得到的结果是一个列向量,有同学会问:为什么A(L)返回的列向量中的元素顺序是3,1,2,而不是其他的顺序?这是因为MATLAB会按照线性索引的顺序来返回提取的元素,就类似于A(:)命令可以将A中的所有元素按照线性索引的顺序返回一个列向量。
以后大家熟练的话,可以直接写成A(A<=3)来提取A中所有小于等于3的元素。再举个例子,如果要提取A中大于等于5且小于8的元素,我们可以使用A( A>=5 & A < 8),千万不能写成A(5 <= A < 8),也不能使用&&运算符:A( A>=5 && A < 8),&&运算符只能用于标量的逻辑运算。
注意,这里有一个易错点:L必须是逻辑矩阵,即里面的0和1都必须是逻辑值,不能是由数字0和1构成的数值矩阵。如果L是数值矩阵,可以使用logical函数进行转换。
下面我们对引用的元素进行修改或删除。下面我们直接看例子:
从上表最后一个例子可以看出,使用逻辑索引删除矩阵中的元素后,MATLAB会将矩阵中剩下的元素按照线性索引的顺序放入到一个行向量中。
我们再来看一个有趣的问题:缺失值的识别和填补。
举个例子:假设清风老师要连续一周测量早上6点室外的温度,结果清风老师周二和周五睡过了头,那两天的温度没有测量,剩下五天的温度分别是10°、5°、2°、8°和5°,周二和周五的温度成了缺失值。现在清风老师想利用有数据的剩余五天的平均气温来代替周二和周五这两天的温度,于是他计算出这五天的平均气温为(10+5+2+8+5)/5=6°,这时候就完成了对缺失值的填补。
那么我们怎样在MATLAB中实现这个过程呢?我们可以先定义一个向量A用来保存这一周的温度:A = [10 NaN 5 2 NaN 8 5],其中第二个元素和第五个元素为NaN,代表周二和周五的温度数据是缺失的。现在需要大家将A中所有的NaN值替换成所有非缺失值的平均值。
答案只需要一行代码:A(isnan(A)) = mean(A(~isnan(A)))。这里用到了isnan函数,它可以判断数组中的元素是否为不定值NaN,并返回一个和输入的数组大小相同的逻辑数组。
例如,这里的isnan(A)返回的结果就是[0 1 0 0 1 0 0]这个逻辑向量。
有同学会想:为什么不直接用命令A==NaN来找A中的缺失值?这是因为在MATLAB中,NaN相互之间不相等,运行NaN == NaN会输出逻辑值0。
那么,如何找出A中所有非缺失值的元素呢?我们可以对isnan(A)的结果进行“逻辑非”运算,即~isnan(A),然后再利用这个逻辑向量对A进行索引:A(~isnan(A))。
下面我们介绍三个非常重要的函数,它们的作用请看下表:
其中,all函数和any函数的用法类似,以all函数为例,它的用法如下:
(1)如果A是一个向量,那么当所有元素均为非零值时,all(A)返回逻辑值1 (true),当存在一个或多个元素为零时,返回逻辑值0 (false)。
(2)如果A是一个矩阵,那么all(A,dim) 沿着dim维来判断元素是否全为非零值,dim等于1时沿着行方向来判断每一列是否全为非零值,并将结果返回为一个全为逻辑值的行向量;dim等于2时表示沿着列方向判断每一行是否全为非零值,并将结果返回为一个全为逻辑值的列向量。特别地,当dim等于1时,可以直接简写成all(A)。
(事实上,all函数和any函数的用法和我们之前讲解的sum函数非常像)
可以看出,all函数相当于对向量或者矩阵的元素进行‘逻辑与&’运算,只有全为非零值时才返回逻辑值1。而any函数则相当于对元素进行‘逻辑或|’运算,存在至少一个非零值时就会返回逻辑值1。
我们来看any函数的例子:
事实上,all函数和any函数很少直接运用在数值矩阵上,它常常配合逻辑矩阵来实现特定的功能。
(3)这三门科目中是否存在科目没有人挂科(所有同学的这一门科目的成绩都高于60分)。要求返回一个包含3个元素的逻辑向量,元素为逻辑1的位置对应的科目表示没有人挂科。
all(score >= 60)
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删