repeat vector elements and operate on them

3 vues (au cours des 30 derniers jours)
MatG
MatG le 6 Oct 2021
Commenté : MatG le 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]

Réponse acceptée

Matt J
Matt J le 6 Oct 2021
Modifié(e) : Matt J le 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 commentaire
Jan
Jan le 6 Oct 2021
Modifié(e) : Jan le 6 Oct 2021
This method is 60 times faster than the arrayfun approach and 10 times faster than the loop. Nice!

Connectez-vous pour commenter.

Plus de réponses (1)

Jan
Jan le 6 Oct 2021
Modifié(e) : Jan le 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 commentaire
MatG
MatG le 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.

Connectez-vous pour commenter.

Catégories

En savoir plus sur Creating and Concatenating Matrices dans Help Center et File Exchange

Community Treasure Hunt

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

Start Hunting!

Translated by