【问题标题】:Performance of find() vs. for loopfind() 与 for 循环的性能
【发布时间】:2011-04-28 21:57:36
【问题描述】:

我有一大组(4000 个值)未排序的正态分布点。我将这些数据点中的每一个都放入限制在 BinLimit 数组中的 bin 中。然后我将每个 bin 中的值的数量制成表格。

X 是原始数据的数组,N 是数据点的数量。所需的 bin 数量 (TotalBins) 由用户指定。

方法#1

for i=1:TotalBins
    Freq(i) = length(find(X >= BinLimit(j) & X <= BinLimit(j+1)));
    j = j + 1;
end

方法#2:

sort(X);

for i=1:N
    if (X(i) >= BinLimit(j) && X(i) <= BinLimit(j+1))
        Freq(j) = freq(j) + 1;
    elseif (j < TotalBins)
        Freq(j+1) = freq(j+1) + 1;
        j = j + 1;
    end
end

现在,我知道第一个解决方案速度较慢 - 对于 Total Bins (25-50) 的正常值,它大约慢 8 倍,但我想知道是否有比我正在做的更快、更有效的解决方案方法#2。

【问题讨论】:

    标签: matlab optimization loops


    【解决方案1】:

    使用histc

    N = HISTC(X,EDGES),对于向量 X, 计算 X 中的值的数量 落在 EDGES 中的元素之间 向量(必须包含 单调非递减值)。 N 是一个 LENGTH(EDGES) 向量,包含 这些很重要。

    N(k) 将计算 值 X(i) 如果 EDGES(k)

    例如

    BinLimit = sort(rand(50,1));
    X = rand(4000,1);
    Freq = histc(X,BinLimit);
    

    为了好玩:

    %%# Generating data
    X = rand(1000000,1);
    BinLimit = sort(rand(50,1));
    
    %%# OP's method
    disp('Method #1');
    disp('=========');
    tic;
    j =1;
    TotalBins = numel(BinLimit)-1;
    for i=1:TotalBins
        Freq(i) = length(find(X >= BinLimit(j) & X <= BinLimit(j+1)));
        j = j + 1;
    end
    toc
    
    %%# histc
    disp('histc');
    disp('=====');
    tic;
    histc(X,BinLimit);
    toc
    
    %%# My method
    disp('Alternative');
    disp('===========');
    tic;
    TotalBins = numel(BinLimit)-1;
    Freq = zeros(TotalBins,1);
    for i = 1:TotalBins
        Freq(i) = sum(X >= BinLimit(i) & X <= BinLimit(i+1));
    end
    toc
    

    经过几次跑步热身:

    Method #1
    =========
    Elapsed time is 0.803120 seconds.
    histc
    =====
    Elapsed time is 0.030633 seconds.
    Alternative
    ===========
    Elapsed time is 0.704808 seconds.
    

    【讨论】:

    • :( 测试过histc,比我做的快1.4倍。
    • 我仍然希望看到在代码逻辑方面最好的解决方案。
    • @Radu:重新发明轮子 :) ? MATLAB 非常注重了解正确的例程和学习如何矢量化(两者都是相关的),所以 ....
    • @Jacob,太棒了。更快,代码更简单!谢谢。
    • 被选为答案是因为它优于其他选项,尽管我确实喜欢 Amro 的解决方案。谢谢!
    【解决方案2】:

    除了使用 HISTC,这里有一个矢量化的单行解决方案:

    X = randn(4000,1);                %# column vector
    BinLimits = linspace(-4,4,10);    %# row vector
    
    Freq = sum( bsxfun(@ge, X, BinLimits(1:end-1)) & bsxfun(@le, X, BinLimits(2:end)) )
    

    请注意,它的 空间 效率不高,因为它创建了一个大小矩阵: length(X) by length(BinLimits)-1

    【讨论】:

    • @Jacob:注意BinLimits是行向量,而X是列向量
    • @Amro:用我的数字测试过,似乎漏掉了最大值和最小值。对于一次重复,它的速度大约是我的解决方案的两倍,但我不能与 1 班轮争论。
    • @Radu:就像我最后提到的,你需要测试两侧之一的相等性,我懒得弄清楚哪个;)
    • @Radu:事实证明你需要两面都做。我编辑了上面的答案
    【解决方案3】:

    我认为您在检查 X 是否属于垃圾箱时可能会出错。对于落在 BinLimits 上的 X 值,您可能会获得多个 bin。

    X >= BinLimit(j) & X <= BinLimit(j+1)
    

    不管怎样,既然你要求一个循环解决方案,这里有一个比你的方法 2 表现更好的解决方案。

    j=1;
    i=1;
    while i<=N
        while i<=N && X(i)<=BinLimit(j+1)
            i=i+1;
        end
        Freq(j) = i-1;
        j = j+1;
    end
    Freq=diff([0 Freq]);
    

    它需要对 X 进行排序,就像在您的代码中一样。下面是所有讨论过的排序 X 数组方法的时间安排( histc 对排序 X 也工作得更快,所以这是一个公平的比较):

    Sort
    ===========
    Elapsed time is 0.019205 seconds.
    Method #1
    =========
    Elapsed time is 0.209979 seconds.
    histc
    =====
    Elapsed time is 0.009595 seconds.
    Alternative
    ===========
    Elapsed time is 0.228400 seconds.
    Method 2
    ===========
    Elapsed time is 0.025400 seconds.
    my method
    ===========
    Elapsed time is 0.011920 seconds.
    bsxfun by Amro
    ===========
    Elapsed time is 0.179937 seconds.
    

    如您所见,此循环结构的性能几乎与 histc 一样好(差 25%)。

    这是用于排序的 X(排序时间也在上面的结果中给出)。这是未排序数组的 histc 结果(与上面相同的 X,随机排列)

    histc
    =====
    Elapsed time is 0.030367 seconds.
    

    如您所见,排序和 histc 在排序后的数组上一起运行的时间类似于在未排序的数组上运行 histc。

    【讨论】:

      猜你喜欢
      • 2015-08-28
      • 2017-03-01
      • 2012-11-18
      • 2017-09-19
      • 2012-08-06
      • 2010-11-13
      • 2018-01-20
      • 1970-01-01
      相关资源
      最近更新 更多