在排序向量中找到位置,快

时间:2016-10-17 22:17:13

标签: matlab performance

我在matlab中有一个已排序的向量,称之为F,对于这个例子,它说的是:

F = [1,2,3,4,5,6,7,8,9,10];

我还有一个任意矩阵A,其中0-10区间的元素说:

 A = [1.5,9.1,5.2;2.3,0.4,7.9];

然后我对矩阵I感兴趣,A中的每个条目告诉我它所属的F中的哪个区间,在这种情况下作为下限给出:< / p>

I = [1,9,5;2,0,7];

我知道,在这种情况下,它可以通过四舍五入获得,但我正在寻找一般情况。我也有一段代码为我解决了这个问题,即:

I = zeros(size(A));
X(A<F(1)) = 0;
for i = 2:length(F)
   X(A<F(i) & A>F(i-1)) = i-1;
end
end

但是,此时,我会检查A中每个i的所有条目,即使条目只满足条件一次。

所以我的问题是找到一种更有效的方法来做到这一点。首先要避免多次检查相同的条目,但也可能利用向量F进行排序。

我也尝试过使用arrayfun(@(a) find(a>=F,1)-1,A);,但这比上面的逻辑版慢了大约100倍。

有什么建议吗?

3 个答案:

答案 0 :(得分:4)

这可以完成这项任务,而且可能非常快:

result = reshape(sum(bsxfun(@lt, F(:), A(:).'), 1), size(A)); 

我假设间隔被定义为( a b ]。对于[ a b )将@lt替换为@le

利用向量排序的事实,做某种binary search可能会更快。 histcounts可能在内部执行此操作。第三个输出给出了你想要的东西:

[~, ~, result] = histcounts(A, F);

答案 1 :(得分:1)

这是一个适用于float和int输入的解决方案。 超出插值区间范围的值NaN,因此应该确定在应用插值之前可以使用thode值做什么。

interp与选项nearest一起使用,将最近邻插值应用于数据。

function out = histcount3(v , interval)
    %compute center of bins
    centers = interval(1:end-1) + diff(interval)/2;
    %apply nearest neighbor interpolation
    out = interp1(centers,interval(1:end-1),v(:),'nearest');
end

如果间隔是整数类型且间隔范围合理,则可能的快速解决方案是:

function out = histcount2(v , interval)
    v= max(interval(1),v);
    v=min(interval(end),v);
    count= diff(interval);
    count(end) = count(end)+1;
    r = repelem(interval(1:end-1), count);
    out = r(floor(v)-interval(1)+1);
end

和以前一样,应该决定使用超出范围的值可以做些什么。我用最小和最大间隔替换那些valuse。 我在Octave中进行了测试,但Octave没有histcounthist函数的时间,前提是我认为它具有与histcount相同的算法,但结果不同。您可以使用histcount运行基准测试以获得正确的结果:

F = unique(randi([1000 100000],1,1000));
A = rand(100000,3)*101000;
disp('------histcount2:--------')
tic
    reshape(histcount2(A(:),F),size(A));
toc
disp('------histcount3:--------')
tic
    reshape(histcount3(A(:),F),size(A));
toc
disp('------hist:--------')
tic
    hist(A(:),F);
toc
disp('------bsxfun:--------')
tic
    reshape(sum(bsxfun(@le, F(:), A(:).'), 1), size(A));
toc

<强>结果

------histcount2:repelem--------
Elapsed time is 0.00749207 seconds.
------histcount3:interpolation--------
Elapsed time is 0.042104 seconds.
------hist:--------
Elapsed time is 0.135928 seconds.
------bsxfun:--------
Elapsed time is 0.516705 seconds.

答案 2 :(得分:1)

根据matlab的建议,对@Luis Mendo建议的一点改进应该是使用discretize

从文档中https://se.mathworks.com/help/matlab/ref/discretize.html

  

离散化的行为类似于histcounts函数的行为。使用histcounts查找每个bin中的元素数。另一方面,使用离散化来查找每个元素属于哪个bin(不计算)。

因此将代码行转换为:

I = discretize(A,F)-1;

虽然必须在变量F中指定最小值。从原始帖子继续我的示例,它会将F转换为:

F = [0,1,2,3,4,5,6,7,8,9,10];

但是,我的测试结果显示histcounts一直快一点。