2
votes

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 ?

4
Avoid using range as a variable name, as it's a function nameEBH
@EBH Even after changing the var name, the same error persisOBX
This is not the cause for the error, just a good habit.EBH

4 Answers

4
votes

lets bsxfun this thing!

A = round(bsxfun(@minus,bsxfun(@times,rand(100,5) ,range'), offset'))
3
votes

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
2
votes

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)')
2
votes

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.