问题
In MATLAB I have a vector x of length n, where n is usually O(10), and I would like to build a tall matrix A of size [n^m,m], where m is again 0(10). The matrix has a peculiar form: if n=4 and m=6, let
x=[x1; x2; x3; x4]
then A is
x1 x1 x1 x1 x1 x1
x2 x1 x1 x1 x1 x1
x3 x1 x1 x1 x1 x1
x4 x1 x1 x1 x1 x1
x1 x2 x1 x1 x1 x1
x2 x2 x1 x1 x1 x1
x3 x2 x1 x1 x1 x1
x4 x2 x1 x1 x1 x1
x1 x3 x2 x1 x1 x1
. .
. .
. .
x4 x4 x4 x4 x4 x4
In practice, each column is obtained by repeating the elements of x, with an increasing stride for each column. How can I do that? If possible, I'd prefer an efficient (vectorized) solution, because, as you can see, the number of rows of A increases exponentially with m. Thanks very much,
Sergio
EDIT: whoops, sorry! I forgot I also need to build a vector V of size [n^m,1], based on vector w having the same length of x
w=[w1; w2; w3; w4]
V is
w1^6
w2*w1^5
w3*w1^5
.
.
.
w4^6
Hope the crappy graphics is clear enough. Anyway, V is a column vector of lenght n^m. Guess I could create a matrix B from w, in the same way one creates a matrix A from x, and then use prod(B,2)?
回答1:
Use allcomb tool from MATLAB file-exchange to generate the possible combinations of the indices [1 2 3 4]
and then use them to index into x
-
v = repmat({1:numel(x)},1,m);
A = x(fliplr(allcomb(v{:})));
Also, it seems instead of using fliplr
, you can use - allcomb(v{:},'matlab')
instead.
For the edited part of the question, you can use a modified version of it -
V = prod(x(allcomb(v{:})),2)
Benchmarking
Please note that these are for runnable solutions posted here.
Benchmarking Code
%// Parameters and input x
n = 10; m = 6;num_runs = 20; x = randi(9,n,1);
disp('-------- With allcomb')
tic
for runs = 1:num_runs
v = repmat({1:numel(x)},1,m);
A = x(fliplr(allcomb(v{:})));
end
toc, clear v A
disp('-------- With bsxfun')
tic
for runs = 1:num_runs
A = x(floor(mod(bsxfun(@rdivide, (0:n^m-1).', n.^[0:m-1] ), n)+1)); %//'
end
toc, clear A
disp('-------- With ttable')
tic
for runs = 1:num_runs
I = ttable(n*ones(1,m));
A = x(I);
end
toc, clear I A
disp('-------- With arrayfun')
tic
for runs = 1:num_runs
A = cell2mat(arrayfun(@(i)...
(repmat(reshape(repmat(x',n^(i-1),1),[],1),n^(m-i),1)),1:m,'uni',0));
end
toc
Results
-------- With allcomb
Elapsed time is 6.544981 seconds.
-------- With bsxfun
Elapsed time is 11.547062 seconds.
-------- With ttable
Elapsed time is 15.729932 seconds.
-------- With arrayfun
Elapsed time is 4.319048 seconds.
回答2:
One-liner based only on built-in functions (namely, mod and the very powerful bsxfun):
result = x(floor(mod(bsxfun(@rdivide, (0:n^m-1).', n.^[0:m-1] ), n)+1));
回答3:
Try this: (all are bulit-in functions)
A = cell2mat(arrayfun(@(i)(repmat(reshape(repmat(x',n^(i-1),1),[],1),n^(m-i),1)),1:m,'UniformOutput',0))
Explanation:
n = 2;
m = 4;
x = (1:n)';
A = [];
for i = 1:m
%// temp1 is (n^(i-1)) x n matrix with each row equal to x'
temp1 = repmat(x',n^(i-1),1);
%// temp2 is (n^(i-1))*n x 1 column vector with corresponding elements of temp1
temp2 = reshape(temp1,[],1);
%// temp3 is a (n^(m-i))*(n^(i-1))*n x 1, i.e n^m x 1 column vector with elements of temp2 repeated n^(m-i) times
temp3 = repmat(temp2,n^(m-i),1);
%// A is appending temp3 into its ith column
A = cat(2,A,temp3);
end
For The EDIT part:
You can do what you said i.e do prod(B,2)
where B is a matrix calculated using above code
回答4:
I think the generalized truth table function from the file exchange will help you
try (not tested):
I = ttable(n*ones(1,m));
x(I);
回答5:
Okay, there are tricky one-liner solutions for this. There is, however, a file on matlab file exchanging providing the solution that you are looking at if the order doesn't matter. See here combn. It basically makes use of the same tricks as the one-liner presented by other answers. If the order matters, you might have to do some sorting aftwerwards or adjust the source code directly.
来源:https://stackoverflow.com/questions/25245105/matlab-create-a-large-matrix-by-repeating-elements-of-a-vector-with-increasing