请独立完成本次作业后再看本推文

更有利于您的Matlab体验

请务必独立完成作业,或者真的是完全没有思路的情况下再来阅读本文,尊重每一位同学的劳动成果,谢谢!

另外请尽可能自行完成作业,或者参考解答思考后仍旧不懂再询问他人。在询问他人的时候请加上注释,并尽可能给变量命有意义的名字。否则一长串代码是看不懂看不清的。如果可以的话,不要发图片,直接发代码,对方也方便运行测试,谢谢!

Requirements :

All the following are NOT allowed unless SPECIFICALLY said otherwise : if statement, operators &  &&  |  || , loops, recursion, all string built in operations ( e.g. str2num, num2str, strfind, strcmp etc).

这里面的built in指的是用其他语言写成的函数。比如说,matlab里面的arrayfun就是一个built-in函数,它的源码是C语言。

这个程序要我们验证我们输入的矩阵是不是magic square。这是一个很有趣的矩阵,在matlab里我们可以通过magic这个函数来获得这样的矩阵

我最先的思路如下

%  get input from user

a = input('Please enter a non empty quadratic array : ');

% find the diagonals

main=a(1:length(a)+1:end);

flip_a=fliplr(a);

sub=flip_a(1:length(flip_a)+1:end);

% sum the columns, lines, diagonals

sum_column=sum(a);

sum_line=sum(a,2);

sum_main=sum(main);

sum_sub=sum(sub);

% see if the sum of columns/rows/diagonals are the same

column=all(~diff(sum_column));

line=all(~diff(sum_line));

column_line=all(all(sum_column==sum_line==1)==1);

main_sub=sum_main==sum_sub;

main_column=sum_main==sum_column(1);

r=column*line*column_line*main_sub*main_column;

这里面我用到了一些老师讲过但不要求掌握,或老师没有讲过的函数。Tzvi看见了,说“这些不要在作业里使用。虽然我们改作业是用机器改的,你这么写虽然在作业里没有问题。但是考试的时候我们是人工改的,你这些就不行了。”。我跟他说,“大部分没教过的函数我都知道怎么由我们已学的函数转变过来。但是我嫌这么写代码会很长,所以就没这么写了。”

为什么不用这些看起来“很高级”的函数呢?因为大多数情况下,我们都不知道它背后的运作原理和逻辑思维。

举个例子,我在requirements里提到的arrayfun,它的效果是Apply function to each element of array。但由于显示在我们眼前的单纯只是一个函数,我们并不清楚它的源码如何。实际上,arrayfun的源码使用了循环,只不过matlab已经将其源码封闭,编译成了动态链接库(dll(Dynamic Link Library))。而且它是用非解释性语言的编译性语言写成的,执行起来会比较快。

matlab这堂课就是要我们循序渐进,学会逻辑思维,哪怕没有这些高级函数,我们也一样可以解决问题。

所以我会尽可能用我们已学的函数来解释新函数,不过大部分Israel都在Lecture上讲过其背后的运作原理了。

回到我们的题目。因为这次注解比较详细,我就不再细说了。

find the diagonals那里,我想提到一个函数,eye,它可以生成main diagonal为1,其余全为零的矩阵

在同一部分,我为了找sub-main diagonal,使用了fliplr,这个函数可以使矩阵左右翻转

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

flip_a=fliplr(a)

它的运作原理是

flip_a=a(:,end:-1:1)

一个矩阵作为变量,后面打括号表示要取它其中的元素,格式为matrix(row,column)

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

b=a(2,3)  %取a矩阵的第2行,第3列的元素,赋值给b

另外,Israel说过,“Oh, Matlab, you like column. What about me? Do you like me?”

matlab里面很多运行都是以列为基准(排除1d array的情况)进行运算,比如sum,all等等。原因是matlab里面元素的index顺序在2d array是以列排列。比如一个3x3的矩阵它的index如下

所以当我们想要把2d array的变量转化为1d array的话,可以使用如下方法

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

b=a(:)   %单打一个冒号相当于打"1:end",取所有

还有如我刚刚所说,它的格式为matrix(row,column),所以有以下例子

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

其中的黄色是row所选的index,蓝色是column所选的index,最后交叉的结果就是我们所得的结果。

好的关于这部分的内容我们就看到这里,让我们再次回到题目。

这个解答的第三部分用到了一个函数,diff,它可以比较array里相邻元素的差值。它获得的是一个比你原array少一个元素的array

diff([1 1 2 5])

我们目的是为了比较我们sum_column里每个元素是否相同,即每一列的和是否相同。当diff的结果全为零的话,那每个元素就相同了。我们想到可以把0转化为1,非零数转化为0,然后用all进行判断。

“~”是“非”的意思,比如“~=”就是“不等于”的意思。我们还可以通过“~”把array里的0转化为1,非零数转化为0。获得logical数组

这样一来,“column=all(~diff(sum_column));”就解释明白了,继续看后面的

column_line=all(all(sum_column==sum_line==1)==1);

这个又该怎么理解呢?看代码我们要学会找顺序,优先看最里面的括号,再从左往右看。所以“sum_column==sum_line==1”应该理解为,先运算“sum_column==sum_line”,然后再把它获得的结果与“1”进行比较

则它的运作原理是

而并不是a,b,c,d同时进行比较的意思

我的解答方法解释完了,我有去问Tzvi这道题怎么写,我觉得我的太复杂了。他看了我的代码后,除了跟我说我在开头打的那段字之外,还给出了一种办法,主要是优化第三部分的比较。第三部分改为

% see if the sum of columns/rows/diagonals are the same

r=all( [sum_column sum_line' sum_main] == sum_sub );

其中的sum_line因为是nx1的矩阵,我想要把它放进一个横着的矩阵里,所以用“ ' ”对其进行了transpose。

他的这个方法非常的妙,取一个元素出来,其他的放进一个矩阵里。用短短一行就可以一次性地对所有元素进行比较,然后用all判断是否每次比较全都符合条件。是则输出1,否则输出0

% --method 1--

%  get input from user

a = input('Please enter a non empty quadratic array : ');

% logical of diagonal

log=ones(length(a));

above_main=triu(log,1);

below_main=tril(log,-1);

above_sub=fliplr(above_main);

below_sub=fliplr(below_main);

Q1=a(logical(above_main.*above_sub));

Q2=a(logical(above_main.*below_sub));

Q3=a(logical(below_main.*below_sub));

Q4=a(logical(below_main.*above_sub));

% sum the elements in Qi

sum_Q1=sum(Q1);

sum_Q2=sum(Q2);

sum_Q3=sum(Q3);

sum_Q4=sum(Q4);

% compare the sum of Qi

Q1_Q2=sum_Q1==sum_Q2;

Q2_Q3=sum_Q2==sum_Q3;

Q3_Q4=sum_Q3==sum_Q4;

r=Q1_Q2*Q2_Q3*Q3_Q4;

ones(num),zeros(num)可以分别创造边长为num的全一矩阵和全零矩阵

triu和tril的作用分别为创造Upper triangular part of matrix,Lower triangular part of matrix。格式为triu(matrix); tril(matrix)。它还可以取部分,格式为triu(matrix,num); tril(matrix,num)。num为右下角部分向右上角扩张的斜行数。

d=triu(a,1)

e=tril(a,2)

他们的原理,就单举triu(num,1)为例子

a=repmat([1:5],5,1)

b=repmat([1:5]',1,5)

d=triu(ones(5),1)

Qi部分记得把double转化为logical,否则会报错。因为变量取部分只能为正整数或logical数组。0会报错

后来和卓哥讨论到这道题的时候,他提到了旋转矩阵的思维

% --method 2--

%  get input from user

a = input('Please enter a non empty quadratic array : ');

% find the logical of Qi

log=ones(length(a));

log_Q1=logical(triu(log,1).*fliplr(triu(log,1)));

log_Q2=rot90(log_Q1,-1);

log_Q3=rot90(log_Q1,2);

log_Q4=rot90(log_Q1,1);

% sum the elements in Qi

Q1=sum(a(log_Q1));

Q2=sum(a(log_Q2));

Q3=sum(a(log_Q3));

Q4=sum(a(log_Q4));

% compare the sum of Qi

r=all([Q1 Q2 Q3]==Q4);

其他部分的理解前面已经提过了。现在着重看向rot90,这个函数可以把矩阵逆时针旋转k*90度(rotate 90 degree counter clockwise)。格式为rot90(matrix,k)

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

b=rot90(a,1)

c=rot90(a,-1)

它的原理是

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

c=b(end:-1:1,:)

这道题我自己想出了两种方法。

因为这是两个矩阵,要他们mix up,最好是先把它们合并,方便使用index对他们的行进行排序,这是第一种方法(见method 1)。另一种方法是把两个矩阵扩展两倍,然后a1偶数行变为0,a2奇数行变为0,然后把两个矩阵合并(见method 2)

% --method 1--

%  get input from user

a1 = input('Please enter the array a1 : ');

a2 = input('Please enter the array a2 : ');

% combine a1 & a2

combine=cat(1,a1,a2);

% find the order

order=repelem(1:max(size(a1,1),size(a2,1)),1,2)+repmat([0 size(a1,1)],1,max(size(a1,1),size(a2,1)));

% let combination of a1 & a2 be in the order we want

r=combine(order,:);

其中有几个新函数,repelem其实就是hw2的question5,它的原理请看hw2评论区的ceil/floor。另一个是cat,它的效果是Concatenate arrays along specified dimension。格式为cat(dim,A1,A2,A3...)

% --method 2--

%  get input from user

a1 = input('Please enter the array a1 : ');

a2 = input('Please enter the array a2 : ');

% expand a1 & a2

A1=repelem(a1,2,1);

A2=repelem(a2,2,1);

% find the suitable logical for a1 & a2

log_a1=repmat([1;0],size(a1,1),1);

log_a2=repmat([0;1],size(a2,1),1);

% log_a1(size(a1,1)+1:size(a2,1),:)=zeros(size(a2,1)-size(a1,1),size(a1,2));

% log_a2(size(a2,1)+1:size(a1,1),:)=zeros(size(a1,1)-size(a2,1),size(a2,2));

% combine a1 & a2

%the elements of column times the elements of column

r=A1.*log_a1+A2.*log_a2;

因为这两个method都只能解决等大矩阵,所以我还在method 2里为短的矩阵补行(看注解log行),但是因为维度不同无法赋值,具体解决办法我还没找到。后来我问Tzvi得到了hint,写出了method 3

% --method 3--

%  get input from user

a1 = input('Please enter the array a1 : ');

a2 = input('Please enter the array a2 : ');

% form a matrix and wait a1 & a2 to fill in

r=zeros(size(a1,1)+size(a2,1),size(a1,2));

% find minimum rows of a1 & a2

minimum=min(size(a1,1),size(a2,1));

% fill a1 & a2 into the matrix

r(1:2:2*minimum,:)=a1(1:minimum,:);

r(2:2:2*minimum,:)=a2(1:minimum,:);

r(2*minimum+1:end,:)=[a1(minimum+1:end,:) a2(minimum+1:end,:)];

老实说,这道题我很快就写出method 1。道理和hw2的Question 3的method 3一模一样,所以我这里就不解释了

% --method 1--

%  get input from user

a = input('Please enter the array : ');

% sort the array

sort_a=sort(a);

% find the position of the elements

position=sort_a==a';

% find the order of the elements

[i,j]=find(position==1);

还是解释一个吧,在2d array里面,find是找它以列为基准的index。道理我都在Question 1里说了。所以前面变量你也可以写成[i,j]的形式,来找到它的[row,column]的index。注意了,这里matlab找的顺序依旧是以列为基础,只不过变为赋值给两个变量i,j

claim: In [i,j], i always is sorted. However, j is not necessary sorted.

然后我又又又去问Tzvi,他跟我提到了,其实sort可以解决这个问题,但是题目希望你用原矩阵和sort矩阵进行比较(就是考试的时候不能使用这种方法啦)

% --method 2--

%  get input from user

a = input('Please enter the array : ');

% sort a and get sort(a) & its elements' index after sort

[sort_a,index]=sort(a);

还有一个函数叫做sortrows,它能把2d array按行排序。但是它其实还是一列列比较,小的在上,大的放在下面,然后再看下一列,以此类推。有兴趣的同学可以试试这个函数。它的道理其实和sort很像

然后他还悄悄提到了他自己做的时候用到了1d array的方法,但是其实是一种trick。他把他的这种给Israel看,Israel跟他说这样可以实现,但是不能这么写。在我的请求下,他把这种方法分享给了我

% --method 3--

%  get input from user

a = input('Please enter the array : ');

% find the length of a base on log10

len=ceil(log10(length(a)));

% attach index to the elements in a

index_a=a.*10^len+(1:length(a));

% sort the elements with index in a

sort_a=sort(index_a);

% get the result we want

r=mod(sort_a,10^len);

但是这种方法在元素个数为10^n (n>0)的情况下,最后一个元素在附加上index时候回出错。所以可以在index_a那一行最后加一个1来解决这个问题

他跟我讲完立马看向了讲台下面,下面的同学都在认真地看向自己的电脑。然后他咧了咧嘴,把白板上刚刚写的东西擦掉了,“no one see it”。我不擅长言语,看了他的这么神奇的方法只会重复说一句,“Magical!”

% --method 1--

%  get input from user

s1 = input('Please enter a string s1 : ','s');

s2 = input('Please enter a string s2 : ','s');

% form an array in order to separate s1 into parts

index_s1=repmat(1:length(s2),length(s1)-length(s2)+1,1)+repmat((0:length(s1)-length(s2))',1,length(s2));

part_s1=s1(index_s1);

% compare part_s1 and part_s2

r=(find(sum(part_s1==s2,2)==length(s2)))';

Tzvi说他的方法跟我很像,就是index_s1获得的方法不一样。我是直接获得,而他是获得一个Israel上个讲过的square后进行剪切

% --method 2--

%  get input from user

s1 = input('Please enter a string s1 : ','s');

s2 = input('Please enter a string s2 : ','s');

% form an array in order to separate s1 into parts

a=repmat(1:length(s1)-length(s2)+1,length(s1)-length(s2)+1,1);

index_s1=a+(a-1)';

index_s1(:,length(s2)+1:end)=[];

part_s1=s1(index_s1);

% compare part_s1 and part_s2

r=(find([sum(part_s1==s2,2);0]==length(s2)))';

method 2的r的那一行发现了没有,相比method 1将sum过行和0组成一个新的矩阵

这只是为了解决当s1和s2长度一样时候生成0x0的empty array的问题(题目要求生成1x0的empty array)

Tzvi说生成什么empty array对我们人类来说都不是问题。它对于matlab只是为了合并矩阵的时候不会出维度问题。比如说一个0xn的empty array才可以合并到mxn的矩阵里。所以Question 3的0x0 empty array和Question 5的1x0 empty array问题他会和Israel进行讨论

a=repmat(1:6,3,1)

以上就是我对matlab本次作业的逻辑思考,肯定有很多的不足,大家都可以指出来,有更好的方法也提出来,相互学习学习,谢谢啦!