user2945166
user2945166

Reputation: 83

generate vector with incremental values in matlab

Given an integer n, I would like to get the vector as follows. E.g. if n=3, then vector should be [1,1,2,1,2,3] and if n=4, then vector should be [1,1,2,1,2,3,1,2,3,4], and so on. Is there a simple way to do it? Thanks.

Upvotes: 4

Views: 325

Answers (4)

obchardon
obchardon

Reputation: 10792

Using a sparse matrix we can do:

p      = 1:4;
x      = cumsum(sparse(1,cumsum(p)+1,-p)+1);
x(end) = []

Result:

x =

   1   1   2   1   2   3   1   2   3   4

The biggest advantage of this method is that it also work with unordered sequence length:

p      = [2 3 2 4];
x      = cumsum(sparse(1,cumsum(p)+1,-p)+1);
x(end) = []

Result:

x =

   1   2   1   2   3   1   2   1   2   3   4

Upvotes: 0

Dennis Jaheruddin
Dennis Jaheruddin

Reputation: 21563

Here are three other methods. I expect them to be more memory efficient than the vectorized solution by @A. Donda or @Mohsen, but of course for n = 1000 or less that is not likely a concern.

The most interesting part is the speed difference between the two methods, the second and third can also work for n=10000 in under a second but the first is much slower!

n = 1000;

% Slow vector growing
tic
v=[];
for t = 1:n
   v = [v 1:t];
end
toc % 0.6 sec


tic
% Fast vector growing
v=[];
for t = 1:n
    x = 1:t;
    v(end+x) = x;
end
toc % 0.01 sec

tic
% Preallocated loop
v=zeros(n*(n+1)/2,1);
count = 0;
for t = 1:n
    x = 1:t;
    v(count+x) = x;
    count = count+t;
end
toc % 0.01 sec as well

Upvotes: 1

Mohsen Nosratinia
Mohsen Nosratinia

Reputation: 9864

You get a fast solution using this

w=ones(1,n*(n+1)/2);
w(1+cumsum(1:n-1))=-(0:n-2);
w=cumsum(w);

On my machine, Dennis's solution is fastest for n<=8, but after that this method is the fastest.

Using this benchmarking code:

n=2000;
N=1e6/n;

tic
for k=1:N
    A = repmat((1 : n)', 1, n);
    A = A(triu(ones(n)) ~= 0);
end
toc


tic
for k=1:N
    w=ones(1,n*(n+1)/2);
    w(1+cumsum(1:n-1))=-(0:n-2);
    w=cumsum(w);
end
toc

tic
for k=1:N
    %// Fast vector growing
    v=[];
    for t = 1:n
        x = 1:t;
        v(end+x) = x;
    end
end
toc 

For n=4

Elapsed time is 5.688693 seconds.
Elapsed time is 3.576366 seconds.
Elapsed time is 1.878887 seconds.

For n=8

Elapsed time is 2.985184 seconds.
Elapsed time is 1.851967 seconds.
Elapsed time is 1.834574 seconds.

For n=100

Elapsed time is 1.084404 seconds.
Elapsed time is 0.352033 seconds.
Elapsed time is 2.502724 seconds.

For n=1000

Elapsed time is 15.625361 seconds.
Elapsed time is 3.949131 seconds.
Elapsed time is 11.497764 seconds.

For n=2000

Elapsed time is 29.940548 seconds.
Elapsed time is 7.649394 seconds.
Elapsed time is 22.846367 seconds.

Upvotes: 3

A. Donda
A. Donda

Reputation: 8467

Here's a solution that uses the "upper triangular matrix" function to select the needed entries from a simple repetition of the full sequence:

A = triu(repmat((1 : n)', 1, n));
A = A(A ~= 0)'

triu sets everything below the diagonal to zero. This approach only works if the original sequence doesn't include zeros, which of course is given for 1 : n. Still, a better solution might be to use the result of triu only for indexing:

A = repmat((1 : n)', 1, n);
A = A(triu(ones(n)) ~= 0);

This is pretty fast, below 1 second runtime for n = 10,000 on my machine.

As Dennis Jaheruddin pointed out, this approach needs a bit less than twice as much memory temporarily than is needed for the result (the precise factor is 2 n / (n + 1)).

Upvotes: 2

Related Questions