Info

This question is closed. Reopen it to edit or answer.

Random zero-restrictions in vector

1 view (last 30 days)
Christian
Christian on 13 Jan 2015
Closed: MATLAB Answer Bot on 20 Aug 2021
I have the following problem and I would be very grateful for support. Let, for instance,
A=[2 0 1]
and
B=[2 1 1 3 0 3]
where the elements of B refer to the index of A. (Context: B is a vector of workers and the elements indicate at which firm (1, 2 or 3) they are employed. 0 indicates unemployment. A is the vector indicating for each firm how many workers they want to lay off. For instance, firm 1 wants to lay of 2 workers. B is such that there is always enough workers to lay off for each firm.) I would like to create a matrix B1 (matrix of workers after random lay-offs) where for each index (firm) in A the corresponding number in A (the number of lay-offs) is randomly set to 0 in B. In the example it would be
B1=[2 0 0 3 0 0] or
B1=[2 0 0 0 0 3]
It would be great to do that without a for-loop. Is that actually possible? Any help is much appreciated! Thank you! Christian

Answers (4)

Matt J
Matt J on 13 Jan 2015
Edited: Matt J on 13 Jan 2015
No loops here, including the ones hidden inside cellfun, num2cell, arrayfun, etc... I still wonder, though, whether avoiding a for-loop is worthwhile.
nA=length(A); nB=length(B);
S=accumarray([(1:nB).',B.'+1],1);
idx=randperm(nB); iidx(idx)=1:nB;
SS=S(idx,2:end);
C=bsxfun(@gt,cumsum(SS,1),A).*SS;
B1=(C(iidx,:)*(1:nA).').'
  2 Comments
Matt J
Matt J on 13 Jan 2015
Edited: Matt J on 13 Jan 2015
Not sure why. I know bsxfun and accumarray get slower when the user supplies an arbitrary function handle for their FUNC arguments,
A = accumarray(SUBS,VAL,SZ,FUNC)
C = bsxfun(FUNC,A,B)
However, when accumarray is used with its default summation rule and bsxfun is used with its pre-optimized list of bi-operand functions (@plus, @minus, times, etc...) I think they've always been observed to outperform for-loops.

Alfonso Nieto-Castanon
Alfonso Nieto-Castanon on 13 Jan 2015
Edited: Alfonso Nieto-Castanon on 14 Jan 2015
B1 = B;
i = randperm(numel(B));
c = sparse(find(B(i)),nonzeros(B(i)),1);
B1(i(~any(c&cumsum(c,1)>repmat(A,size(c,1),1),2)))=0;
EDIT: cleaned up the code a bit
  1 Comment
Alfonso Nieto-Castanon
Alfonso Nieto-Castanon on 13 Jan 2015
Edited: Alfonso Nieto-Castanon on 14 Jan 2015
this is just a variation of what Matt said before me

Guillaume
Guillaume on 13 Jan 2015
It probably could be done with a loop using accumarray, arrayfun and cellfun, but the following would do and is much clearer. It's arguable that these three functions are not loops, anyway.
%demo data
tofire = [2 0 1 2];
workers = [4 2 1 4 1 3 0 4 3 4];
workersemployed = nonzeros(workers)';
employeecount = histc(workersemployed, unique(workersemployed));
for companyidx = 1:numel(tofire)
employees = find(ismember(workers, companyidx));
fired = randperm(employeecount(companyidx), tofire(companyidx));
workers(employees(fired)) = 0;
end

Andrei Bobrov
Andrei Bobrov on 13 Jan 2015
Edited: Andrei Bobrov on 13 Jan 2015
B1 = B;
ii = B~=0;
C = B(ii);
D = accumarray(C(:),(1:numel(C))',[],@(x){ x(randperm(numel(x))) });
x = cellfun(@(x,y)reshape(x(1:y),[],1),D,num2cell(A(:)),'un',0)
C(cat(1,x{:})) = 0;
B1(ii) = C;
with for..end loop
B1 = B;
for ii = 1:numel(A)
jj = find(ii == B);
B1(jj(randperm(numel(jj),A(ii)))) = 0;
end

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by