Calculating combinations of paired items with item constraint [closed] - matlab

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have a set of 120 pairs, which are derived from a set of 16 items (i.e. c(16,2)=120).
I want to determine how many combinations of 56 pairs can be chosen from the 120 pairs, but with the constraint that each combination has to contain exactly 7 of each of the 16 items (i.e. in each subset of 56 pairs, each of the 16 items is equally represented).
In addition to determining the number of combinations, I also need to list them. Can anyone help with how I could code this in Matlab please?

It is an interesting combinitorial question since nchoosek(120,56) gives 7.4149e+34 possible ways to select 56 pairs out of the 120. So you shouldn't solve this by a loop over all selections.
To handle the problem I introduce a representation of selected pairs given by a matrix A = (aij), where aij == 1 if and only if (i,j) oder (j,i) is a pair among the 56 selected ones. This matrix represents a selection of 56 pairs as described above if and only if row sums are 7, i.e. A*ones(16,1) = 7*ones(16,1) (symmetric!). It is easy to solve this systematically to print all possible solutions, but this results in about 1e20 candidates...
But according to this high number you can easily select one example randomly by some stupid code like the following:
%% find some random example
A = zeros(16,16);
b1 = ones(16,1);
b7 = 7*b1;
while norm(A*b1 - b7) ~= 0
A = zeros(16,16);
change = true;
while norm(A*b1 - b7) ~= 0 && change
change = false;
todo = find(A*b1<7);
idx = todo(randperm(length(todo)));
for k = 1:2:length(idx)-1
if A(idx(k),idx(k+1)) == 0
change = true;
end
A(idx(k),idx(k+1)) = 1;
A(idx(k+1),idx(k)) = 1;
end
end
end
%% print it
pIdx = 0;
for lIdx = 1:16
for cIdx = 1:lIdx-1
if A(lIdx,cIdx) == 1
fprintf(['(' num2str(cIdx) ',' num2str(lIdx) ') ']);
pIdx = pIdx + 1;
if mod(pIdx,7) == 0
fprintf('\n');
end
end
end
end
Example result:
(1,3) (2,4) (1,5) (2,5) (2,6) (3,6) (4,6)
(5,6) (1,7) (3,7) (6,7) (4,8) (5,8) (2,9)
(5,9) (6,9) (7,9) (8,9) (2,10) (3,10) (4,10)
(5,10) (7,10) (1,11) (3,11) (6,11) (7,11) (9,11)
(1,12) (4,12) (7,12) (11,12) (3,13) (8,13) (9,13)
(10,13) (1,14) (2,14) (4,14) (8,14) (12,14) (13,14)
(1,15) (3,15) (5,15) (8,15) (11,15) (12,15) (13,15)
(2,16) (4,16) (8,16) (10,16) (12,16) (13,16) (14,16)
Update: Added code to scan all possible solutions...
function main()
%% provide n-choose-k sequences (compute only once!)
global ncks maxResult;
disp('Prepare n choose k sequences...');
for n = 1:15
fprintf([num2str(n) '... ']);
for k = 1:min(7,n)
ncks{n}{k} = nchoosek_sequence(n,k);
end
end
fprintf('\n');
%% start finding solutions
disp('Scan space of solutions...')
A = zeros(16,16);
maxResult = -Inf;
tic;
findMats(A,1)
toc;
end
function findMats(A,curRow)
global ncks maxResult;
remain = 7-sum(A+A');
if curRow == 16
if remain(16) == 0
A = A + A';
if norm(sum(A)-7*ones(1,16)) ~= 0
error('AHHH');
end
%A
%pause();
maxResult = max(YOUR_FUNCTION(A),maxResult); % <- add your function here
end
return;
end
if remain(curRow) == 0
findMats(A,curRow+1);
elseif remain(curRow) > 0
remainingRows = curRow + find(remain(curRow+1:end) > 0);
if ~isempty(remainingRows) && length(remainingRows) >= remain(curRow)
for r = 1:nchoosek(length(remainingRows),remain(curRow))
row = remainingRows(ncks{length(remainingRows)}{remain(curRow)}(r,:));
Anew = A;
Anew(curRow,row) = 1;
findMats(Anew,curRow+1);
end
end
end
end
% very simple and not optimized way to get a sequence of subset-selections
% in increasing order
%
% Note: This function was easy to implement and sufficient for our
% purposes. For more efficient implementations follow e.g.
% http://stackoverflow.com/questions/127704/algorithm-to-return-all-combin
% ations-of-k-elements-from-n
function s = nchoosek_sequence(N,k)
s = zeros(nchoosek(N,k),k);
idx = 1;
for n=(2^k-1):(2^N-1)
if sum(dec2bin(n)=='1') == k
temp = dec2bin(n);
s(idx,:) = find([zeros(1,N-length(temp)), temp=='1']);
idx = idx + 1;
end
end
% just for convinience ;-)
s = s(end:-1:1,:);
end
But this can take a while... ;-)

Related

Seeking advice on trying to read a moore neighbourhood for a 2D cellular automata in MATLAB for an epidemic simulator

I'm currently working on a code that makes use of a 2D cellular automata as an epidemic simulator in MATLAB. The main basic rule I'm trying to implement is that if any neighbour within a Moore Neighbourhood with a 1-cell radius is infected, the cell will become infected. But I can't seem to get a good code working for it.
Basically what I'm trying to do is say with for a cell with a one cell radius Moore neighbourhood, if any values in this neighbourhood = 2, then the initial cell will become 2.
I've tried using the forest fire code on the rosetta code as a basis for my code behaviour but it doesnt work very well. The rules don't really work that well when applying it to mine. I've tried using the mod function and a series of if loops to attach. I'll put in some code of each to give context.
This example doesn't really function well as an epidemic simulator to be honest.
matlab
clear; clc;
n = 200;
N = n/2;
E = 0.001; % Creating an arbitrary number for population exposed to
the disease but not infected
p = 1 + (rand(n,n)<E);
%p = ceil(rand(n,n)*2.12) - 1;
% ratio0 = sum(p(:)==0)/n^2;
% ratio1 = sum(p(:)==1)/n^2;
% ratio2 = sum(p(:)==2)/n^2;
% ratio3 = sum(p(:)==3)/n^2;
S = ones(3); S(2,2) = 0;
ff = 0.00000000002;
p(N,N) = 3;
%% Running the simulation for a set number of loops
colormap([1,1,1;1,0,1;1,0,0]); %Setting colourmap to Green, red and
grey
count = 0;
while(count<365) % Running the simulation with limited number of runs
count = count + 1;
image(p); pause(0.1); % Creating an image of the model
P = (p==1); % Adding empty cells to new array
P = P + (p==2).*((filter2(S,p==3)>0) + (rand(n,n)<ff) + 2); % Setting
2 as a tree, ignites based on proximity of trees and random
chance ff
P = P + (p==3); % Setting 3 as a burning tree, that becomes 1,
p = P;
end
second idea. this basically returns nothing
matlab
clear;clf;clc;
n = 200;
pos = mod((1:n),n) + 1; neg = mod((1:n)-2,n) + 1;
p = (ceil(rand(n,n)*1.0005));
for t = 1:365
if p(neg,neg) ==2
p(:,:) = 2;
end
if p(:,neg)==2
p(:,:) = 2;
end
if p(pos,neg)==2
p(:,:) = 2;
end
if p(neg,:)==2
p(:,:) = 2;
end
if p(pos,:)==2
p(:,:) = 2;
end
if p(neg,pos)==2
p(:,:) = 2;
end
if p(:,pos)==2
p(:,:) = 2;
end
if p(pos,pos)== 2
p(:,:) = 2;
end
image(p)
colormap([1,1,1;1,0,1])
end
third I tried using logic gates to see if that would work. I don't know if commas would work instead.
matlab
clear;clf;clc;
n = 200;
pos = mod((1:n),n) + 1; neg = mod((1:n)-2,n) + 1;
p = (ceil(rand(n,n)*1.0005));
%P = p(neg,neg) + p(:,neg) + p(pos,neg) + p(neg,:) + p(:,:) + p(pos,:)
+ p(neg,pos) + p(:,pos) + p(pos,pos)
for t=1:365
if p(neg,neg)|| p(:,neg) || p(pos,neg) || p(neg,:) || p(pos,:) ||
p(neg,pos) || p(:,pos) || p(pos,pos) == 2
p(:,:) = 2;
end
image(p)
colormap([1,1,1;1,0,1])
end
I expected the matrix to just gradually become more magenta but nothing happens in the second one. I get this error for the third.
"Operands to the || and && operators must be convertible to logical scalar values."
I just have no idea what to do!
Cells do not heal
I assume that
Infected is 2, non-infected is 1;
An infected cell remains infected;
A non-infected cell becomes infected if any neighbour is.
A simple way to achieve this is using 2-D convolution:
n = 200;
p = (ceil(rand(n,n)*1.0005));
neighbourhood = [1 1 1; 1 1 1; 1 1 1]; % Moore plus own cell
for t = 1:356
p = (conv2(p-1, neighbourhood, 'same')>0) + 1; % update
image(p), axis equal, axis tight, colormap([.4 .4 .5; .8 0 0]), pause(.1) % plot
end
Cells heal after a specified time
To model this, it is better to use 0 for a non-infected cell and a positive integer for an infected cell, which indicated how long it has been infected.
A cell heals after it has been infected for a specified number of iterations (but can immediately become infeced again...)
The code uses convolution, as the previous one, but now already infected cells need to be dealt with separately from newly infected cells, and so a true Moore neighbourhood is used.
n = 200;
p = (ceil(rand(n,n)*1.0005))-1; % 0: non-infected. 1: just infected
T = 20; % time to heal
neighbourhood = [1 1 1; 1 0 1; 1 1 1]; % Moore
for t = 1:356
already_infected = p>0; % logical index
p(already_infected) = p(already_infected)+1; % increase time count for infected
newly_infected = conv2(p>0, neighbourhood, 'same')>0; % logical index
p(newly_infected & ~already_infected) = 1; % these just became infected
newly_healed = p==T; % logical index
p(newly_healed) = 0; % these are just healed
image(p>0), axis equal, axis tight, colormap([.4 .4 .5; .8 0 0]), pause(.1) % plot
% infected / non-infected state
end

Output 1, 0.5, or 0 depending if a matrix elements are prime, 1, or neither

I am sending a matrix to my function modifikuj, where I want to replace the elements of the matrix with:
1 if element is a prime number
0 if element is a composite number
0.5 if element is 1
I dont understand why it is not working. I just started with MATLAB, and I created this function:
function B = modifikuj(A)
[n,m] = size(A);
for i = 1:n
for j = 1:m
prost=1;
if (A(i,j) == 1)
A(i,j) = 0.5;
else
for k = 2:(A(i,j))
if(mod(A(i,j),k) == 0)
prost=0;
end
end
if(prost==1)
A(i,j)=1;
else
A(i,j)=0;
end
end
end
end
With
A = [1,2;3,4];
D = modifikuj(A);
D should be:
D=[0.5, 1; 1 0];
In MATLAB you'll find you can often avoid loops, and there's plenty of built in functions to ease your path. Unless this is a coding exercise where you have to use a prescribed method, I'd do the following one-liner to get your desired result:
D = isprime( A ) + 0.5*( A == 1 );
This relies on two simple tests:
isprime( A ) % 1 if prime, 0 if not prime
A == 1 % 1 if == 1, 0 otherwise
Multiplying the 2nd test by 0.5 gives your desired condition for when the value is 1, since it will also return 0 for the isprime test.
You are not returning anything from the function. The return value is supposed to be 'B' according to your code but this is not set. Change it to A.
You are looping k until A(i,j) which is always divisible by itself, loop to A(i,j)-1
With the code below I get [0.5,1;1,0].
function A = modifikuj(A)
[n,m] = size(A);
for i = 1:n
for j = 1:m
prost=1;
if (A(i,j) == 1)
A(i,j) = 0.5;
else
for k = 2:(A(i,j)-1)
if(mod(A(i,j),k) == 0)
prost=0;
end
end
if(prost==1)
A(i,j)=1;
else
A(i,j)=0;
end
end
end
end
In addition to #EuanSmith's answer. You can also use the in built matlab function in order to determine if a number is prime or not.
The following code will give you the desired output:
A = [1,2;3,4];
A(A==1) = 0.5; %replace 1 number with 0.5
A(isprime(A)) = 1; %replace prime number with 1
A(~ismember(A,[0.5,1])) = 0; %replace composite number with 0
I've made the assumption that the matrice contains only integer.
If you only want to learn, you can also preserve the for loop with some improvement since the function mod can take more than 1 divisor as input:
function A = modifikuj(A)
[n,m] = size(A);
for i = 1:n
for j = 1:m
k = A(i,j);
if (k == 1)
A(i,j) = 0.5;
else
if all(mod(k,2:k-1)) %check each modulo at the same time.
A(i,j)=1;
else
A(i,j)=0;
end
end
end
end
And you can still improve the prime detection:
2 is the only even number to test.
number bigger than A(i,j)/2 are useless
so instead of all(mod(k,2:k-1)) you can use all(mod(k,[2,3:2:k/2]))
Note also that the function isprime is a way more efficient primality test since it use the probabilistic Miller-Rabin algorithme.

Indexing a vector within a bigger one in MATLAB

I'm trying to find the index position of the smaller vector inside a bigger one.
I've already solved this problem using strfind and bind2dec,
but I don't want to use strfind, I don't want to convert to string or to deciamls at all.
Given the longer vector
a=[1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1];
I want to find the index of the smaller vector b inside a
b=[1,1,1,0,0,0];
I would expect to find as result:
result=[15,16,17,18,19,20];
Thank you
Here is as solution using 1D convolution. It may find multiple matches so start holds beginning indices of sub-vectors:
f = flip(b);
idx = conv(a,f,'same')==sum(b) & conv(~a,~f,'same')==sum(~b);
start = find(idx)-ceil(length(b)/2)+1;
result = start(1):start(1)+length(b)-1;
Solution with for loops:
a=[1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1];
b=[1,1,1,0,0,0];
c = [];
b_len = length(b)
maxind0 = length(a) - b_len + 1 %no need to search higher indexes
for i=1:maxind0
found = 0;
for j=1:b_len
if a(i+j-1) == b(j)
found = found + 1;
else
break;
end
end
if found == b_len % if sequence is found fill c with indexes
for j=1:b_len
c(j)= i+j-1;
end
break
end
end
c %display c
Does it need to be computationally efficient?
A not very efficient but short solution would be this:
a=[1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1];;
b=[1,1,1,0,0,0];
where = find(arrayfun(#(n) all(a(n+1:n+length(b))==b),0:length(a)-length(b)));
... gives you 15. Your result would be the vector where:where+length(b)-1.
edit: I tried it and I stand corrected. Here is a version with loops:
function where = find_sequence(a,b)
na = 0;
where = [];
while na < length(a)-length(b)
c = false;
for nb = 1:length(b)
if a(na+nb)~=b(nb)
na = na + 1; % + nb
c = true;
break
end
end
if ~c
where = [where,na+1];
na = na + 1;
end
end
Despite its loops and their bad reputation in Matlab, it's a lot faster:
a = round(rand(1e6,1));
b = round(rand(10,1));
tic;where1 = find(arrayfun(#(n) all(a(n+1:n+length(b))==b),0:length(a)-length(b)));toc;
tic;where2 = find_sequence(a,b);toc;
>> test_find_sequence
Elapsed time is 4.419223 seconds.
Elapsed time is 0.042969 seconds.
A neater method using for would look like this, there is no need for an inner checking loop as we can vectorize that with all...
a = [1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1];
b = [1,1,1,0,0,0];
idx = NaN( size(b) ); % Output NaNs if not found
nb = numel( b ); % Store this for re-use
for ii = 1:numel(a)-nb+1
if all( a(ii:ii+nb-1) == b )
% If matched, update the index and exit the loop
idx = ii:ii+nb-1;
break
end
end
Output:
idx = [15,16,17,18,19,20]
Note, I find this a bit easier to read that some of the nested solutions, but it's not necessarily faster, since the comparison is done on all elements in b each time.

Implementing Gaussian elimination with partial pivoting [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I am writing a program to implement Gaussian elimination with partial pivoting in MATLAB. I created an integer array to store the interchange of rows, instead of directly exchanging the rows.
However, I could not obtain the correct result and I could not figure out the problem. How should I modify my code to get the right answer?
A=[1, -2, 1; 2, 1, -3; 4, -7, 1];
b=[0, 5, 1];
b=b';
function x = GE(A,b)
[m,n]= size(A);
if m ~= n
disp('Not a square');
end
for p=1:n
array(p)=p;
endfor
A = [A,b];
%elimination
for i = 1:n-1
pivot = i;
%select pivot
for j = i+1:n
if abs(A(array(i),i)) < abs(A(array(i),i)) %row interchange
temp = array(i);
array(i) = array(j);
array(j) = temp;
end
end
while (pivot <= n && A(pivot,i)== 0)
pivot = pivot+1;
end
if pivot > n
disp('No unique solution');
break
else
if pivot > i
tem = array(i);
array(i) = pivot
pivot= tem;
end
end
for j = i+1:n
m = -A(array(j),i)/A(array(i),i);
for k = i+1:n+1
A(array(j),k) = A(array(j),k) + m*A(array(i),k);
end
end
end
if A(n,n) == 0
disp('No unique solution');
return
end
%backward substitution
x(n) = A(array(n),n+1)/A(array(n),n);
for i = n - 1:-1:1
sum = 0;
for j = i+1:n
sum = sum + A(array(i),j)*x(j);
end
x(i) = (A(array(i),n+1) - sum)/A(array(i),i);
end
endfunction
Your error is pretty simple. You're not pivoting properly - specifically here inside your if statement:
for j = i+1:n
if abs(A(array(i),i)) < abs(A(array(i),i)) %row interchange <-------
temp = array(i);
array(i) = array(j);
array(j) = temp;
end
end
Your check to see which coefficient to pivot from is not correct because you are using the index i for checking along the columns when you need to use j. Therefore, simply change the other side of your Boolean condition so that it uses j:
for j = i+1:n
if abs(A(array(i),i)) < abs(A(array(j),j)) %// CHANGE HERE
temp = array(i);
array(i) = array(j);
array(j) = temp;
end
end
With your example A and b, the answer I get is x = [2 1 0], which agrees with MATLAB's ldivide operation: x = A \ b.

switching numbers in matlab [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
I have N-time steps and want to create N real numbers corresponding to each time step as follows: the numbers should be in the range [a, b] with a switching probability of p.
Ie, if kth number (corresponding to kth time step) is n, then the probability that k+1th number (corresponding to k+1th time step) is any other number different than n is p. All created numbers should be in the range [a,b].
How can this be done in matlab?
Thanks.
I am not really sure that I got all of your requirements but this might be the script you are searching for:
N = 10; % count of numbers
p = 0.2; % switching probability
a = 5;
b = 20;
% init empty numbers and get the first random number
numbers = zeros(N,1);
numbers(1) = rand(1) * (b-a) + a;
for iNumber = 2:N
% test if number must change
if rand(1) < (1-p)
% the number must be the same than the previous
% copy the value and go to next number
numbers(iNumber) = numbers(iNumber-1);
continue;
else
% a new number must be found
while 1
% get a new random number
numbers(iNumber) = rand(1) * (b-a) + a;
% check if the new random number is different from the previous
if numbers(iNumber) ~= numbers(iNumber-1)
% in case they are different, the while 1 can be stopped
break;
end
end % while 1
end % if rand(1) < (1-p)
end % for iNumber = 2:N
Guaranteed super random:
N = randi(100);
p = rand;
l1 = rand*randi(100);
l2 = rand*randi(100);
if l2 < l1
[l2 l1] = deal(l1,l2);
end
out = []
while isempty(out)
if rand>rand
n = 2:N
a = rand([N,1])*(l2-l1)+l1;
x = rand([N-1,1])<p;
n(x==0)=n(x==0)-1;
n = [1 n];
out = a(n);
end
end