【问题标题】:Create an index vector based on start and stop values [duplicate]根据开始和停止值创建索引向量[重复]
【发布时间】:2017-01-18 16:33:54
【问题描述】:

基于包含索引间隔的开始(第一列)和结束(第二列)的几行的矩阵,我想创建一个所有索引的向量。比如A = [2 4; 8 11 ; 12 16],我想要下面的向量index = [2 3 4 8 9 10 11 12 13 14 15 16]

我正在寻找最快的方法来做到这一点。目前,我发现只有两种可能:

1) 带循环

index = [];
for n = 1:size(A, 1)
    index = [index A(n, 1):A(n, 2)];
end

2) 与arrayfun

index = cell2mat(arrayfun(@(n) A(n, 1):A(n, 2), 1:size(A, 1), 'uni', 0));

有趣的是,arrayfun 比循环版本快得多,我不知道为什么。另外,我使用从单元格到垫子的转换,所以这很奇怪。你怎么看?您还有其他建议吗?

感谢您的帮助

【问题讨论】:

  • 第一句已被删除!大家好 ;) 对不起,我弄错了,向量索引将等于 [2 3 4 8 9 10 11 12 13 14 15 16]
  • 第一句被删除了,因为它在你的问题中构成噪音,称呼是不必要的。您可以(并且应该)使用下面的edit 链接编辑您的问题。 arrayfun 只是一个循环的包装器,它更快的唯一原因是您未能在循环版本中进行预分配,因此在每次迭代中都会进行一些(缓慢的)内存分配。
  • @Divakar 您重复的答案似乎没有给出正确的结果
  • @thewaywewalk 啊,谢谢,确实是一个错误!现在应该修好了。
  • 欢迎来到 StackOverflow!请考虑通过单击左侧的绿色复选标记来接受其中一个答案,以指示您的问题已解决的系统。谢谢!

标签: matlab loops vectorization


【解决方案1】:

很难说有多快,至少没有循环:

A = [1,3;11,13;31,33;41,42;51,54;55,57;71,72];

%// prepare A
A = A.';

%// create index matrix
idx = bsxfun(@plus, A, [0; 1]);
%// special case: 54 and 55 are basically superfluous
%// need to be removed, but 71 and 72 shouldn't
A = A(:); 
dA = diff(A); dA(1:2:end) = 0;
idx = idx(~( [0;dA] == 1 | [dA;0] == 1 ));

%// create mask
mask = zeros(max(A),1);
mask(idx(:)) = (-1).^(0:numel(idx)-1);

%// index vector
out = find(cumsum(mask))

out.' =

      1  2  3 11 12 13 31 32 33 41 42 51 52 53 54 55 56 57 71 72

【讨论】:

  • 非常好!写idx = [A(1,:); A(2,:)+1] 不是比调用bsxfun 更简单/更快吗?另外你在infex 中有一个小错字,out 是一个列向量。
  • @EBH 我会说idx = [A(1,:); A(2,:)+1] 更慢,但更简单 - 可能。 out 是一个列向量,没错,我只是为了显示目的而转置了它。
  • 您的解决方案会产生部分结果,请使用 A =[1, 3;11,15;21,25;31,33;41,48; 进行测试51,54;61,67;71,72;81,82;91,94];
  • @thewaywewalk 似乎可行!.添加到基准中
【解决方案2】:

这里是一组方法:

方法一来自https://stackoverflow.com/a/39422485/6579744

lo = A(:,1);
up=A(:,2);
index=cumsum(accumarray(cumsum([1;up(:)-lo(:)+1]),[lo(:);0]-[0;up(:)]-1)+1);
index= index(1:end-1);

方法 2: 这是来自 https://stackoverflow.com/a/38507276/6579744 。我也提供了相同的答案,但因为 Divakar 的答案在我之前是他的(修改后的)答案首选:

start_idx = A(:,1)';
end_idx = A(:,2)';
lens = end_idx - start_idx + 1;
shift_idx = cumsum(lens(1:end-1))+1;
id_arr = ones(1,sum(lens));
id_arr([1 shift_idx]) = [start_idx(1) start_idx(2:end) - end_idx(1:end-1)];
index = cumsum(id_arr);

方法3:这是我的

N = A(:,2) - A(:,1) +1;
s=cumsum([ 1; N]);
index=(1:s(end)-1) -repelem(s(1:end-1),N) + repelem(A(:,1),N);
%octave    index=(1:s(end)-1) -repelems(s(1:end-1),[1:numel(N);N']) + repelems(A(:,1),[1:numel(N);N']);

方法4:来自thewaywewalk这个帖子的另一个naswer

A = A.';
idx = bsxfun(@plus, A, [0; 1]);
A = A(:); 
dA = diff(A); dA(1:2:end) = 0;
idx = idx(~( [0;dA] == 1 | [dA;0] == 1 ));
mask = zeros(max(A),1);
mask(idx(:)) = (-1).^(0:numel(idx)-1);
index = find(cumsum(mask));

方法5你的第二种方法:

index = cell2mat(arrayfun(@(n) A(n, 1):A(n, 2), 1:size(A, 1), 'uni', 0));

方法6来自https://stackoverflow.com/a/39423102/6579744

sz= size(A, 1);
index_c = cell(1,sz);
for n = 1:sz
    index_c{n} = [A(n, 1):A(n, 2)];
end
index = cell2mat(index_c);

方法 7 仅适用于 Octave:

idx = 1:size(A ,1);
index_a =bsxfun(@(a,b) (a(b):A (b,2))',A (:,1),idx);
index = index_a(index_a ~= 0); 

方法8你的第一种方法:

index = [];
for n = 1:size(A, 1)
    index = [index A(n, 1):A(n, 2)];
end

测试数据:

i= 1:500:10000000;
j= i+randi([1 490],1, numel(i));
A = [i', j'];

结果在 Octave 中测试,在 Matlab 中可能不同

method1: 0.077063 seconds
method2: 0.094579 seconds
method3: 0.145004 seconds
method4: 0.180826 seconds
method5: 0.317095 seconds
method6: 0.339425 seconds
method7: 3.242287 seconds
method8: doesn't complete in 15 seconds

bechmark 中使用的代码在Online Demo

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2020-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-19
  • 2014-11-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多