can we check a condition at the end of a loop?

14 views (last 30 days)
Hi everyone.
I am trying to create a new matrix. In each row, it isn't allowed to have two of the same numbers next to each other (more than 1x).
So for this, I created a new empty matrix and then I wanted to create a while loop for each of the rows and input them in the zero matrix.
So far I have something like :
%creates an empty matrix B
B = zeros (6,32);
%creates a row vector with numbers from 1 to 8
rowvector = 1:8;
%creates a vector that copies the rowvector above 4 times
vector = repmat (rowvector, 1, 4);
%creates the first row for vector B
Brow1 = vector (randperm (length(vector)))
%counts how many times the same number is listed twice in a row
count = sum(diff(Brow1)==0 & diff([NaN, Brow1(1:end-1)])~=0)
----here is where I struggle... I want to create a while loop that looks into Brow1 and if count <= 1 then it inputs it into the matrix B, but I am not sure how to go about this. Would you be able to help me with this ?

Accepted Answer

John D'Errico
John D'Errico on 22 Jun 2019
Edited: John D'Errico on 22 Jun 2019
Where you are struggling is in explaining what you want to do. Giving an example is often a good idea. That is, give an example of a row that fails your criteria. Then one that is satisfactory.
As far as counting the number of times an element is listed consecutively in a row, then it would seem satisfactory to just use
count = sum(diff(Brow1)==0);
Even simpler is to just for a logical nagation of the vector.
count = sum(~diff(V));
The secondary clause in your test does not seem to be pertinent. As I wrote it, this just counts the number of events where an element is equal to the element that follows it. As long as that never exceeds 1, then you are ok. However, that is not terribly clear it is what you are asking for, since your question is fairly confusing.
V0 = repmat(1:8,1,4);
V = V0(randperm(32))
V =
Columns 1 through 25
4 3 8 4 5 5 2 6 8 6 6 4 1 1 8 5 3 4 2 3 7 8 7 6 1
Columns 26 through 32
2 7 1 5 3 7 2
sum(~diff(V))
ans =
3
So there are 3 such events in this sample. They are located at elements [5,10,13] with the next element in each case identical.
find(~diff(V))
ans =
5 10 13
Your requirement is that can happen at most once, although zero such events is apprently ok? Is a count of zero such events preferable? BE CLEAR!!!!!
Anyway, it seems to me to be simplest to
nrows = 6;
V0 = repmat(1:8,1,4);
nv = numel(V0);
B = zeros(nrows,nv);
for i = 1:nrows
V = V0(randperm(nv));
loc = find(~diff(V));
while numel(loc) > 1
% For each element of loc that was adjacent to a replicate,
% pick a new random location to swap it to.
newloc = setdiff(1:nv,loc);
newloc = randperm(numel(newloc),numel(loc));
% the swap itself is simple
V([loc,newloc]) = V([newloc,loc]);
% get the new set of problem neighbors, if any remain
loc = find(~diff(V));
end
% when we drop through to here, the vector is now satisfactory.
% save it
B(i,:) = V;
end
In the above loop, you will find the internal while loop actually only runs very few times, because most of the time the swap results in few mishaps on the next pass through the loop.
As well, I could surely have been more intelligent about how I choose a location to swap an element into, but why bother to make something that is already efficient, slightly more so? That at the cost of more complex code? Don't pre-optimize your code unless it is a problem.
B'
ans =
6 5 5 7 1 7
4 4 3 3 5 1
1 8 1 6 7 5
3 2 3 7 1 3
7 3 6 8 3 4
4 6 7 5 6 6
5 2 6 3 3 5
2 5 2 6 5 6
4 1 5 5 6 2
6 7 1 2 2 7
7 7 5 4 4 5
1 1 7 2 2 4
7 7 3 7 7 5
2 1 8 3 7 8
5 5 4 1 4 1
3 8 6 8 5 3
1 6 8 6 3 6
5 4 7 5 4 8
8 8 2 1 1 1
2 5 3 4 6 7
8 2 1 1 8 3
4 3 2 3 4 4
8 1 6 4 8 4
3 6 4 4 5 2
2 4 8 2 2 1
6 7 2 7 6 6
1 6 1 8 2 3
6 8 4 5 8 2
5 3 7 8 1 7
3 2 4 1 7 8
7 3 7 6 3 2
8 4 5 2 8 8
>> sum(~diff(B,[],2),2)
ans =
0
1
0
1
1
1
It would seem the above set should be satisfactory. But I am not at all sure.
  2 Comments
Yoanna Ivanova
Yoanna Ivanova on 22 Jun 2019
Edited: Yoanna Ivanova on 22 Jun 2019
Okay so what I want to have in the end is a 6x32 matrix.
For this my train of thought was that I could create a new empty matrix and then generate a sequence of numbers in each row - from 1 to 8 and they repeat 4 times, so each row has 32 elements in it. I need 6 rows in total. Those rows I could then input into the original matrix.
Then what I wanted to do is to create a new row, and check whether it satisfies my criteria, if yes then to be inserted in the empty matrix B that I've created above. For example, 1 8 2 6 3 4 5 6 7 8...= this is okay 1 1 2 3 4 7 8 3 2 3...= this sequence is also OK 1 1 2 2 3 4 2 5 7 8 = this is not okay because there are two repetitions - 1 and 2 both repeat in the row so it isn't a good sequence and I don't want to input it in the original matrix. I don't want to swap anything - - I just want to create a new random sequence and check whether it satisfies the criteria above.
John D'Errico
John D'Errico on 22 Jun 2019
Edited: John D'Errico on 22 Jun 2019
SIGH. Is there a VALID reason why randomly swapping elements, based on a random sequence is not sufficient? Anyway, nothing stops you from changing the code I gave you to just keep generating new random sequences untill you are happy. The code will be slower, and no better in the end. So again, is there a valid reason for this requirement.
As for the result, you can see the result is indeed a 6x32 matrix. I transposed it at the end there to display it, so it would be readable online.
I gave you a result that is exactly what you want. So what is the problem?

Sign in to comment.

More Answers (1)

Andrei Bobrov
Andrei Bobrov on 22 Jun 2019
Edited: Andrei Bobrov on 22 Jun 2019
B = sort(repmat(1:8,6,4),2);
[m,n] = size(B);
for i0 = 1:m
while any(~diff(B(i0,:)))
B(i0,:) = B(i0,randperm(n));
end
end

Categories

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

Community Treasure Hunt

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

Start Hunting!