[英]Vectorizing code - How to reduce MATLAB computational time
我有這段代碼
N=10^4;
for i = 1:N
[E,X,T] = fffun(); % Stochastic simulation. Returns every time three different vectors (whose length is 10^3).
X_(i,:)=X;
T_(i,:)=T;
GRID=[GRID T];
end
GRID=unique(GRID);
% Second part
for i=1:N
for j=1:(kmax)
f=find(GRID==T_(i,j) | GRID==T_(i,j+1));
s=f(1);
e=f(2)-1;
counter(X_(i,j), s:e)=counter(X_(i,j), s:e)+1;
end
end
該代碼對隨機過程執行 N 個不同的模擬(由 10^3 個事件組成,發生在取決於特定模擬的離散時刻(T 向量)。現在(第二部分)我想知道,作為時間常數的函數, 有多少模擬處於特定狀態(X 假定值介於 1 和 10 之間)。我的想法是:創建一個網格向量,其中包含任何模擬中發生某些事情的所有時刻。然后,循環模擬,循環發生某些事情的時間步長,並遞增與此特定時間片相對應的所有計數器 indeces。
然而,第二部分非常繁重(我的意思是在標准四核 CPU 上處理數天)。 它不應該。 是否有任何想法(也許是關於以更有效的方式比較向量)來減少 CPU 時間?
這是一個獨立的“second_part”
N=5000;
counter=zeros(11,length(GRID));
for i=1:N
disp(['Counting sim #' num2str(i)]);
for j=1:(kmax)
f=find(GRID==T_(i,j) | GRID==T_(i,j+1),2);
s=f(1);
e=f(2)-1;
counter(X_(i,j), s:e)=counter(X_(i,j), s:e)+1;
end
end
counter=counter/N;
stop=find(GRID==Tmin);
stop=stop-1;
plot(counter(:,(stop-500):stop)')
與相關的虛擬數據( filedropper.com/data_38 )。 在實際情況下,矩陣有 2x 行和 10x 列。
這是我的理解:
T_
是來自 N 次模擬的時間步長矩陣。
X_
是這些模擬中T_
處的模擬狀態矩陣。
所以如果你這樣做:
[ut,~,ic]= unique(T_(:));
你得到ic
,它是T_
所有唯一元素的索引向量。 然后你可以寫:
counter = accumarray([ic X_(:)],1);
並得到沒有的counter
。 行作為您唯一的時間步長,而不是。 列作為X_
的唯一狀態(它們都是並且必須是整數)。 現在你可以說對於每個時間步ut(k)
模擬處於狀態m
是counter(k,m)
。
在您的數據中,值大於 1 的m
和k
的唯一組合是(1,1)
。
編輯:
從下面的評論中,我了解到您記錄了所有狀態更改以及它們發生時的時間步長。 然后每次模擬更改狀態時,您都希望從所有模擬中收集所有狀態並計算每種類型有多少狀態。
這里的主要問題是你的時間是連續的,所以基本上T_
每個元素都是唯一的,並且你有超過一百萬個時間步來循環。 完全矢量化這樣的過程需要大約 80GB 的內存,這可能會卡住你的計算機。
所以我尋找向量化和循環時間步長的組合。 我們首先找到所有唯一的間隔,並預分配counter
:
ut = unique(T_(:));
stt = 11; % no. of states
counter = zeros(stt,numel(ut));r = 1:size(T_,1);
r = 1:size(T_,1); % we will need that also later
然后我們遍歷ut
所有元素,每次以矢量化方式在所有模擬中查找T_
中的相關時間步長。 最后我們使用histcounts
來計算所有狀態:
for k = 1:numel(ut)
temp = T_<=ut(k); % mark all time steps before ut(k)
s = cumsum(temp,2); % count the columns
col_ind = s(:,end); % fins the column index for each simulation
% convert the coulmns to linear indices:
linind = sub2ind(size(T_),r,col_ind.');
% count the states:
counter(:,k) = histcounts(X_(linind),1:stt+1);
end
在我的電腦上進行 1000 次模擬大約需要 4 秒,因此整個過程增加了一個多小時。 不是很快...
您還可以嘗試以下一兩個調整來縮短運行時間:
正如你可以在這里閱讀, accumarray
似乎在小數組更快的工作,然后histcouns
。 所以可能想切換到它。
此外,直接計算線性索引比sub2ind
更快,因此您可能想嘗試一下。
在上面的循環中實施這些建議,我們得到:
R = size(T_,1);
r = (1:R).';
for k = 1:K
temp = T_<=ut(k); % mark all time steps before ut(k)
s = cumsum(temp,2); % count the columns
col_ind = s(:,end); % fins the column index for each simulation
% convert the coulmns to linear indices:
linind = R*(col_ind-1)+r;
% count the states:
counter(:,k) = accumarray(X_(linind),1,[stt 1]);
end
在我的計算機中切換到accumarray
和/或刪除sub2ind
獲得了輕微的改進,但它並不一致(使用timeit
對ut
100 或 1K 元素進行測試),所以你最好自己測試一下。 然而,這仍然很長。
您可能需要考慮的一件事是嘗試離散化您的時間步長,這樣您循環的獨特元素就會少得多。 在您的數據中,大約 8% 的時間間隔小於 1。如果您可以假設這足夠短以被視為一個時間步長,那么您可以舍入您的T_
並僅獲得 ~12.5K 唯一元素,這需要大約一分鍾循環。 您可以對 0.1 個間隔(小於時間間隔的 1%)執行相同操作,並獲得 122K 個元素進行循環,大約需要 8 小時...
當然,以上所有時間都是使用相同算法的粗略估計。 如果您確實選擇舍入時間,則可能有更好的方法來解決此問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.