2

I've a matrix of order 100*5 . Now the objective is to fill each columns of the matrix with random integer within a specific range. Now the problem is for every column the range of the random number changes. For instance, for the first column, the range is 1 to 100 , for the second its -10 to 1 and so on till 5th column.

This is what I've tried:

b = [0,100;-10,1;0,1;-1,1;10,20]
range = b(:,2) - b(:,1) 
offset = b(:,1)
A = round(rand(100,5) * range - offset)

which is from this question. However this generates an error,

Error using * Inner matrix dimensions must agree.

What's possibly causing this and how to resolve it ?

Ander Biguri
  • 35,140
  • 11
  • 74
  • 120
OBX
  • 6,044
  • 7
  • 33
  • 77
  • Avoid using `range` as a variable name, as it's a [function name](http://www.mathworks.com/help/stats/range.html?s_tid=doc_ta) – EBH Aug 09 '17 at 10:08
  • @EBH Even after changing the var name, the same error persis – OBX Aug 09 '17 at 10:09
  • This is not the cause for the error, just a good habit. – EBH Aug 09 '17 at 10:10

4 Answers4

4

lets bsxfun this thing!

A = round(bsxfun(@minus,bsxfun(@times,rand(100,5) ,range'), offset'))
Ander Biguri
  • 35,140
  • 11
  • 74
  • 120
3

As an alternative solution, you could use repmat to complete what you already had:

b = [0, 100; -10, 1; 0, 1; -1, 1; 10, 20].'; 
rng = b(2, :) - b(1, :); 
ofst = b(1, :);
A = round(rand(100,5) .* repmat(rng, 100, 1) + repmat(ofst, 100, 1));

You don't have to define rng or ofst, and this can be simply written as:

A = round(rand(10,5) .* repmat(diff(b), 10, 1) + repmat(b(1,:), 10, 1));

Out of curiousity I wrote this quick benchmark* to compare to Ander's bsxfun method. It appears that bsxfun has some initial overhead which means for 5 columns (test other cases yourself) and less than a few thousand rows, repmat is quicker. Above this, the creation of additional large arrays by repmat probably causes a slow down, and we see bsxfun is much quicker.

small vals large vals

For future readers if this doesn't apply to you: with broadcasting introduced from R2016b you may find you can dodge using bsxfun and repmat entirely.

*benchmarking code. Tested on Windows 64-bit R2015b, your mileage may vary etc.

function benchie()
    b = [0, 100; -10, 1; 0, 1; -1, 1; 10, 20].';
    Tb = [];
    Tr = [];
    K = 20;
    for k = 1:K
        n = 2^k;
        fb = @()bsxfunMethod(b,n);
        fr = @()repmatMethod(b,n);
        Tb(end+1) = timeit(fb);
        Tr(end+1) = timeit(fr);
    end
    figure; plot(2.^(1:K), Tb, 2.^(1:K), Tr); legend('bsxfun', 'repmat');
end
function bsxfunMethod(b, n)
    round(bsxfun(@minus,bsxfun(@times, rand(n,5), diff(b)), b(1,:)));
end
function repmatMethod(b, n)
    round(rand(n,5) .* repmat(diff(b), n, 1) + repmat(b(1,:), n, 1));
end
Wolfie
  • 27,562
  • 7
  • 28
  • 55
  • The result that `repmat` is quicker than `bsxfun` is [somewhat surprising](https://stackoverflow.com/questions/29719674/comparing-bsxfun-and-repmat). – EBH Aug 13 '17 at 06:29
  • Thanks for the link, can you replicate my results on your own machine? I feel like the benchmark above is reasonable, but of course subtle differences to the use cases in that question may affect things! As I speculate, there may be some initial `bsxfun` overhead since it is much quicker for large arrays – Wolfie Aug 13 '17 at 08:17
  • When I run this for the range 1-10000 rows, I get a [noisy pattern](https://i.stack.imgur.com/q2ym9.png), with a general advantage for `repmat`. When I run it for 2-2^20 range, I get the same result as yours. – EBH Aug 13 '17 at 09:47
  • Hmm I wonder where that noise comes from, are you using a version with the newer JIT compiler? – Wolfie Aug 13 '17 at 09:56
  • I use MATLAB 2017a. – EBH Aug 13 '17 at 13:41
2

You can do this with randi, passing in rows of b to its first argument:

b = [0,100;-10,1;0,1;-1,1;10,20];
A = zeros(100,5);
f=@(ii)randi(b(ii,:),100,1);
for ii = 1:size(A,2)
  A(:,ii) = f(ii);
end

I suspect there is a way of doing this without looping through rows/columns, probably with bsxfun.

Steve
  • 1,579
  • 10
  • 23
2

You can use arrayfun, even though I don't see any harm in using loops and writing more readable code as in Steve's answer.

A = cell2mat(arrayfun(@(imin, imax) randi([imin, imax], 100, 1), b(:,1), b(:,2), 'uni', 0)')
Mohsen Nosratinia
  • 9,844
  • 1
  • 27
  • 52