How can I create a vector with constrains on Matlab?

2 次查看(过去 30 天)
I have been working on this for two days and I'm becoming crazy.
I need to create several different vectors where 1 is repeated 46 times, 2 is repeated 40 times and 3 is repeated 45 times. 1 2 and 3 have to alternate with constrains:
  1. no repetition of 2 identical numbers one after the other
  2. between two equal numbers (1 or 2) there has to be a 3
How can I do it? Thank you very much
  3 个评论
Miriam Acquafredda
Miriam Acquafredda 2020-7-23
I need to simulate a perceptual alternance of stimuli with physical alternation, so I need to replicate the "real" conditions.
Miriam Acquafredda
Miriam Acquafredda 2020-7-23
Yes Hayden this seems a good idea! Thanks! However I don't know how to insert the 2s without replacing the existing values. Any suggestion? Thanks a lot

请先登录,再进行评论。

回答(5 个)

KSSV
KSSV 2020-7-23
编辑:KSSV 2020-7-23
A = ones(1,46) ;
B = 2*ones(1,40) ;
C = 3*ones(1,45) ;
iwant = [A B C] ; N = length(iwant) ;
% randomise the vector
idx = randperm(N) ;
iwant = iwant(idx) ;
% check if any number repeating
Now from the vector iwant, find the pattern [1 1], [2 2], [3 3] ..get those positions. to get this use:
Once you got the indices, you can insert the number 2 or 3 for repeating [1 1], etc using:
  1 个评论
Miriam Acquafredda
Miriam Acquafredda 2020-7-23
Thank you very much this is a good solution, I just can't seem to keep the quantities (46,40,45) after I insert the numbers in the repetitions.

请先登录,再进行评论。


Hayden Garmon
Hayden Garmon 2020-7-23
Does this work for you?
% b=combnk(1:46,40); if you want to make many vectors, run a for loop with rows from this
b=1:40; % example trial
a=[];
for i=1:46
a=[a,1];
if find(find(b(1,:)==i))==1
a=[a,2];
end
a=[a,3];
end
FINAL=a(1:end-1)

Clemens Gersch
Clemens Gersch 2020-7-23
I can give you a starting point:
nOnes = 1;
nTwos = 2;
nThrees = 2;
% Create permutations.
Elements = [repmat(1, 1, nOnes), repmat(2, 1, nTwos), repmat(3, 1, nThrees)];
PermsPossible = perms(Elements);
PermsPossible = unique(PermsPossible,'rows');
Of course you have to change the first three lines. What you get are all the possible combinations of a sequence of numbers that satisfies that the amount of each number is as you speciefied it in the first three lines of code
What is left to do is filtering out the invalid solutions according to your constraints.
Therefore you can use strfind to check for tuples and triples, but you probably need to proceed row by row.
  1 个评论
Clemens Gersch
Clemens Gersch 2020-7-24
Another approach could be to start with the set of sequence of length 1 ([1;2;3]), then for each of the three sequences of length 1 form all allowed sequences of length 2 and so on.
That would mean: One loop over the length of the sequences (2:46+40+45), one nested loop over the number of sequences you have at the beginning of each loop. In the inner loop you check for each of the three numbers if it can be added to form a valid sequence.
As conditions you have then not only the 2 conditions you stated but also the three conditions on the numbers used, e.g. a running out of ones condition where you check if the number of ones within a sequence is already 46, so that you don't place a 47th one by accident.
To illustrate:
Starting point is [1;2;3]
First iteration yields [ 1,2 ; 1,3 ; 2,1 ; 2,3 ; 3,1 ; 3,2 ] (here 'no repetition of 2 identical numbers one after the other' is triggered, that why three possible sequences are missing)
That should be very easy to implement. The only point where it might get a bit difficult is the 'between two equal numbers (1 or 2) there has to be a 3' condition. Just think of it as
If the last but one number is the number you check to be set, it can only be set if the previous number is a three.

请先登录,再进行评论。


John D'Errico
John D'Errico 2020-7-24
One approach would be to construct the set of all possible sequences of length perhaps 10.
seq10 = char(dec2base(0:59048,3) + 1);
k = any(diff(seq10,[],2) == 0,2);
seq10(k,:) = [];
This produces a set of 1536 sequences of length 10.
size(seq10)
ans =
1536 10
seq10(1:100:end,:)
ans =
16×10 char array
'1212121212'
'1213212312'
'1232123121'
'1312313212'
'1321231212'
'1323231312'
'2123132121'
'2131323212'
'2312312121'
'2321212312'
'2323213121'
'3123123212'
'3131321212'
'3212131312'
'3213232121'
'3232132312'
some of which are shown above. Of course, this is still too broad of a set, as it includes sequences that are illegal. I next need to exclude sequences that do not contain a 3 in a subsequence [1 ? 1] or [2 ? 2]. Therefore, I just need to exclude any strings in this set that contain either '121' or '212' as a substring.
n10 = size(seq10,1);
k = false(n10,1);
for i = 1:n10
k(i) = ~isempty(strfind(seq10(i,:),'121')) | ~isempty(strfind(seq10(i,:),'212'));
end
seq10(k,:) = [];
n10 = size(seq10,1)
n10 =
552
Too early in the morning for me to think of how to use regexp to do that last operation. Sorry. There are 552 such sequences of length 10. The problem is, this scheme becomes intractable when sequences of length say 15 or 20 must be constructed. Constructing all possible sequences of a longer length would be difficult.
Therefore, consider a different approach, where I'll construct the set of all admissable sequences recursively.
Suppose a given sequence ends with the subsequence: '12'. The next element canot be 1, since then we have the subsequence '121'. It cannot be 2, since then we swould have '122'. This means the next element must ALWAYS be 3.
Similarly, suppose the sequence ends with '13'. Now the legal continuations are always either '132' or '131'.
finallength = 30;
seqs2 = ['12';'13';'21';'23';'31';'32'];
% allseqs = seqs2;
for n = (size(allseqs,2)+1):finallength
newseqs = '';
for i = 1:6
k = ismember(allseqs(:,[end-1,end]),seqs2(i,:),'rows');
nk = sum(k);
switch i
case {1 3}
% allseqs ends in '12' or '21'. the next element must always be '3'
newseqs = [newseqs;[allseqs(k,:),repmat('3',nk,1)]];
case {2 4}
% allseqs ends in '13' or '23'. the next element must be either '1' or '2'
newseqs = [newseqs;[allseqs(k,:),repmat('1',nk,1)];[allseqs(k,:),repmat('2',nk,1)]];
case 5
% allseqs ends in '31'. the next element must be either '2' or '3'
newseqs = [newseqs;[allseqs(k,:),repmat('2',nk,1)];[allseqs(k,:),repmat('3',nk,1)]];
case 6
% allseqs ends in '32'. the next element must be either '1' or '3'
newseqs = [newseqs;[allseqs(k,:),repmat('1',nk,1)];[allseqs(k,:),repmat('3',nk,1)]];
end
end
allseqs = newseqs;
end
size(allseqs)
The code above, with finallength as successively [5,10,15,20,25,30], yields the number of all possible sequences as respectively [32, 552, 9568, 165888, 2876160, 49866752]. I cannot go much beyond that point.
Of those roughly 50 million possible sequences of length 30. We could choose one at random.
seq = allseqs(randi(n30),:)
seq =
'232323132321323132131232131231'
accumarray(seq' - '0',1)
ans =
8
10
12
So in this initial sequence of length 30, we found 8 ones, 10 twos, and 12 threes. We could now append one of the sequences from allseqs, as long as it starts in a way that is compatible with the terminating characters of seq, and as long as the total number of 1,2,3 elements are not more than the target of 46,40,45.

Miriam Acquafredda
Miriam Acquafredda 2020-7-24
Thank you all for your answers, thanks to your suggestions I managed to solve (I created only 36 vectors but it's enough for my purposes) the issue in this way:
a = repmat([1 2 3 2 1 3],1,20); %120-long vector with 40 2s
b = [1 3 1 3 1 3 1 3 1 3 1]; %11 missing elements
for idx= 2:108 %108 because it's 120-11
if a(1,idx-1) == 1 || a(1,(idx+12)) == 1
else
s(idx) = idx;
end
end
p = s(s~=0);
for i = 1:length(p)
anew(i,:) = [a(1:length(a) < p(i)), b, a(1:length(a) >= p(i))];
end

Community Treasure Hunt

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

Start Hunting!

Translated by