??之前的文章,可能過于晦澀難懂匙睹。再加上我寫的也不好愚屁,解釋的不夠清楚,導(dǎo)致最后看的人不多痕檬。這次來點簡單輕松的霎槐,爭取大家都能看懂。
??下面都是我自己個人總結(jié)的梦谜,未經(jīng)本人許可丘跌,禁止轉(zhuǎn)載(長文預(yù)警,能仔細看完的唁桩,看完還不主動點贊收藏的闭树,我覺得應(yīng)該沒有。)
1.
??我們知道荒澡, 是一個小數(shù)點后無限位的無理數(shù)报辱,計算機是無法精確表示的。所以单山,在MATLAB中碍现,
pi
只是一個近似值,3.141592653589793米奸,精確到小數(shù)點后15位昼接。
pi == 3.141592653589793
% ans = 1
2. sin(pi)≠0
??因為,導(dǎo)致的一個問題是躏升,
sin(pi)
并不精確的等于0辩棒。事實上,所有的sin(n*pi)
都不為0膨疏,且互不相等一睁。
sin(pi)
% ans = 1.224646799147353e-16
sin(2*pi)
% ans = -2.449293598294706e-16
sin(3*pi)
% ans = 3.673940397442059e-16
甚至可以看到,當(dāng)n足夠大時佃却,sin(n*pi)會接近于±1.
sin(4e16*pi)
% ans = -0.999478704149319
正確地計算的方法是者吁,利用
sinpi
函數(shù)。這是一個R2018b推出的新函數(shù)饲帅,能夠精確計算形如的值复凳。
sinpi(1) == 0
% ans = 1
sinpi(4e16) == 0
% ans = 1
sinpi(1/4) == sqrt(2)/2
% ans = 1
如果是早期版本的MATLAB瘤泪,就只能將弧度制轉(zhuǎn)換成角度制后,用sind函數(shù)計算了育八。
sind(180) == 0
% ans = 1
sind(180*4e16) == 0
% ans = 1
sind(45) == sqrt(2)/2
% ans = 1
3. 0.7/0.1≠7 但0.2/0.1=2
0.7/0.1 == 7
% ans =0
??這是由于浮點數(shù)的表示機制決定的对途,計算機是二進制的,在表示某些小數(shù)時髓棋,并不是精確表示的实檀。由于二進制的限制,計算機無法精確的表示0.7按声。
format long
0.7/0.1
% ans = 6.999999999999999
??事實上膳犹,計算機中,所有的數(shù)不是連續(xù)的签则,而是離散的须床,每個浮點數(shù)和下一個更大的浮點數(shù)之間都存在一個較小的間隔,這個間隔可以用eps
函數(shù)來計算渐裂。
??例如豺旬,1234567890.0123456789最后一位的9是無效的,因為即使是雙精度double也無法精確到最后一位芯义。
1234567890.0123456789 == 1234567890.012345678
% ans = 1
% 左邊的數(shù)比右邊的數(shù)多了最后一位的9哈垢,但仍然是相等的。
那么扛拨,如果遇到類似的問題時耘分,如何輸出正確的判斷結(jié)果呢?一種可行的方法是全部轉(zhuǎn)換為整數(shù)
(0.7*10)/(0.1*10) == 7
% ans = 1
或者绑警,求兩個數(shù)的差值求泰,看差值的絕對值是否足夠小。如果精度要求不高的話,可以自己設(shè)定一個較小的閾值
abs(0.7/0.1 - 7) <= 1e-6
% ans = 1
如果精度要求高的話,可借助于eps函數(shù)
abs(0.7/0.1 - 7) <= eps(7)
% ans = 1
4. 矩陣轉(zhuǎn)置.' 與' 的區(qū)別
??大多數(shù)教程计盒、網(wǎng)上博客渴频、甚至?xí)锩娑紩岬剑琈ATLAB的矩陣轉(zhuǎn)置運算符'
北启。事實上卜朗,這是一個很大的誤區(qū)。A'
是矩陣A的共軛轉(zhuǎn)置咕村,只不過大多數(shù)情況下场钉,A虛部為0,無法體現(xiàn)出共軛懈涛。
A = [1, 2; 3, 4];
A'
% ans = [1, 3; 2, 4]
但是逛万,如果A是復(fù)數(shù)矩陣,就會出現(xiàn)共軛批钠。
A = [1+2i, 3+4i; 5+6i, 7+8i];
A'
% ans = [1-2i,5-6i; 3-4i,7-8i];
正確的轉(zhuǎn)置運算符是.'
A = [1+2i, 3+4i; 5+6i, 7+8i];
A.'
% ans = [1+2i,5+6i; 3+4i,7+8i];
5. 每次生成相同的隨機數(shù)
??在知乎上面有很多類似的問題宇植,如何保證每次生成的隨機數(shù)相同得封。我看到很多回答都是說,將生成的隨機數(shù)save
成本地mat文件指郁,然后在下次要用的時候忙上,用load
函數(shù)讀取mat文件中的值。這種方法也不是不可以闲坎,但是效率比較低晨横,文件讀寫可能會花費大量的時間。
??事實上箫柳,MATLAB生成的隨機數(shù)都是偽隨機數(shù),也就是說啥供,按照某種特定的算法生成的悯恍。這意味著,隨機數(shù)的生成過程是可以復(fù)現(xiàn)的伙狐,可以通過控制隨機數(shù)生成器的"種子"涮毫,確保每次生成相同的隨機數(shù)。
??rng
函數(shù)用于控制隨機數(shù)的生成贷屎。
% 保存當(dāng)前隨機數(shù)生成器的狀態(tài)("種子")
scurr = rng();
% 生成隨機數(shù)
A = randn([1,4])
% A = [-0.4611 0.1132 1.3442 -0.4842];
% 每次想要生成相同的隨機數(shù)時
% 先用rng函數(shù)將生成器的狀態(tài)置為上次保存的
scurr rng(scurr);
A = randn([1,4])
% A = [-0.4611 0.1132 1.3442 -0.4842];
6. 如何生成類似于A1,A2,....,A100的變量
??在知乎上也有很多類似的問題罢防,紛紛表示用手一個一個的敲進去太累了。大部分的回答是唉侄,用eval
咒吐、feval
之類的函數(shù)。但是eval
函數(shù)的效率很低属划,而且代碼的可讀性會很差恬叹。
??事實是,絕大部分情況下同眯,并不需要生成A1,A2,...,A100這樣的變量绽昼。真正需要做的是,改變自己的編程思路须蜗,給數(shù)組A增加一個額外的維度硅确,用這個維度來表示A1到A100。
% 例如A1到A100,
% An每個變量的值為[n,n+1,...,n+99]
% 用eval函數(shù)可以完成上述賦值
for ii = 1:100
eval(['A' num2str(ii) '=' num2str(ii) ':' num2str(ii+99)]);
end
% 事實上明肮,只需要給A多一個維度就可以了
% 這樣當(dāng)你想要使用A1變量時菱农,只需要調(diào)用A(1,:)就可以了
for ii = 1:100
A(ii,:) = ii:ii+99;
end
如果A1,A2,...A100每個變量的長度不一樣,不能放到一個矩陣里面怎么辦晤愧?改用cell
數(shù)組
% 例如A1到A100,
% An每個變量的值為[n,n+1,...,100]
% 用eval函數(shù)可以生成完成上述賦值
for ii = 1:100
eval(['A' num2str(ii) '=' num2str(ii) ':' num2str(100)]);
end
% 使用cell元胞數(shù)組完成類似的賦值
% 當(dāng)你想使用A1變量時,只需要調(diào)用A{1}
for ii = 1:100
A{ii} = ii:100;
end
7. i 和j都是MATLAB內(nèi)置函數(shù)(built-in function)
??在上面的例子大莫,我在for循環(huán)里面的循環(huán)變量用的ii,而不是常用的i官份,這是為什么呢只厘?因為在MATLAB中烙丛,i
是一個內(nèi)置函數(shù),代表的是虛數(shù)單位(j
也是)羔味,用于輸入復(fù)數(shù)河咽。
% 確保當(dāng)前工作區(qū)沒有i,j變量
clear i j
i == j
% ans = 1
??當(dāng)然可以將i
和j
覆蓋為變量,但是覆蓋內(nèi)置函數(shù)不是一個好的編程習(xí)慣赋元,同時也會帶來運行速度上的降低忘蟹。而且,一旦程序中涉及輸入復(fù)數(shù)搁凸,就可能會出現(xiàn)錯誤媚值。
% 下面的代碼中,想實現(xiàn)的是復(fù)數(shù)1+2i
% 但實際上,A = [3,5,7,9,11]
a = 1;
b = 2;
for i = 1:5
A(i) = a + b*i;
end
那么,為了避免這個問題护糖,一種良好的編程習(xí)慣就是褥芒,將循環(huán)變量i,j改成ii,jj。在寫虛數(shù)單位時嫡良,用1i,1j代替i,j.
for ii = 1:5
A(ii) = a + b*1i;
end
8. dbstop if error真的很好用
??當(dāng)我作為一個MATLAB新手首次接觸這個命令時锰扶,感覺仿佛迎來了春天,給我?guī)淼恼鸷巢粊営诘谝淮谓佑|bsxfun
寝受。
??在調(diào)試MATLAB程序時坷牛,如果有錯誤,我們經(jīng)常需要在出錯的那一行加上斷點很澄,然后重新運行腳本或函數(shù)京闰。這樣程序會運行到斷點前,我們就可以觀察出錯前各個變量的值甩苛,進而找到bug忙干。這一過程需要經(jīng)歷運行,出錯浪藻,加斷點捐迫,再運行這一過程。
??如果在MATLAB命令行輸入dbstop if error
爱葵,然后再運行程序施戴,這樣程序會自動停在出錯的那一行,這時可以直接觀察各個變量的值萌丈,省去了自己加斷點的過程赞哗。而且,不需要每次運行程序前都輸入dbstop if error
辆雾,只需要輸入一次就OK肪笋。
如果想要在每次warning前暫停程序,也可以在命令行輸入dbstop if warning
9. 調(diào)試程序時在不同的函數(shù)之間傳遞值
??我們知道,如果想在想讓一個值在各個函數(shù)都使用藤乙,可以將其設(shè)為全局變量(global
)猜揪,此時聲明了該全局變量的函數(shù),可以互相傳遞這個值坛梁。
??那么而姐,如果是在調(diào)試時,程序已經(jīng)運行了一半划咐,停在了某個斷點前拴念,想將函數(shù)內(nèi)當(dāng)前的某個值傳遞到上一個函數(shù),或者直接傳遞到基礎(chǔ)工作區(qū)褐缠,用于后續(xù)對變量值的仔細分析政鼠,怎么辦?(基礎(chǔ)工作區(qū)就是队魏,MATLAB的主函數(shù)空間缔俄,當(dāng)程序運行完后的工作區(qū)。)
??解決方法是使用assignin
函數(shù)器躏。
??assignin
函數(shù)能夠在基礎(chǔ)工作區(qū)(base
),或者調(diào)用當(dāng)前函數(shù)的函數(shù)工作區(qū)(caller
)內(nèi)蟹略,產(chǎn)生一個新的變量登失,該變量的值等于當(dāng)前函數(shù)空間內(nèi)的某個值。(比較繞口挖炬,仔細看下面例子吧)
??例如揽浙,下面的函數(shù)myfun, 函數(shù)本身是用于畫圖的,不會返回任何值意敛。
function myfun()
a = randn([1,1e4]);
plot(a);
end
第3行前有一個斷點馅巷,程序停在斷點前。這時草姻,我們可以看到a的值钓猬,但我們希望將a的值導(dǎo)入到基礎(chǔ)工作區(qū),進行后續(xù)的分析撩独。(一旦我們退出調(diào)試敞曹,或者繼續(xù)運行程序,都無法得到a的值综膀。當(dāng)然澳迫,可以修改myfun,給它添加一個返回值a剧劝,然后重新運行程序橄登。但是,如果是一個復(fù)雜的程序,花了很多時間程序才運行到這個位置拢锹,重新開始意味著前功盡棄谣妻。)
??這時,我們可以在調(diào)試時面褐,在MATLAB命令行中輸入下列命令:
% 在基礎(chǔ)工作區(qū)生成一個變量a_debug
% a_debug的值與a的值相等
assignin('base','a_debug',a)
這樣拌禾,在退出當(dāng)前調(diào)試后,能夠在基礎(chǔ)工作區(qū)看到一個a_debug變量展哭,它的值等于myfun函數(shù)中a的值湃窍。
10.退出MATLAB時自動保存當(dāng)前工作空間的變量
??如果當(dāng)前路徑中存在finish.m文件,matlab在退出前匪傍,會自動運行finish.m文件您市。通過編寫finish文件,即可實現(xiàn)對當(dāng)前工作空間變量的保存役衡。
save 'C:\Users\matlab.mat'
新建腳本文件茵休,將上述命令復(fù)制過去,保存為finish.m文件手蝎。這樣當(dāng)你退出MATLAB時榕莺,就會自動將當(dāng)前工作區(qū)內(nèi)的變量全部保存到C:\Users\matlab.mat文件中。
注意:
- 如果想要當(dāng)前文件夾位于任何文件夾時均能觸發(fā)finish.m腳本的運行棵介,將其保存在MATLABPATH文件夾中钉鸯,具體請使用
path
命令查看。 - 退出matlab時邮辽,右上角的"×"唠雕,命令行輸入
quit
,exit
均能自動觸發(fā)finish.m腳本的運行。 - 命令行輸入
quit force
時吨述,強制退出matlab岩睁,不會執(zhí)行finish.m腳本。
11. 啟動MATLAB時自動讀取上次保存的工作空間變量
??在MATLAB默認的工作文件夾揣云,編寫startup.m腳本捕儒,MATLAB在啟動時會自動運行該腳本。
load 'C:\Users\matlab.mat'
startup.m文件必須位于MATLAB啟動后的默認文件夾內(nèi)
注意:
- MATLAB的默認文件夾可以更改
12. 使用pcode對MATLAB代碼進行模糊處理
??有時邓夕,我們希望自己的代碼別人能夠使用肋层,但不能看到源代碼,這是可以通過pcode
命令簡單實現(xiàn)這一操作翎迁。
??MATLAB在運行.m程序文件時栋猖,事實上是先將.m文件(文本文件)轉(zhuǎn)換成.p文件(二進制文件)。p文件一般是隱藏文件汪榔,在文件夾中是無法看到的蒲拉。我們可以通過pcode命令將m文件轉(zhuǎn)換成p文件肃拜。將p文件轉(zhuǎn)給第三方使用,但對方無法輕易看到源代碼雌团。
% 下列命令會在當(dāng)前文件夾內(nèi)生成myfun.p
% myfun.p與myfun.m具有相同的功能
pcode myfun
注意:
- p文件的優(yōu)先級高于m文件燃领,如果函數(shù)名相同,matlab會優(yōu)先使用p文件锦援。
- p文件不夠安全猛蔽,不能將其視為加密,只是一個簡單的模糊處理灵寺。
- p文件是可以被逆向的曼库,至少MathWorks就可以。
- 網(wǎng)上流傳過p文件逆向的小工具略板,這些工具要么被刪毁枯,要么已不適用現(xiàn)在的版本。
- 上古版本的MATLAB可以對p文件進行斷點調(diào)試叮称,某種程度上可以推測出源碼种玛。
- MATLAB軟件本身也使用了很多p文件,說明MathWorks認為p文件沒有那么容易被破解瓤檐。
- 如果代碼涉及知識產(chǎn)權(quán)或商業(yè)機密赂韵,p文件是不安全的,妥善的方法是將代碼部署在遠程服務(wù)器上挠蛉。
結(jié)束語
你都看到這里了祭示,還不點贊收藏?
- 本文由本人首發(fā)于知乎:易夕-MATLAB Tricks 專欄碌秸,現(xiàn)由本人轉(zhuǎn)發(fā)于簡書-易夕奐,未得到本人許可悄窃,禁止轉(zhuǎn)載讥电。