【问题标题】:Matlab index to logic indexingMatlab 索引到逻辑索引
【发布时间】:2013-01-30 14:54:20
【问题描述】:

我已经给出了一个索引列表,例如i = [3 5] 和一个向量 v = 1:6。我需要一个函数f,它返回给定索引i 的向量v 的逻辑映射,例如:

f(i, length(v)) = [0 0 1 0 1 0]

因为我会调用这个函数几百万次,所以我想让它尽可能快。是否有执行此任务的内置函数?

【问题讨论】:

  • 使用i= [3 5],逻辑映射不应该是[0 0 1 0 1 0]吗?
  • @H.Muster:当然,谢谢你接听!
  • 注意这个在find的逆运算中,即find(ismember(v,i))返回i

标签: matlab logical-operators matrix-indexing


【解决方案1】:

我知道我迟到了,但我真的很想找到一个更快的解决方案,它和ismember 一样优雅。确实有一个,它使用了未记录的ismembc 函数:

ismembc(v, i)

基准测试

N = 7;
i = [3 5];

%// slayton's solution
tic
for ii = 1:1e5
    clear idx;
    idx(N) = false;
    idx(i) = true;
end
toc

%// H.Muster's solution
tic
for ii = 1:1e5
    v = 1:N;
    idx = ismember(v, i);
end
toc

%// Jonas' solution
tic
for ii = 1:1e5
    idx = sparse(i, 1, true, N, 1);
end
toc

%// ismembc solution
tic
for ii = 1:1e5
    v = 1:N;
    idx = ismembc(v, i);
end
toc

这是我得到的:

Elapsed time is 1.482971 seconds.
Elapsed time is 6.369626 seconds.
Elapsed time is 2.039481 seconds.
Elapsed time is 0.776234 seconds.

令人惊讶的是,ismembc 确实是最快的!

编辑:
对于非常大的 N 值(,当 v 是一个大数组时),更快的解决方案实际上是 slayton 的(以及 HebeleHododo 的,就此而言)。您有多种策略可供选择,请谨慎选择:)

H.Muster 编辑:
以下是基准测试结果,包括_ismemberoneoutput

Slayton's solution:
   Elapsed time is 1.075650 seconds.
ismember:
   Elapsed time is 3.163412 seconds.
ismembc:
   Elapsed time is 0.390953 seconds.
_ismemberoneoutput:
   Elapsed time is 0.477098 seconds.

有趣的是,Jonas 的解决方案不适合我,因为我收到了 Index exceeds matrix dimensions. 错误...

由 hoogamaphone 编辑:
值得注意的是,ismembc 要求两个输入都是数字的、排序的、非稀疏的、非 NaN 值,这是source documentation 中很容易忽略的一个细节。

【讨论】:

  • 请注意,我的回答还包括更快的解决方案builtin('_ismemberoneoutput', v, i),它可能与ismembc 基本相同。不过,ismembc 是一个不错的发现。
  • @H.Muster 我在尝试使用_ismemberoneoutput 时收到Cannot find builtin function '_ismemberoneoutput' 错误。如果它适合您,那么您可以对所有四种解决方案进行基准测试吗?
  • 我用你的基准测试了_ismemberoneoutput,它比ismembc稍慢。了解@blubb 可能也很有趣。
  • 感谢您的精彩汇编。这是使 SE 社区如此独特的完美示例!
  • 如果索引已排序,ismembc 工作正常,但如果没有排序则失败。
【解决方案2】:

您可以使用ismember

 i = [3 5];
 v = 1:6;

 ismember(v,i)

会回来

ans =

     0     0     1     0     1     0

对于可能更快的版本,您可以尝试

builtin('_ismemberoneoutput', v, i)

请注意,我仅针对您指定的行向量进行了测试。

【讨论】:

  • 这就是我要找的。不过应该注意的是,至少在我的特定情况下,@slayton 的解决方案如果使用预分配实现的话,速度会快 30%。
  • 如果您还没有向量 v,那么这会很慢,因为您需要在每次调用时分配一个带有 1:N 的向量。
  • @blubb:请注意 Eithan 的回答,它和 ismember 一样好,但速度要快得多。接受他的回答而不是我的回答似乎是公平的。
  • @H.Muster 谢谢,但是当v 是一个大数组时,它比 slayton 的解决方案要慢。
  • @EitanT:是的,但是无论出于何种原因,OP 都喜欢ismember 而不是 slayton 的解决方案,尽管它速度较慢。因此,他应该更喜欢你的解决方案......
【解决方案3】:

只需创建一个逻辑索引向量并将所需位置设置为真/假

idx = false( size( v) );
idx( i ) = true;

这可以像这样包装在一个函数中:

function idx = getLogicalIdx(size, i)
  idx = false(size);
  idx(i) = true;
end

如果您需要为每百万次操作中的每个操作分配一次相同大小的索引向量,然后在每次迭代中对其进行操作:

idx = false(size(v)); % allocate the vector
while( keepGoing)

  idx(i) = true; % set the desired values to true for this iteration

  doSomethingWithIndecies(idx);

  idx(i) = false; % set indices back to false for next iteration

end

如果您真的需要性能,那么您可以编写一个 mex 函数来为您执行此操作。这是我编写的一个非常基本的未经测试的函数,它比其他方法快约 2 倍:

#include <math.h>
#include <matrix.h>
#include <mex.h>

void mexFunction(int nlhs, mxArray *plhs[],
                 int nrhs, const mxArray *prhs[])
{
    double M;
    double *in;

    M = mxGetScalar(prhs[0]);
    in = mxGetPr(prhs[1]);
    size_t N = mxGetNumberOfElements(prhs[1]);



    plhs[0] = mxCreateLogicalMatrix( M,1 );
    mxLogical *out= mxGetLogicals( plhs[0] );


    int i, ind;
    for (i=0; i<N; i++){
        out[ (int)in[i] ] = 1;
    }

}

在matlab中有几种不同的方式来分配一个向量。有些比其他更快,请参阅此未记录的 Matlab 帖子以获得很好的总结:

这里有一些比较不同方法的快速基准。最后一种方法是迄今为止最快的,但它要求您为每个操作使用相同大小的逻辑索引向量。

N = 1000;
ITER = 1e5;

i = randi(5000,100,1);
sz = [N, 1];

fprintf('Create using false()\n');
tic;
for j = 1:ITER
    clear idx;
    idx = false( N, 1 );
    idx(i) = true;
end
toc;

fprintf('Create using indexing\n');
tic;
for j = 1:ITER
    clear idx;
    idx(N) = false;
    idx(i) = true;
end
toc;

fprintf('Create once, update as needed\n');
tic;
idx = false(N,1);
for j = 1:ITER
    idx(i) = true;
    idx(i) = false;
end
toc;

fprintf('Create using ismembc\n');
a = ones(N,1);
tic;
for j = 1:ITER

    idx = ismembc(1:N, i);
end
toc;

【讨论】:

  • 谢谢。这工作得相当好,但是,我将执行此操作数百万次。有什么办法可以加快速度?
  • @blubb 是的,您可以通过多种方式加快速度。它们中的大多数都与预分配有关,例如,如果您要创建相同长度的逻辑索引向量,请先创建它,然后再对其进行操作...
【解决方案4】:

我希望@slayton 的解决方案最快。但是,这是一种单线替代方案,如果向量很大,它至少可以为您节省一些内存。

vecLen = 6;
logicalIdx = sparse(idx,1,true,vecLen,1);

【讨论】:

    【解决方案5】:

    只需使用 idx 矩阵寻址一个新变量,它就会为您填充零:

    idx = [3 5];
    a(idx) = true
    

    不需要函数,也不需要传入长度,除非你也想要尾随零。

    【讨论】:

    • 因为 a 将是 [0 0 1 0 1]...?
    • 如果您愿意,可以将其设置为 true
    • 但是输入尺寸是 1x6 而不是 1x5。
    • 因此我的最后一句话。无论如何,你可以做同样的事情来初始化变量,即 a(length(v)) = false...
    【解决方案6】:

    你可以这样写一个函数:

    function logicalIdx = getLogicalIdx(idx, v)
        logicalIdx = zeros(1,size(v,2));
        logicalIdx(idx) = 1;
    end
    

    调用函数时:

    v = 1:6;
    idx = [3 5];
    getLogicalIdx(idx,v)
    

    输出将是:

    ans =
    
         0     0     1     0     1     0
    

    【讨论】:

      【解决方案7】:

      你能简单地做 v(i) =1 吗?

      例如,如果您说 x = zeros(1,10); 和 a = [1 3 4];

      x(a) = 1 将返回 1 0 1 1 0 0 0 0 0 0

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-08-28
        • 2014-04-16
        • 2014-11-05
        • 1970-01-01
        • 1970-01-01
        • 2015-11-29
        相关资源
        最近更新 更多