Here what I would like to achieve:
I have a matrix C
C=[1 2 3; 4 5 6; 7 8 9];
And a vector a
a=[1 2];
I would like to make such an operation, that each element of the a
vector is multiplied with C
( scalar multiplication ) and out comes a 3-dimensional array D
:
(:,:,1) =
1 2 3
4 5 6
7 8 9
(:,:,2) =
2 4 6
8 10 12
14 16 18
It would certainly work with a loop, but, since I'll need this operation on multiple occasions, a oneliner would be a great saver.
This is a beautiful example for the use of bsxfun
and reshape
. While @thewaywewalks proposes first calling bsxfun
and reshaping the result, I'd suggest the opposite. This makes one of the key concepts of bsxfun
- the singleton dimension expansion - more clear:
out = bsxfun(@times,C,reshape(a,1,1,[]))
ans(:,:,1) =
1 2 3
4 5 6
7 8 9
ans(:,:,2) =
2 4 6
8 10 12
14 16 18
With reshape(a,1,1,[])
, you make a
be in the third dimension. If you now apply bsxfun
, it will multiply the matrix C
with each element of a
.
Some reshape
'ing and some bsxfun
will do:
out = reshape(bsxfun(@mtimes, C(:), a(:).'), [size(C),numel(a)] )
As suggested in hbaderts answer one could also use bsxfun
's capability of dimension expansion, and provide a permuted vector of factors:
out = bsxfun(@mtimes,C,permute(a,[3,1,2]))
out(:,:,1) =
1 2 3
4 5 6
7 8 9
out(:,:,2) =
2 4 6
8 10 12
14 16 18
I have another method for the benchmark compare... IMO it's the neatest way, at least for the syntax/readability term:
out = reshape(kron(a,C),[size(C),numel(a)]);
out(:,:,1) =
1 2 3
4 5 6
7 8 9
out(:,:,2) =
2 4 6
8 10 12
14 16 18
Another possibility is to use matrix multiplication of C
as a column vector times a
as a row vector (this gives all element-wise products), and then reshape
the result:
out = reshape(C(:)*a, size(C,1), size(C,2), numel(a));
EDIT (BENCHMARKING): Since several solutions (including mine below) have been suggested, here is some rough benchmarking to compare the different solutions, using larger arrays:
a=1:10;
N=1000; timers=zeros(N,6);
for ii=1:N; C=rand(400);
tic; out = repmat(C,[1,1,numel(a)]).*reshape(repelem(a,size(C,1),size(C,2)),[size(C),numel(a)]); timers(ii,1)=toc;
tic; out = bsxfun(@times,C,reshape(a,1,1,[])); timers(ii,2)=toc;
tic; out = reshape(C(:)*a, size(C,1), size(C,2), numel(a)); timers(ii,3)=toc;
tic; out = bsxfun(@mtimes,C,permute(a,[3,1,2])); timers(ii,4)=toc;
tic; out = reshape(bsxfun(@mtimes, C(:), a(:).'), [size(C),numel(a)] ); timers(ii,5)=toc;
tic; out = reshape(kron(a,C),[size(C),numel(a)]); timers(ii,6)=toc;
end;
mean(timers)
ans =
0.0080863 0.0032406 0.0041718 0.015166 0.0074462 0.0033051
... suggesting that @hbaderts solution is fastest, then @Adiel's, then @Luis Mendo's, then @thewaywewalk's (1), then mine, then @thewaywewalk's (2).
My solution:
Another option, using repmat
and reshape
(no bsxfun
):
out = repmat(C,[1,1,numel(a)]).*reshape(repelem(a,size(C,1),size(C,2)),[size(C),numel(a)])
out(:,:,1) =
1 2 3
4 5 6
7 8 9
out(:,:,2) =
2 4 6
8 10 12
14 16 18
This is the element-wise multiplication of two arrays. The first is your original matrix C
repeated numel(a)
times in the third dimension:
repmat(C,[1,1,numel(a)])
ans(:,:,1) =
1 2 3
4 5 6
7 8 9
ans(:,:,2) =
1 2 3
4 5 6
7 8 9
The second is the same size as the first, with each slice containing the corresponding element of a
:
reshape(repelem(a,size(C,1),size(C,2)),[size(C),numel(a)])
ans(:,:,1) =
1 1 1
1 1 1
1 1 1
ans(:,:,2) =
2 2 2
2 2 2
2 2 2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With