repeat vector elements and operate on them

3 views (last 30 days)
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
Edited: 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.

Categories

Find more on Creating and Concatenating Matrices in Help Center and File Exchange

Community Treasure Hunt

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

Start Hunting!