Generating all combinations with repetition using MATLAB - matlab

How do I create all k-combinations with repetitions of a given set (also called k-multicombinations or multisubsets) using MATLAB?
This is similar to the cartesian product, but two rows that only differ by their sorting should be considered the same (e.g. the vectors [1,1,2]=~=[1,2,1] are considered to be the same), so generating the cartesian product and then applying unique(sort(cartesianProduct,2),'rows') should yield the same results.
Example:
The call nmultichoosek(1:n,k) should generate the following matrix:
nmultichoosek(1:3,3)
ans =
1 1 1
1 1 2
1 1 3
1 2 2
1 2 3
1 3 3
2 2 2
2 2 3
2 3 3
3 3 3

We can use the bijection mentioned in the wikipedia article, which maps combinations without repetition of type n+k-1 choose k to k-multicombinations of size n. We generate the combinations without repetition and map them using bsxfun(#minus, nchoosek(1:n+k-1,k), 0:k-1);. This results in the following function:
function combs = nmultichoosek(values, k)
%// Return number of multisubsets or actual multisubsets.
if numel(values)==1
n = values;
combs = nchoosek(n+k-1,k);
else
n = numel(values);
combs = bsxfun(#minus, nchoosek(1:n+k-1,k), 0:k-1);
combs = reshape(values(combs),[],k);
end

Thanks to Hans Hirse for a correction.
Brute-force approach: generate all tuples and then keep only those that are sorted. Not suitable for large values of n or k.
values = 1:3; %// data
k = 3; %// data
n = numel(values); %// number of values
combs = values(dec2base(0:n^k-1,n)-'0'+1); %// generate all tuples
combs = combs(all(diff(combs.')>=0, 1),:); %'// keep only those that are sorted

This is probably an even more brutal (memory intensive) method than previous posts, but a tidy readable one-liner:
combs = unique(sort(nchoosek(repmat(values,1,k),k),2),'rows');

Related

MATLAB: Applying vectors of row and column indices without looping

I have a situation analogous to the following
z = magic(3) % Data matrix
y = [1 2 2]' % Column indices
So,
z =
8 1 6
3 5 7
4 9 2
y represents the column index I want for each row. It's saying I should take row 1 column 1, row 2 column 2, and row 3 column 2. The correct output is therefore 8 5 9.
I worked out I can get the correct output with the following
x = 1:3;
for i = 1:3
result(i) = z(x(i),y(i));
end
However, is it possible to do this without looping?
Two other possible ways I can suggest is to use sub2ind to find the linear indices that you can use to sample the matrix directly:
z = magic(3);
y = [1 2 2];
ind = sub2ind(size(z), 1:size(z,1), y);
result = z(ind);
We get:
>> result
result =
8 5 9
Another way is to use sparse to create a sparse matrix which you can turn into a logical matrix and then sample from the matrix with this logical matrix.
s = sparse(1:size(z,1), y, 1, size(z,1), size(z,2)) == 1; % Turn into logical
result = z(s);
We also get:
>> result
result =
8
5
9
Be advised that this only works provided that each row index linearly increases from 1 up to the end of the rows. This conveniently allows you to read the elements in the right order taking advantage of the column-major readout that MATLAB is based on. Also note that the output is also a column vector as opposed to a row vector.
The link posted by Adriaan is a great read for the next steps in accessing elements in a vectorized way: Linear indexing, logical indexing, and all that.
there are many ways to do this, one interesting way is to directly work out the indexes you want:
v = 0:size(y,2)-1; %generates a number from 0 to the size of your y vector -1
ind = y+v*size(z,2); %generates the indices you are looking for in each row
zinv = z';
zinv(ind)
>> ans =
8 5 9

Matlab matrix with fixed sum over rows

I'm trying to construct a matrix in Matlab where the sum over the rows is constant, but every combination is taken into account.
For example, take a NxM matrix where M is a fixed number and N will depend on K, the result to which all rows must sum.
For example, say K = 3 and M = 3, this will then give the matrix:
[1,1,1
2,1,0
2,0,1
1,2,0
1,0,2
0,2,1
0,1,2
3,0,0
0,3,0
0,0,3]
At the moment I do this by first creating the matrix of all possible combinations, without regard for the sum (for example this also contains [2,2,1] and [3,3,3]) and then throw away the element for which the sum is unequal to K
However this is very memory inefficient (especially for larger K and M), but I couldn't think of a nice way to construct this matrix without first constructing the total matrix.
Is this possible in a nice way? Or should I use a whole bunch of for-loops?
Here is a very simple version using dynamic programming. The basic idea of dynamic programming is to build up a data structure (here S) which holds the intermediate results for smaller instances of the same problem.
M=3;
K=3;
%S(k+1,m) will hold the intermediate result for k and m
S=cell(K+1,M);
%Initialisation, for M=1 there is only a trivial solution using one number.
S(:,1)=num2cell(0:K);
for iM=2:M
for temporary_k=0:K
for new_element=0:temporary_k
h=S{temporary_k-new_element+1,iM-1};
h(:,end+1)=new_element;
S{temporary_k+1,iM}=[S{temporary_k+1,iM};h];
end
end
end
final_result=S{K+1,M}
This may be more efficient than your original approach, although it still generates (and then discards) more rows than needed.
Let M denote the number of columns, and S the desired sum. The problem can be interpreted as partitioning an interval of length S into M subintervals with non-negative integer lengths.
The idea is to generate not the subinterval lengths, but the subinterval edges; and from those compute the subinterval lengths. This can be done in the following steps:
The subinterval edges are M-1 integer values (not necessarily different) between 0 and S. These can be generated as a Cartesian product using for example this answer.
Sort the interval edges, and remove duplicate sets of edges. This is why the algorithm is not totally efficient: it produces duplicates. But hopefully the number of discarded tentative solutions will be less than in your original approach, because this does take into account the fixed sum.
Compute subinterval lengths from their edges. Each length is the difference between two consecutive edges, including a fixed initial edge at 0 and a final edge at S.
Code:
%// Data
S = 3; %// desired sum
M = 3; %// number of pieces
%// Step 1 (adapted from linked answer):
combs = cell(1,M-1);
[combs{end:-1:1}] = ndgrid(0:S);
combs = cat(M+1, combs{:});
combs = reshape(combs,[],M-1);
%// Step 2
combs = unique(sort(combs,2), 'rows');
%// Step 3
combs = [zeros(size(combs,1),1) combs repmat(S, size(combs,1),1)]
result = diff(combs,[],2);
The result is sorted in lexicographical order. In your example,
result =
0 0 3
0 1 2
0 2 1
0 3 0
1 0 2
1 1 1
1 2 0
2 0 1
2 1 0
3 0 0

Matlab: find second argmax [duplicate]

How do I find the index of the 2 maximum values of a 1D array in MATLAB? Mine is an array with a list of different scores, and I want to print the 2 highest scores.
You can use sort, as #LuisMendo suggested:
[B,I] = sort(array,'descend');
This gives you the sorted version of your array in the variable B and the indexes of the original position in I sorted from highest to lowest. Thus, B(1:2) gives you the highest two values and I(1:2) gives you their indices in your array.
I'll go for an O(k*n) solution, where k is the number of maximum values you're looking for, rather than O(n log n):
x = [3 2 5 4 7 3 2 6 4];
y = x; %// make a copy of x because we're going to modify it
[~, m(1)] = max(y);
y(m(1)) = -Inf;
[~, m(2)] = max(y);
m =
5 8
This is only practical if k is less than log n. In fact, if k>=3 I would put it in a loops, which may offend the sensibilities of some. ;)
To get the indices of the two largest elements: use the second output of sort to get the sorted indices, and then pick the last two:
x = [3 2 5 4 7 3 2 6 4];
[~, ind] = sort(x);
result = ind(end-1:end);
In this case,
result =
8 5

How to construct a 128x32 scrambled matrix?

How can I construct a scrambled matrix with 128 rows and 32 columns in vb.net or Matlab?
Entries of the matrix are numbers between 1 and 32 with the condition that each row mustn't contain duplicate elements and rows mustn't be duplicates.
This is similar to #thewaywewalk's answer, but makes sure that the matrix has no repeated rows by testing if it does and in that case generating a new matrix:
done = 0;
while ~done
[~, matrix] = sort(rand(128,32),2);
%// generate each row as a random permutation, independently of other rows.
%// This line was inspired by randperm code
done = size(unique(matrix,'rows'),1) == 128;
%// in the event that there are repeated rows: generate matrix again
end
If my computations are correct, the probability that the matrix has repteated rows (and thus has to be generated again) is less than
>> 128*127/factorial(32)
ans =
6.1779e-032
Hey, it's more likely that a cosmic ray will spoil a given run of the program! So I guess you can safely remove the while loop :-)
With randperm you can generate one row:
row = randperm(32)
if this vector wouldn't be that long you could just use perms to find all permutations:
B = perms(randperm(32))
but it's memory-wise too much! ( 32! = 2.6313e+35 rows )
so you can use a little loop:
N = 200;
A = zeros(N,32);
for ii = 1:N
A(ii,:) = randperm(32);
end
B = unique(A, 'rows');
B = B(1:128,:);
For my tests it was sufficient to use N = 128 directly and skip the last two lines, because with 2.6313e+35 possibly permutations the probability that you get a correct matrix with the first try is very high. But to be sure that there are no row-duplicates choose a higher number and select the first 128 rows finally. In case the input vector is relatively short and the number of desired rows close to the total number of possible permutations use the proposed perms(randperm( n )).
small example for intergers from 1 to 4 and a selection of 10 out of 24 possible permutations:
N = 20;
A = zeros(N,4);
for ii = 1:N
A(ii,:) = randperm(4);
end
B = unique(A, 'rows');
B = B(1:10,:);
returns:
B =
1 2 3 4
1 2 4 3
1 3 4 2
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
some additional remarks for the choice of N:
I made some test runs, where I used the loop above to find all permutations like perms does. For vector lengths of n=4 to n=7 and in each case N = factorial(n): 60-80% of the rows are unique.
So for small n I would recommend to choose N as follows to be absolutely on the safe side:
N = min( [Q factorial(n)] )*2;
where Q is the number of permutations you want. For bigger n you either run out of memory while searching for all permutations, or the desired subset is so small compared to the number of all possible permutations that repetition is very unlikely! (Cosmic Ray theory linked by Luis Mendo)
Your requirements are very loose and allow many different possibilities. The most efficient solution I can think off that meets these requirements is as follows:
p = perms(1:6);
[p(1:128,:) repmat(7:32,128,1)]

Subtotal Calculation in Matlab

I would like to take subtotal of table in matlab. If the values of two columns are equal, take the value and add if there is an entry.
If we give an example, source matrix is as follows:
A = [1 2 3;
1 2 2;
1 4 1;
2 2 1;
2 2 3];
The output would look like this:
B = [1 2 5;
1 4 1;
2 2 4];
If the first two columns are equal, sum the third column. Is there a simple way of doing, without having to loop several times?
You can do this with a combination of unique and accumarray:
%# find unique rows and their corresponding indices in A
[uniqueRows,~,rowIdx]=unique(A(:,1:2),'rows');
%# for each group of unique rows, sum the values of the third column of A
subtotal = accumarray(rowIdx,A(:,3),[],#sum);
B = [uniqueRows,subtotal];
You can use unique to get all of the groups, then splitapply to sum them
[u, ~, iu] = unique( A(:,1:2), 'rows' ); % Get unique rows & their indices
sums = splitapply( #sum, A(:,3), iu ); % Sum all values according to unique indices
output = [u, sums]
% >> output =
% output =
% 26 7 124
% 26 8 785
% 27 7 800
This is a late answer because a duplicate question has just been asked so I posted here instead. Note that splitapply was introduced in R2015b, so wasn't around when the accumarray solution was posted.