repeat vector elements and operate on them

1 view (last 30 days)
MatG
MatG on 6 Oct 2021
Commented: MatG on 7 Oct 2021
I have column vectors A = [a1;a2;...;an] and B = [b1;b2;...;bn] where B elements are integer numbers. I want a fast way to generate a vector D such that D = [a1+0 ; a1+1 ; a1+2 ; a1+b1-1 ; a2+0 ; a2+1 ; ...; a2+ b2 -1; ... ; an+0 ; an+1 ; ... ; an+bn-1]. An example is below. So far, I know I can use "repelem(A,B) to make sure matrix C has the right number of rows; howver, I don't know what to do next (whether using C or an entirely new idea).
A=[12; 10; 5];
B=[4;3;2];
C=repelem(A,B);
% I want D=[ 12 ; 13; 14 ; 15 ; 10 ; 11 ; 12 ; 5 ; 6]
VectorA = [[3;4;5]

Accepted Answer

Matt J
Matt J on 6 Oct 2021
Edited: Matt J on 6 Oct 2021
A=[12; 10; 5];
B=[4;3;2];
C=repelem(A-1,B);
T=ones(size(C));
I=B(1:end-1);
T(cumsum(I)+1)=1-I;
D=C+cumsum(T);
D.'
ans = 1×9
12 13 14 15 10 11 12 5 6
  1 Comment
Jan
Jan on 6 Oct 2021
This method is 60 times faster than the arrayfun approach and 10 times faster than the loop. Nice!

Sign in to comment.

More Answers (1)

Jan
Jan on 6 Oct 2021
Edited: Jan on 6 Oct 2021
Start with a simple loop:
A = [12; 10; 5];
B = [4; 3; 2];
RepSum(A, B)
function C = RepSum(A, B)
len = sum(B);
C = zeros(len, 1);
i1 = 1;
for k = 1:numel(A)
i2 = i1 + B(k) - 1;
C(i1:i2) = A(k):A(k) + B(k) - 1;
i1 = i2 + 1;
end
end
And a C-Mex version:
// RepSumX.c, Compile with: mex -O -R2018a RepSumX.c
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mwSize nA, i, j, b;
double *A, *B, *C, v;
nA = mxGetNumberOfElements(prhs[0]);
A = mxGetDoubles(prhs[0]);
B = mxGetDoubles(prhs[1]);
v = 0.0;
for (i = 0; i < nA; v += B[i++]) ;
plhs[0] = mxCreateDoubleMatrix((mwSize) v, 1, mxREAL);
C = mxGetDoubles(plhs[0]);
for (i = 0; i < nA; i++) {
v = A[i];
b = (mwSize) B[i];
while (b--) {
*C++ = v++;
}
}
}
And the timings for 1e6 elements on my R2018b i5 machine:
A = randi([1, 1000], 1e6, 1);
B = randi([1, 20], 1e6, 1);
tic
C1 = RepSum(A,B);
toc
tic
F = @(a, b) a + (0:b-1).';
C2 = cell2mat(arrayfun(F, A, B, 'uni', 0));
toc
tic
C = repelem(A,B);
T = ones(size(C));
T(cumsum(B(1:end-1))+1) = 1 - B(1:end-1);
C3 = C + cumsum(T) - 1;
toc
tic
C4 = RepSumX(A, B);
toc
isequal(C1, C2, C3, C4) % 1
% Elapsed time is 1.234160 seconds. Loop
% Elapsed time is 7.517230 seconds. Arrayfun (Stephen)
% Elapsed time is 0.134246 seconds. cumsum (Matt J)
% Elapsed time is 0.032087 seconds. C-mex
  1 Comment
MatG
MatG on 7 Oct 2021
Thanks @Jan for benchmarking. I'll adjust the accepted answer to the fastest pure MATLAB one by @Matt J. Thanks a lot for mexing.

Sign in to comment.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by