【问题标题】:Optimize two nested for-loops and if-condition优化两个嵌套的for循环和if-condition
【发布时间】:2016-12-18 13:05:01
【问题描述】:

我有以下一段代码,它通过两个嵌套的for-loops,中间有一个if 条件:

N=1e4;

cond_array = [0:(N/2-1) -N/2+(0:(N/2-1))]/(N);
condition  = 0.1;

arr_one = zeros(N, 1);
arr_two = zeros(N, 1);

for m=1:N
    for k=1:N
        if(abs(cond_array(k)) <= condition)
            arr_one(m)  = arr_one(m)  + m*k;
        else
            arr_two(m)  = arr_two(m)  + m*k;
        end
    end
end

我想优化此代码,因为我可能需要使用非常大的 N (&gt;1e4),并且根据我自己的经验,MATLAB 中的 for-loops 通常非常消耗 CPU 并且效率不高.

有没有办法优化这段代码,也许是通过使用适用于整个数组的向量化函数?

【问题讨论】:

  • arr_one = (1:N)*sum(find(abs(cond_array) &lt;= condition))arr_two类似?
  • 优化工作代码实际上是Code review的目的,所以像这样的问题在这里非常受欢迎! :)

标签: matlab for-loop optimization


【解决方案1】:

这是获得相同结果的更快(且可读性)的方法:

N=1e4;
cond_array = [0:(N/2-1) -N/2+(0:(N/2-1))]/(N);
condition  = 0.1;
% this is so you don't check the same condition multiple times:
cond = abs(cond_array)<=condition;
% a vector of the positions in 'arr_one' and 'arr_two':
pos = (1:N).';
% instead of m*k(1)+m*k(2)... use: m*sum(k):
k1 = sum(pos(cond)); % for arr_one
k2 = sum(pos(~cond));% for arr_two
% the final result:
arr_one = pos.*k1;
arr_two = pos.*k2;

对于您在 cmets 中提到的第二种情况,其中 m*k 变为 exp((m-1)*(k-1)),我们可以再次使用矢量化计算 exp((m(1)-1)*(k(1)-1)) +...+ exp((m(1)-1)*(k(N)-1))... 的总和,然后使用一些最小循环遍历所有 ms:

% we define new vectors of constants:
k1 = pos(cond);
k2 = pos(~cond);
% define new functions for each of the arrays:
ek1 = @(m) sum(exp((m-1).*(k1-1)));
ek2 = @(m) sum(exp((m-1).*(k2-1)));
% and we use 'arrayfun' for the final result:
arr_one = arrayfun(ek1,1:N).';
arr_two = arrayfun(ek2,1:N).';

arrayfun 并不比for 循环快,只是更紧凑。使用for 循环将是这样的:

arr_one = zeros(N,1);
arr_two = zeros(N,1);
for k = 1:N
    arr_one(k) = ek1(k);
    arr_two(k) = ek2(k);
end

这是另一个更通用的选项,使用bsxfun

ek = @(m,k) exp((m-1).*(k-1));
arr_one = sum(bsxfun(ek,1:N,k1)).';
arr_two = sum(bsxfun(ek,1:N,k2)).';

【讨论】:

  • 我不确定arr_one(N,1) = 0; 是否安全,如果我没记错的话,您的向量中可能会出现随机值。为了安全起见,最好使用zeros(N, 1)
  • @gaborous 很安全,MATLAB 无论如何都会初始化一个 zeros 矩阵,你可以自己检查。在初始化之后尝试使用任何小于Narr_one(k)arr_one(k),看看你得到的不是零。
  • 好吧,我have just seen 认为,自从 MATLAB R2015b 以来,zeros 函数更快。所以我编辑我的答案。
  • @EBH 谢谢。如果最后一个词 m*k 采用更高级的形式,例如 exp((m-1)*(k-1))
  • @BillyJean 这比编程更重要,也是另一个问题的主题,但无论如何,请参阅我的编辑。如果它解决了问题,请考虑接受答案,如果您对特定场景还有其他问题 - 发布一个新问题。
【解决方案2】:

研究逻辑索引的使用。 如果不太了解您的代码,我想您可以通过类似于下面的数组函数删除循环。

arr_one(abs(cond_array)<condition)

这将消除对比较函数的需要,这将允许您使用匿名 array function 来创建您的计算。

但是,如果我正确理解您的代码,您只是将条件索引的位置和数组索引的位置的乘积添加到数组中。 如果你这样做,那么只做类似于下面的事情会容易得多。

position=(1:N)'
arr_one(abs(cond_array)<condition)=arr_one(abs(cond_array)<condition)+position(abs(cond_array)<condition)

在此语句中,您正在查找 cond_array 小于条件的所有项目,并添加位置(实际上是您之前示例中的 m 和 k 的代理)。

在运行时方面,我认为你必须比 1e4 大 1 到 3 个数量级才能使这段代码在运行时产生显着差异。

【讨论】:

    猜你喜欢
    • 2019-08-23
    • 2012-01-27
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    相关资源
    最近更新 更多