Unique combinations of a beaded necklace [duplicate] - matlab

This question already has answers here:
Generate all possible combinations of the elements of some vectors (Cartesian product)
(4 answers)
Closed 8 years ago.
So I'm writing a program to determine the unique combinations of a beaded necklace, but I can't seem to get it right. The rules are you can't have the same necklace forwards and backwards, and you can't have the same necklace with one bead being slid around to the other end. I've attached some pictures to clarify.
I wrote the code for it, and I thought I had achieved what I was trying to do, but it's not working correctly.
n = [1 2 3 4 2 4];
% green = 1
% blue = 2
% yellow = 3
% red = 4
p = perms(n);
total = max(size(p));
for i = 1:max(size(p))
q = p;
q(i) = [];
for j = 1:max(size(q))
if isequal(p(i),fliplr(q(j)))
total = total - 1;
elseif isequal(p(i),circshift(q(j),[1,1]))
total = total - 1;
elseif isequal(p(i),circshift(q(j),[length(q(j))-1,length(q(j))-1]))
total = total - 1;
end
disp(total)
end
end
Logically, this makes sense to me, but I could just be crazy.

If the problem size is small, you can vectorize all the comparisons (using bsxfun):
n = [1 2 3 4 2 4];
%// green = 1
%// blue = 2
%// yellow = 3
%// red = 4
N = numel(n);
p = perms(n).'; %'// generate all permutations
p2 = NaN([size(p) N+1]); %// this will store permutations with flips and shifts
p2(:,:,1) = p; %// original
p2(:,:,2) = flipud(p); %// flips
for k = 1:N-1
p2(:,:,2+k) = circshift(p,k); %// circular shifts
end
eqElem = bsxfun(#eq, p, permute(p2, [1 4 2 3]));
eqMat = squeeze(any(all(eqElem, 1), 4)); %// 1 if equal
remove = any(tril(eqMat, -1), 1); %// remove permutations that are "similar"
%// to a previous one, where "similar" means "equal up to circular shifts or
%// flips"
result = p(:,~remove).'; %'// all valid arrangements; one per row
resultNum = size(result, 1); %// number of arrangements
Results:
result =
1 3 2 2 4 4
1 3 2 4 4 2
1 3 2 4 2 4
1 3 4 2 2 4
1 3 4 2 4 2
1 3 4 4 2 2
1 2 3 2 4 4
1 2 3 4 2 4
1 2 3 4 4 2
1 2 2 3 4 4
1 2 2 4 4 3
1 2 2 4 3 4
1 2 4 3 2 4
1 2 4 3 4 2
1 2 4 2 3 4
1 2 4 2 4 3
1 2 4 4 2 3
1 2 4 4 3 2
1 4 4 3 2 2
1 4 4 2 2 3
1 4 4 2 3 2
1 4 3 4 2 2
1 4 3 2 2 4
1 4 3 2 4 2
1 4 2 3 2 4
1 4 2 3 4 2
1 4 2 2 3 4
1 4 2 2 4 3
1 4 2 4 2 3
1 4 2 4 3 2
resultNum =
30

You should do p = unique(p,'rows') before any loops. To see why, call perms([1 1 1]) at the command line.
There are a few issues here:
1) p, the perms, is a 2D matrix, so to get each perm you need to do p(i,:) to get the row. p(i) is just a single number.
2) You don't remove wrong answers from your list, so you will check against them twice. For example, say the first in the list is [1 2 3 4 2 4]; and the second is [4 2 4 3 2 1];. The fliplr check will compare these two combinations twice, once in the first loop around, once in the second.
3) If you want to make sure that any permutation which is a rotation is excluded (not just moving one bead around), you'll need some more circshift.
Consider using ismember with rows option again to compare a single row (e.g. a flipped version of the row you're checking) to an entire matrix.

Related

Matlab: How to enumerate the possible ways of forming pairs from a list

Suppose I have a list of length 2k, say {1,2,...,2k}. The number of possible ways of grouping the 2k numbers into k (unordered) pairs is n(k) = 1*3* ... *(2k-1). So for k=2, we have the following three different ways of forming 2 pairs
(1 2)(3 4)
(1 3)(2 4)
(1 4)(2 3)
How can I use Matlab to create the above list, i.e., create a matrix of n(k)*(2k) such that each row contains a different way of grouping the list of 2k numbers into k pairs.
clear
k = 3;
set = 1: 2*k;
p = perms(set); % get all possible permutations
% sort each two column
[~, col] = size(p);
for i = 1: 2: col
p(:, i:i+1) = sort(p(:,i:i+1), 2);
end
p = unique(p, 'rows'); % remove the same row
% sort each row
[row, col] = size(p);
for i = 1: row
temp = reshape(p(i,:), 2, col/2)';
temp = sortrows(temp, 1);
p(i,:) = reshape(temp', 1, col);
end
pairs = unique(p, 'rows'); % remove the same row
pairs =
1 2 3 4 5 6
1 2 3 5 4 6
1 2 3 6 4 5
1 3 2 4 5 6
1 3 2 5 4 6
1 3 2 6 4 5
1 4 2 3 5 6
1 4 2 5 3 6
1 4 2 6 3 5
1 5 2 3 4 6
1 5 2 4 3 6
1 5 2 6 3 4
1 6 2 3 4 5
1 6 2 4 3 5
1 6 2 5 3 4
As someone think my former answer is not useful, i post this.
I have the following brute force way of enumerating the pairs. Not particularly efficient. It can also cause memory problem when k>9. In that case, I can just enumerate but not create Z and store the result in it.
function Z = pair2(k)
count = [2*k-1:-2:3];
tcount = prod(count);
Z = zeros(tcount,2*k);
x = [ones(1,k-2) 0];
z = zeros(1,2*k);
for i=1:tcount
for j=k-1:-1:1
if x(j)<count(j)
x(j) = x(j)+1;
break
end
x(j) = 1;
end
y = [1:2*k];
for j=1:k-1
z(2*j-1) = y(1);
z(2*j) = y(x(j)+1);
y([1 x(j)+1]) = [];
end
z(2*k-1:2*k) = y;
Z(i,:) = z;
end
k = 3;
set = 1: 2*k;
combos = combntns(set, k);
[len, ~] = size(combos);
pairs = [combos(1:len/2,:) flip(combos(len/2+1:end,:))];
pairs =
1 2 3 4 5 6
1 2 4 3 5 6
1 2 5 3 4 6
1 2 6 3 4 5
1 3 4 2 5 6
1 3 5 2 4 6
1 3 6 2 4 5
1 4 5 2 3 6
1 4 6 2 3 5
1 5 6 2 3 4
You can also use nchoosek instead of combntns. See more at combntns or nchoosek

Find the rows of a matrix with conditions concerning the values of certain columns in matlab

As the title says, I want to find all rows in a Matlab matrix that in certain columns the values in the row are equal with the values in the previous row, or in general, equal in some row in the matrix. For example I have a matrix
1 2 3 4
1 2 8 10
4 5 7 9
2 3 6 4
1 2 4 7
and I want to find the following rows:
1 2 3 4
1 2 3 10
1 2 4 7
How do I do something like that and how do I do it generally for all the possible pairs in columns 1 and 2, and have equal values in previous rows, that exist in the matrix?
Here's a start to see if we're headed in the right direction:
>> M = [1 2 3 4;
1 2 8 10;
4 5 7 9;
2 3 6 4;
1 2 4 7];
>> N = M; %// copy M into a new matrix so we can modify it
>> idx = ismember(N(:,1:2), N(1,1:2), 'rows')
idx =
1
1
0
0
1
>> N(idx, :)
ans =
1 2 3 4
1 2 8 10
1 2 4 7
Then you can remove those rows from the original matrix and repeat.
>> N = N(~idx,:)
N =
4 5 7 9
2 3 6 4
this will give you the results
data1 =[1 2 3 4
1 2 8 10
4 5 7 9
2 3 6 4
1 2 4 7];
data2 = [1 2 3 4
1 2 3 10
1 2 4 7];
[exists,position] = ismember(data1,data2, 'rows')
where the exists vector tells you wheter the row is on the other matrix and position gives you the position...
a less elegant and simpler version would be
array_data1 = reshape (data1',[],1);
array_data2 = reshape (data2',[],1);
matchmatrix = zeros(size(data2,1),size(data1,1));
for irow1 = 1: size(data2,1)
for irow2 = 1: size(data1,1)
matchmatrix(irow1,irow2) = min(data2(irow1,:) == data1(irow2,:))~= 0;
end
end
the matchmatrix is to read as a connectivity matrix where value of 1 indicates which row of data1 matches with which row of data2

Given a number, Generate a series of 'L' shaped matrix with MATLAB

Given any number. Lets say for example 5, I need to generate a matrix similar to this:
1 2 3 4 5
2 2 3 4 5
3 3 3 4 5
4 4 4 4 5
5 5 5 5 5
How to generate a matrix similar to this using Matlab?
I'd use bsxfun:
n = 5;
matrix = bsxfun(#max, 1:n, (1:n).');
An alternative (probably slower) is to use ndgrid:
n = 5;
[ii, jj] = ndgrid(1:n);
matrix = max(ii, jj);
Nothing will ever beat bsxfun as used by Luis Mendo., but for the sake of reminding people of the existence of Matlab's gallery function, here another approach:
n = 5;
A = gallery('minij',n)
B = n + 1 - A(end:-1:1,end:-1:1)
A =
1 1 1 1 1
1 2 2 2 2
1 2 3 3 3
1 2 3 4 4
1 2 3 4 5
B =
1 2 3 4 5
2 2 3 4 5
3 3 3 4 5
4 4 4 4 5
5 5 5 5 5

Plot a matrix, values as colors

I have random matrix with arbitrary dimensions and I want to assign a color for each value (randomly or not) and plot the matrix with numbers like,
So far I've done this,
m = 12;
n = 8;
A = randi(5,[m n]);
Arot = flipud(A);
pcolor(Arot);figure(gcf);
for i = 1 : n -1
for j = 1 : m -1
text(i + .5 , j + .5 ,num2str(Arot(j,i)),'FontSize',18);
end
end
which gives me this,
for
A =
4 4 4 1 2 1 4 2
5 2 2 3 2 1 1 2
1 2 1 4 1 2 5 5
1 3 5 3 1 4 1 3
3 4 4 4 3 3 3 4
2 5 2 2 1 1 2 4
1 3 1 3 5 5 2 4
5 1 2 4 1 4 1 2
2 4 5 5 1 3 5 2
4 2 2 3 4 3 3 4
3 5 3 2 4 3 3 1
1 4 5 3 2 4 3 5
but as you can see I've lost first row and last column of A.
Actually the problem starts bu using pcolor, which gives an (m-1)x(n-1) plot for mxn input.
Any suggestions?
Thanks,
Using imagesc instead of pcolor solves the problem. It also brings some other benefits:
Avoids the need for flipud;
The coordinates of the text objects become integer values;
Axes are automatically set to "matrix" mode, with the origin in the upper right corner.
Code:
m = 8;
n = 6;
A = randi(5,[m n]);
imagesc(A);
for ii = 1:n
for jj = 1:m
text(ii, jj, num2str(A(jj,ii)), 'FontSize', 18);
end
end
For
A =
4 5 4 2 4 4
5 4 3 4 4 2
5 4 1 1 1 3
4 3 5 2 5 4
1 2 2 2 5 3
1 5 2 5 1 3
4 3 1 3 3 1
3 1 2 4 2 3
this produces
I just padded the matrix prior to pcolor and I think it's the effect you wanted. The reason it works comes from the help doc for pcolor, which states that
In the default shading mode, 'faceted', each cell has a constant color
and the last row and column of C are not used.
m = 12;
n = 8;
A = randi(5,[m n]);
Arot = flipud(A);
Arot = [ Arot; Arot(end,:) ];
Arot = [ Arot, Arot(:,end) ];
pcolor(Arot);figure(gcf);
for i = 1 : n
for j = 1 : m
text(i + .5 , j + .5 ,num2str(Arot(j,i)),'FontSize',18);
end
end

How to flip specific parts of a matrix

I am trying to flip certain parts of a matrix. I can explain better by example. Let's say that I have a matrix
M = [ 1 3 6;
1 2 4;
1 7 1;
2 9 0;
2 8 3;
2 4 2;
2 3 1;
3 6 5;
3 4 5;
3 1 9;
4 2 4;
4 8 6 ]
What I'd like to do here is take any rows with an even number in the first column, and flip the third column elements. The end result would look like this:
1 3 6
1 2 4
1 7 1
2 9 1 *
2 8 2 *
2 4 3 *
2 3 0 *
3 6 5
3 4 5
3 1 9
4 2 6 *
4 8 4 *
Note the rows marked with a star have had the elements of the third column flipped upside-down. The problem I'm having is going through each row like in a for-loop you cannot flip an entire set of rows.
Thanks in advance for any help.
Another time accumarray is the way to go:
A =[ 1 3 6 ;
1 2 4 ;
1 7 1 ;
2 9 0 ;
2 8 3 ;
2 4 2 ;
2 3 1 ;
3 6 5 ;
3 4 5 ;
3 1 9 ;
4 2 4 ;
4 8 6 ]
C = accumarray(A(:,1),A(:,3),[],#(x) {flipud(x)} ); %// get groups according to
%// first column and flip it
C = vertcat(C{:}); %// cell array returned,
%// transform to matrix
mask = ~mod(A(:,1),2); %// mask for even numbers
A(mask,3) = C(mask); %// replace masked values of 3rd column with flipped ones
returns:
A =
1 3 6
1 2 4
1 7 1
2 9 1
2 8 2
2 4 3
2 3 0
3 6 5
3 4 5
3 1 9
4 2 6
4 8 4
Certainly slower, but just for fun in two lines:
C = accumarray(A(:,1),A(:,3),[],#(x) {flipud(x)} );
A(~mod(A(:,1),2),3) = getfield( vertcat(C{:}), {~mod(A(:,1),2)});
%// well no, I won't explain it...
Edit: I assumed your first column just contains integers!
I would suggest you break the problem down into stages, something like so:
Identify blocks you wish to flip
Extract them
Flip them
Replace them
You can identify a set of even numbers using the unique and mod functions, then use a for loop over them and use logical indexing to pull/replace the blocks.
Here, try this
a = magic(5); % Some data in a 5x5 matrix
b = 1:numel(a); % Indices of <a>
Rearrange b however you want, then do a=a(b) to reassign a based on the reassigned indices of b. For example, the following code
disp(a(b));
would just return the elements of a in their original order. For your application this code should work:
a = <your matrix data>
b = 1:numel(a);
b = [b(1:27) fliplr(b(28:31)) b(32:34) fliplr(b(35:36))] % Change this part
a = reshape(a(b),size(a))
You should change b based on whatever you need it to do.