簡體   English   中英

向量化代碼 - 如何減少 MATLAB 計算時間

[英]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)模擬處於狀態mcounter(k,m)

在您的數據中,值大於 1 的mk的唯一組合是(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 秒,因此整個過程增加了一個多小時。 不是很快...

您還可以嘗試以下一兩個調整來縮短運行時間:

  1. 正如你可以在這里閱讀accumarray似乎在小數組更快的工作,然后histcouns 所以可能想切換到它。

  2. 此外,直接計算線性索引比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獲得了輕微的改進,但它並不一致(使用timeitut 100 或 1K 元素進行測試),所以你最好自己測試一下。 然而,這仍然很長。


您可能需要考慮的一件事是嘗試離散化您的時間步長,這樣您循環的獨特元素就會少得多。 在您的數據中,大約 8% 的時間間隔小於 1。如果您可以假設這足夠短以被視為一個時間步長,那么您可以舍入您的T_並僅獲得 ~12.5K 唯一元素,這需要大約一分鍾循環。 您可以對 0.1 個間隔(小於時間間隔的 1%)執行相同操作,並獲得 122K 個元素進行循環,大約需要 8 小時...

當然,以上所有時間都是使用相同算法的粗略估計。 如果您確實選擇舍入時間,則可能有更好的方法來解決此問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM