【问题标题】:Vectorizing for loops in MATLAB在 MATLAB 中对循环进行矢量化
【发布时间】:2010-02-10 05:23:19
【问题描述】:

我不太确定这是否可能,但我对 MATLAB 的理解肯定会更好。

我有一些代码希望矢量化,因为它在我的程序中造成了相当大的瓶颈。它是优化例程的一部分,它有许多可能的短期平均 (STA)、长期平均 (LTA) 和灵敏度 (OnSense) 配置来运行。

时间是矢量格式,FL2onSS是主要数据(一个Nx1双精度),FL2onSSSTA是它的STA(NxSTA双精度),FL2onSSThresh是它的阈值(NxLTAxOnSense双精度)

这个想法是计算一个红色警报矩阵,它将是 4D - 在整个程序的其余部分中使用的 alarmStatexSTAxLTAxOnSense。

Red = zeros(length(FL2onSS), length(STA), length(LTA), length(OnSense), 'double');
for i=1:length(STA)
    for j=1:length(LTA)
        for k=1:length(OnSense)
            Red(:,i,j,k) = calcRedAlarm(Time, FL2onSS, FL2onSSSTA(:,i), FL2onSSThresh(:,j,k));
        end
    end
end

我目前有这个重复的功能,试图从中获得更快的速度,但显然如果整个事情可以矢量化会更好。换句话说,如果有更好的解决方案,我不需要保留该功能。

function [Red] = calcRedAlarm(Time, FL2onSS, FL2onSSSTA, FL2onSSThresh)

% Calculate Alarms
% Alarm triggers when STA > Threshold

zeroSize = length(FL2onSS);

%Precompose
Red = zeros(zeroSize, 1, 'double');

for i=2:zeroSize
    %Because of time chunks being butted up against each other, alarms can
    %go off when they shouldn't. To fix this, timeDiff has been
    %calculated to check if the last date is different to the current by 5
    %seconds. If it isn't, don't generate an alarm as there is either a
    %validity or time gap.
    timeDiff = etime(Time(i,:), Time(i-1,:));
    if FL2onSSSTA(i) > FL2onSSThresh(i) && FL2onSSThresh(i) ~= 0 && timeDiff == 5 
        %If Short Term Avg is > Threshold, Trigger
        Red(i) = 1;
    elseif FL2onSSSTA(i) < FL2onSSThresh(i) && FL2onSSThresh(i) ~= 0 && timeDiff == 5
        %If Short Term Avg is < Threshold, Turn off
        Red(i) = 0;
    else
        %Otherwise keep current state
        Red(i) = Red(i-1);
    end
end
end

代码很简单,我就不多解释了。如果您需要说明特定行的作用,请告诉我。

【问题讨论】:

    标签: matlab performance vectorization


    【解决方案1】:

    诀窍是将所有数据放入同一个表单,主要使用 repmatpermute。那么逻辑就是简单的部分。

    我需要一个讨厌的技巧来实现最后一部分(如果条件都不成立,则使用最后的结果)。通常这种逻辑是使用 cumsum 完成的。我必须使用另一个 2.^n 矩阵来确保使用定义的值(这样 +1,+1,-1 将真正给出 1,1,0) - 只需查看代码 :)

    %// define size variables for better readability
    N = length(Time);
    M = length(STA);
    O = length(LTA);
    P = length(OnSense);
    
    %// transform the main data to same dimentions (3d matrices)
    %// note that I flatten FL2onSSThresh to be 2D first, to make things simpler. 
    %// anyway you don't use the fact that its 3D except traversing it.
    FL2onSSThresh2 = reshape(FL2onSSThresh, [N, O*P]);
    FL2onSSThresh3 = repmat(FL2onSSThresh2, [1, 1, M]);
    FL2onSSSTA3 = permute(repmat(FL2onSSSTA, [1, 1, O*P]), [1, 3, 2]);
    timeDiff = diff(datenum(Time))*24*60*60;
    timeDiff3 = repmat(timeDiff, [1, O*P, M]);
    %// we also remove the 1st plain from each of the matrices (the vector equiv of running i=2:zeroSize
    FL2onSSThresh3 = FL2onSSThresh3(2:end, :, :);
    FL2onSSSTA3 = FL2onSSSTA3(2:end, :, :);
    
    Red3 = zeros(N-1, O*P, M, 'double');
    
    %// now the logic in vector form
    %// note the chage of && (logical operator) to & (binary operator)
    Red3((FL2onSSSTA3 > FL2onSSThresh3) & (FL2onSSThresh3 ~= 0) & (timeDiff3 == 5)) = 1;
    Red3((FL2onSSSTA3 < FL2onSSThresh3) & (FL2onSSThresh3 ~= 0) & (timeDiff3 == 5)) = -1;
    %// now you have a matrix with +1 where alarm should start, and -1 where it should end.
    
    %// add the 0s at the begining
    Red3 = [zeros(1, O*P, M); Red3];
    
    %// reshape back to the same shape
    Red2 = reshape(Red3, [N, O, P, M]);
    Red2 = permute(Red2, [1, 4, 2, 3]);
    
    %// and now some nasty trick to convert the start/end data to 1 where alarm is on, and 0 where it is off.
    Weights = 2.^repmat((1:N)', [1, M, O, P]); %// ' damn SO syntax highlighting. learn MATLAB already!
    Red = (sign(cumsum(Weights.*Red2))+1)==2;
    
    %// and we are done. 
    %// print sum(Red(:)!=OldRed(:)), where OldRed is Red calculated in non vector form to test this.
    

    【讨论】:

    • 太棒了!谢谢奥弗里。这肯定会让我有点消化,我需要澄清几点:我假设 FL2onSSThresh3 = reshape(FL2onSSThresh, [N, O*P]);实际上应该与 FL2onSSThresh2 相关联吗? timeDiff = etime(Time(i,:), Time(i-1,:));在这种情况下不可行吗?因为我们不再使用 i 进行迭代。 Time 是一个 datevec,所以我假设我可以使用 timeDiff = diff(Time) 代替上面的行,但这会给出一个维度差异很大的数组,并且 Red3 操作失败。
    猜你喜欢
    • 2015-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多