19

I have a vector, e.g.

vector = [1 2 3]

I would like to duplicate it within itself n times, i.e. if n = 3, it would end up as:

vector = [1 2 3 1 2 3 1 2 3]

How can I achieve this for any value of n? I know I could do the following:

newvector = vector;
for i = 1 : n-1
    newvector = [newvector vector];
end

This seems a little cumbersome though. Any more efficient methods?

CaptainProg
  • 5,610
  • 23
  • 71
  • 116

3 Answers3

39

Try

repmat([1 2 3],1,3)

I'll leave you to check the documentation for repmat.

High Performance Mark
  • 77,191
  • 7
  • 105
  • 161
20

This is a Faster Method Than repmat or reshape by an Order of Magnitude

One of the best methods for doing such things is Using Tony's Trick. Repmat and Reshape are usually found to be slower than Tony's trick as it directly uses Matlabs inherent indexing. To answer you question,

Lets say, you want to tile the row vector r=[1 2 3] N times like r=[1 2 3 1 2 3 1 2 3...], then,

c=r'
cc=c(:,ones(N,1));
r_tiled = cc(:)';

This method has significant time savings against reshape or repmat for large N's.

EDIT : Reply to @Li-aung Yip's doubts

I conducted a small Matlab test to check the speed differential between repmat and tony's trick. Using the code mentioned below, I calculated the times for constructing the same tiled vector from a base vector A=[1:N]. The results show that YES, Tony's-Trick is FASTER BY AN ORDER of MAGNITUDE, especially for larger N. People are welcome to try it themselves. This much time differential can be critical if such an operation has to be performed in loops. Here is the small script I used;

N= 10 ;% ASLO Try for values N= 10, 100, 1000, 10000

% time for tony_trick
tic;
A=(1:N)';
B=A(:,ones(N,1));
C=B(:)';
t_tony=toc;
clearvars -except t_tony N

% time for repmat
tic;
A=(1:N);
B=repmat(A,1,N);
t_repmat=toc;
clearvars -except t_tony t_repmat N

The Times (in seconds) for both methods are given below;

  • N=10, time_repmat = 8e-5 , time_tony = 3e-5
  • N=100, time_repmat = 2.9e-4 , time_tony = 6e-5
  • N=1000, time_repmat = 0.0302 , time_tony = 0.0058
  • N=10000, time_repmat = 2.9199 , time_tony = 0.5292

My RAM didn't permit me to go beyond N=10000. I am sure, the time difference between the two methods will be even more significant for N=100000. I know, these times might be different for different machines, but the relative difference in order-of-magnitude of times will stand. Also, I know, the avg of times could have been a better metric, but I just wanted to show the order of magnitude difference in time consumption between the two approaches. My machine/os details are given below :

Relevant Machine/OS/Matlab Details : Athlon i686 Arch, Ubuntu 11.04 32 bit, 3gb ram, Matlab 2011b

Abhinav
  • 1,882
  • 1
  • 18
  • 34
  • 1
    Firstly; What?! Secondly, one wonders why `repmat()` is slower than doing it 'manually'. Thirdly, anyone who uses this better put a comment next to it... – Li-aung Yip Apr 26 '12 at 05:37
  • 1
    After some [research into Tony's Trick](http://www.mathworks.com/matlabcentral/newsreader/view_thread/261716), it appears that it was faster at the time of writing - *14 years ago*. MATLAB has improved a lot since then, and Tony's Trick may not be faster than `repmat` any more. (You should write a benchmark and test this. ;) ) – Li-aung Yip Apr 26 '12 at 06:16
  • Will do, thanks for the links. – Abhinav Apr 26 '12 at 06:25
  • @Li-aungYip Yes, the post date on math forums is 13 Dec 2010. Also, replying to your earlier comment : C was written 40 years ago, this doesnt mean it is slower than Matlab. Don't rely on opinions/contemporary-conventional wisdoms to make objective decisions. – Abhinav Apr 26 '12 at 08:38
  • 5
    Excellent answer: a statement about program performance backed up by evidence. I see lots of assertions about program performance here on SO, very few backed up by evidence. I can't see myself using Tony's Trick regularly but then I don't use repmat much, and now I know where to look if I ever find repmat too slow. – High Performance Mark Apr 26 '12 at 08:42
  • 1
    **UPDATE FOR 2018** I replicated the above experiment (do put your code to be timed in a function M-file!), and got consistently faster results with `repmat`. That said, even `repmat` is outdated, we have had `bsxfun` for over a decade, and now have implicit singleton expansion. Legitimate uses of `repmat` are very few and far between nowadays. – Cris Luengo Jul 19 '18 at 05:40
4

Based on Abhinav's answer and some tests, I wrote a function which is ALWAYS faster than repmat()!

It uses the same parameters, except for the first parameter which must be a vector and not a matrix.

function vec = repvec( vec, rows, cols )
%REPVEC Replicates a vector.
%   Replicates a vector rows times in dim1 and cols times in dim2.
%   Auto optimization included.
%   Faster than repmat()!!!
%   
%   Copyright 2012 by Marcel Schnirring

    if ~isscalar(rows) || ~isscalar(cols)
        error('Rows and cols must be scaler')
    end

    if rows == 1 && cols == 1
        return  % no modification needed
    end

    % check parameters
    if size(vec,1) ~= 1 && size(vec,2) ~= 1
        error('First parameter must be a vector but is a matrix or array')
    end

    % check type of vector (row/column vector)
    if size(vec,1) == 1
        % set flag
        isrowvec = 1;
        % swap rows and cols
        tmp = rows;
        rows = cols;
        cols = tmp;
    else
        % set flag
        isrowvec = 0;
    end

    % optimize code -> choose version
    if rows == 1
        version = 2;
    else
        version = 1;
    end

    % run replication
    if version == 1
        if isrowvec
            % transform vector
            vec = vec';
        end

        % replicate rows
        if rows > 1
            cc = vec(:,ones(1,rows));
            vec = cc(:);
            %indices = 1:length(vec);
            %c = indices';
            %cc = c(:,ones(rows,1));
            %indices = cc(:);
            %vec = vec(indices);
        end

        % replicate columns
        if cols > 1
            %vec = vec(:,ones(1,cols));
            indices = (1:length(vec))';
            indices = indices(:,ones(1,cols));
            vec = vec(indices);
        end

        if isrowvec
            % transform vector back
            vec = vec';
        end
    elseif version == 2
        % calculate indices
        indices = (1:length(vec))';

        % replicate rows
        if rows > 1
            c = indices(:,ones(rows,1));
            indices = c(:);
        end

        % replicate columns
        if cols > 1
            indices = indices(:,ones(1,cols));
        end

        % transform index when row vector
        if isrowvec
            indices = indices';
        end

        % get vector based on indices
        vec = vec(indices);
    end
end

Feel free to test the function with all your data and give me feedback. When you found something to even improve it, please tell me.