choose one out of every 8 elements randomly

I have a 1xm matrix and I want to choose one out of every 8 elements randomly, I can think of ways using for loops but I was hoping for a solution without for loops and efficient in terms of speed. Thanks

 Accepted Answer

goerk
goerk on 8 Jan 2016
Edited: goerk on 8 Jan 2016
m = 21;
A = (1:m)+0.314;
nof8 = ceil(m/8);
randSelect = randi([1 8],1,nof8);
inds = randSelect+(0:nof8-1)*8;
% if m is not a multiple of 8 the last samples must get special treatment
% the simplest solution is to limit the indices in inds to the m
inds(end)=min(inds(end),m);
% but this leads to a non-uniform distribution for the last selection.
selectedElements = A(inds)
A solution with an uniform distribution for all cases was suggested from Guillaume:
randselect = randi(8, 1, floor(numel(A)/8));
if mod(numel(A), 8) > 0
randselect = [randselect, randi(mod(numel(A), 8))];
end
selectedvalues = A(randselect + (0:ceil(numel(A)/8)-1)*8)

11 Comments

Cheers exactly what I needed! Kindly can you please explain what this line is doing since I'm not understanding its function..I'm still getting the required results without it. Thanks again
inds(end)=min(inds(end),m);
In the vector inds there are the indices of the selected elements. If m is not a multiple of 8, it is possible that the last index is greater than m (the length of your input signal). In this case the last index in inds is set to m (that is the last element of your input signal).
It is the simplest way to deal with vectors with a length different to x*8.
I couldn't resist the urge to create this one-liner:
selectedElements = A(arrayfun(@(k) randi([k min(k+8-1,number(A))],1),1:8:number(A)))
The solution may not have the problem of reshaping (is it a problem?), but it has the problem that the selection of the last block is strongly non-uniform. If there are only two elements in the last block, you have a 7/8 chance of selecting the 2nd.
a better solution would be:
randselect = randi(8, 1, floor(numel(A)/8));
if mod(numel(A), 8) > 0
randselect = [randselect, randi(mod(numel(A), 8))];
end
selectedvalues = A(randselect + (0:ceil(numel(A)/8)-1)*8)
@Jos: I like it! A one-liner is always very pretty, but difficult to read and understand.
@Guillaume: That's right, but as i have noted it is the simplest way. It may be non-uniform but it is random. The perfect solution for the problem with the incomplete last 'subarray' depends on the purpose of the algorithm. In addition I don't want to solve all possible problems that my occur, my goal is it to give a quick idea of one possible solution for the problem. So that the reader can implement the specific solution for his problem.
@Guillaume: Reshaping is only a 'problem' if m is not a multiple of 8. The term problem in my first answer is a little bit strong. I wanted to state that this solution don't need an assert for this case.
@Goerk: Yes, reshaping is a problem if m is not a multiple of 8, but as you pointed out, we don't have enough information from the OP to know if it is. However, if it is, I would think that a bias for the last value of the last block would also be a problem.
I think it is important to warn the OP of that bias as they may not have realised it. Worse than a solution than does not solve the problem is a solution that appears to solve the problem but fails on some edge cases.
Yes that's true and therefor thanks for the hint. I will add a comment to the initial code snippet to clarify that.
bug bug
bug bug on 9 Jan 2016
Edited: bug bug on 9 Jan 2016
Hi All sorry for my late reply but for the required problem m will always be divisible by 8 (therefore I will not have any bias problems):) I really appreciate all your help..all solutions were good I just chose the one which seemed most easy given my programming experience
@goerk is it possible to generalise the first section of code you gave me from which i can then choose either 1 every 8, 2 every 8 elements or 3 every 8 elements?
Select multiple (unique) Samples:
m = 21;
A = (1:m)+0.314;
nof8 = floor(m/8); %floor-> if m ~= x*8 ignore last samples
nofSamples = 2;
randSelect = zeros(nofSamples,nof8);
for i=1:nof8
randSelect(:,i)= randperm(8,nofSamples);
end
inds = randSelect+repmat((0:nof8-1)*8,nofSamples,1);
selectedElements = A(inds)
Since the number of elements is always multiple of 8, this is a case where using reshape is a lot more elegant.
A = 1:104;
nofsamples = 3;
A8 = reshape(A, 8, []);
selectedelements = A8(sub2ind(size(A8), randperm(8, nofsamples), 1:size(m8, 2)))
%if required as a vector:
selectedelements = selectedelements(:)

Sign in to comment.

More Answers (2)

One possible way:
m = 1:104; %input
assert(mod(numel(m), 8) == 0, 'the number of elements in m must be a multiple of 8');
m8 = reshape(m, 8, []);
m8(sub2ind(size(m8), randi(8, 1, size(m8, 2)), 1:size(m8, 2)))
m = 100;
spacedIds = [0:8:m];
rands = randi(8, 1, size(spacedIds,2)-1);
randIds = spacedIds(1:end-1)+ rands;
% check if m is not a multiple of 8
% add another number from the remaining tail-section
if ~(spacedIds(end) == m)
randIds(end+1) = spacedIds(end) + randi(m-spacedIds(end), 1,1);
end

Categories

Find more on Loops and Conditional Statements in Help Center and File Exchange

Asked:

on 8 Jan 2016

Edited:

on 12 Jan 2016

Community Treasure Hunt

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

Start Hunting!