How would one go about generating a matrix with all the possible combinations of number totaling to a sum with repetition?
Basically, combinations of x1, x2, x3 such that x1 + x2 + x3 = n.
For example: n =3
0 1 2
0 2 1
1 0 2
1 2 0
1 1 1
Is there simple way of doing this using predefined Matlab functions?
I tried
n=6;
nchoosek(0:n,3)
which gives me
0 1 2
0 1 3
0 1 4
0 1 5
0 1 6
0 2 3
0 2 4
0 2 5
0 2 6
0 3 4
0 3 5
0 3 6
0 4 5
0 4 6
0 5 6
1 2 3
1 2 4
1 2 5
1 2 6
1 3 4
1 3 5
1 3 6
1 4 5
1 4 6
1 5 6
2 3 4
2 3 5
2 3 6
2 4 5
2 4 6
2 5 6
3 4 5
3 4 6
3 5 6
4 5 6
How would one extract all rows that have the total equal to n?
I think linear indexing or find should make it possible, but I don't know how to go about that.
Regards
For concreteness, let's go with your example of 3 values adding up to 6. The standard way to do this is to think of placing 2 'dividers' into a row of 6 identical 'objects': those dividers then divide the objects into 3 groups, and you can read off the length of each group. So all we need to do is enumerate all ways of placing those dividers. You can use nchoosek(1:8, 2) for this: each row of that matrix describes a division, by describing the positions of the 2 dividers amongst the 2 + 6 == 8 objects + dividers.
This is a more efficient approach than enumerating all triples of integers 0-6 and then picking out those that add to the correct total.
I don't really speak MATLAB, so the following is probably unidiomatic (and suggestions to improve it are welcome!), but something like this should work:
% Total we're aiming for.
n = 6;
% Number of pieces to divide that total into.
k = 3;
% All possible placements of internal dividers.
dividers = nchoosek(1:(n+k-1), k-1);
ndividers = size(dividers, 1);
% Add dividers at the beginning and end.
b = cat(2, zeros(ndividers, 1), dividers, (n+k)*ones(ndividers, 1));
% Find distances between dividers.
c = diff(b, 1, 2) - 1
And here are the results, as provided by this site:
c =
0 0 6
0 1 5
0 2 4
0 3 3
0 4 2
0 5 1
0 6 0
1 0 5
1 1 4
1 2 3
1 3 2
1 4 1
1 5 0
2 0 4
2 1 3
2 2 2
2 3 1
2 4 0
3 0 3
3 1 2
3 2 1
3 3 0
4 0 2
4 1 1
4 2 0
5 0 1
5 1 0
6 0 0
Use dec2base to generate all combinations with repetition, and logical indexing to keep only those with the desired sum:
n = 6;
m = 3;
c = dec2base(0:(n+1)^m-1,n+1,m)-'0'; %// generate combinations with repetition
result = c(sum(c,2)==n,:); %// keep those with desired sum. Logical indexing
I believe you are describing permutations of restricted integer partitions, though your example doesn't seem complete. For n=3 into three parts from elements {0, 1, 2} there are two solutions: {0, 1, 2} and {1, 1, 1}. These may further be permuted into:
{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}, {1, 1, 1}}
If this is not what you want you should clarify why these additional orderings are not to be included.
If this understanding is correct you should be able to find a number of resources by searching for that phrase. Some examples:
Elegant Python code for Integer Partitioning
Integer Partition in Java
Algorithm for generating integer partitions
Integer Partition Algorithm by Jerome Kelleher
Integer Partition Algorithm by Daniel Scocco
Fast Algorithms for Generating Integer Partitions (PDF) (looks heavy-duty)
Stony Brook Algorithm Repository - Partitions
As a practical example, using Mathematica one would write:
IntegerPartitions[6, {3}, Range[0, 5]]
Output:
{{5, 1, 0}, {4, 2, 0}, {4, 1, 1}, {3, 3, 0}, {3, 2, 1}, {2, 2, 2}}
These could each then be permuted to produce the other orderings.
It is quite easy to set up a recursive function to generate these partitions by starting with all numbers <= n, then appending values that are <= that value that do not exceed n. This is continued until each list is length p; if the list totals n it is kept; if it does not it is discarded. Again, in Mathematica:
f[n_, p_, c__] /; Length#{c} == p := If[+c == n, {{c}}, {}]
f[n_, p_, c___] := Array[f[n, p, c, #] &, Min[c, n - +c] + 1, 0, Join]
f[6, 3]
Output:
{{2, 2, 2}, {3, 2, 1}, {3, 3, 0}, {4, 1, 1}, {4, 2, 0}, {5, 1, 0}, {6, 0, 0}}
The function nchoosek provides the possible ways of selecting r-1 plus signs in the sum. For example, x1 + x2 + x3 = 5, then two signals must be selected over the sum 1 + 1 + 1 + 1 + 1 = 5. For nonnegative solutions make the transformation yi = x1 + 1, and solves y1+y2+ ... = m + n
The result of nchoosek provides the position of the plus sign.
The remaining of program provides the sum between selected signals.
clear all
close all
clc
% Program that generates the possible distributions
% M objects in r boxes
% Éderson D'Martin Costa
% 12/02/2015
% number of objects
m = 3;
% number of boxes
r = 3;
% total number of possibilities
% C = nchoosek(m+r-1,r-1)
v = 1:m+r-1;
C = nchoosek(v,r-1);
[l,c] = size(C);
Y = zeros(l,c+1);
Y(:,1) = C(:,1);
Y(:,end) = m+r-C(:,end);
for i = r-1:-1:2
Y(:,i) = C(:,i)-C(:,i-1);
end
X = Y-1;
display(X)
% sum(X,2)
As #Mark Dickinson pointed out. I just wanted to give intuitive explanation.
Your problem can be restated as "How many ways can you distribute 3 apples to 3 people"?
Suppose you have 3 people: AA, BB, CC and you want to distribute 3 apple among them. How can you do it?
AA | BB | CC
*** | |
* | * | *
** | * |
........
* -> represent apples.
Now if you think the problem is just selecting two | among 5 positions which we know can be done using 5 choose 2.
Related
I want to find all ways that n items can be split among m bins. For example, for n=3 and m=3 the output would be (the order doesn't matter):
[3 0 0
0 3 0
0 0 3
2 1 0
1 2 0
0 1 2
0 2 1
1 0 2
2 0 1
1 1 1]
The algorithm should be as efficient as possible, preferrably vectorized/using inbuilt functions rather than for loops. Thank you!
This should be pretty efficient.
It works by generating all posible splitings of the real interval [0, n] at m−1 integer-valued, possibly coincident split points. The lengths of the resulting subintervals give the solution.
For example, for n=4 and m=3, some of the possible ways to split the interval [0, 4] at m−1 points are:
Split at 0, 0: this gives subintervals of lenghts 0, 0, 4.
Split at 0, 1: this gives subintervals of lenghts 0, 1, 3.
...
Split at 4, 4: this gives subintervals of lenghts 4, 0, 0.
Code:
n = 4; % number of items
m = 3; % number of bins
x = bsxfun(#minus, nchoosek(0:n+m-2,m-1), 0:m-2); % split points
x = [zeros(size(x,1),1) x n*ones(size(x,1),1)]; % add start and end of interval [0, n]
result = diff(x.').'; % compute subinterval lengths
The result is in lexicographical order.
As an example, for n = 4 items in m = 3 bins the output is
result =
0 0 4
0 1 3
0 2 2
0 3 1
0 4 0
1 0 3
1 1 2
1 2 1
1 3 0
2 0 2
2 1 1
2 2 0
3 0 1
3 1 0
4 0 0
I'd like to suggest a solution based on an external function and accumarray (it should work starting R2015a because of repelem):
n = uint8(4); % number of items
m = uint8(3); % number of bins
whichBin = VChooseKR(1:m,n).'; % see FEX link below. Transpose saves us a `reshape()` later.
result = accumarray([repelem(1:size(whichBin,2),n).' whichBin(:)],1);
Where VChooseKR(V,K) creates a matrix whose rows are all combinations created by choosing K elements of the vector V with repetitions.
Explanation:
The output of VChooseKR(1:m,n) for m=3 and n=4 is:
1 1 1 1
1 1 1 2
1 1 1 3
1 1 2 2
1 1 2 3
1 1 3 3
1 2 2 2
1 2 2 3
1 2 3 3
1 3 3 3
2 2 2 2
2 2 2 3
2 2 3 3
2 3 3 3
3 3 3 3
All we need to do now is "histcount" the numbers on each row using positive integer bins to get the desired result. The first output row would be [4 0 0] because all 4 elements go in the 1st bin. The second row would be [3 1 0] because 3 elements go in the 1st bin and 1 in the 2nd, etc.
Can I shift rows in matrix A with respect to values in vector v?
For instance A and v specified as follows:
A =
1 0 0
1 0 0
1 0 0
v =
0 1 2
In this case I want to get this matrix from A:
A =
1 0 0
0 1 0
0 0 1
Every i-th row in A has been shifted by i-th value in v
Can I do this operation with native functions?
Or should I write it by myself?
I've tried circshift function, but I couldn't figure out how to shift rows separately.
The function circshift does not work as you want and even if you use a vector for the amount of shift, that is interpreted as the amount of shift for each dimension. While it is possible to loop over the rows of your matrix, that will not be very efficient.
More efficient is if you compute the indexing for each row which is actually quite simple:
## First, prepare all your input
octave> A = randi (9, 4, 6)
A =
8 3 2 7 4 5
4 4 7 3 9 1
1 6 3 9 2 3
7 4 1 9 5 5
octave> v = [0 2 0 1];
octave> sz = size (A);
## Compute how much shift per row, the column index (this will not work in Matlab)
octave> c_idx = mod ((0:(sz(2) -1)) .- v(:), sz(2)) +1
c_idx =
1 2 3 4 5 6
5 6 1 2 3 4
1 2 3 4 5 6
6 1 2 3 4 5
## Convert it to linear index
octave> idx = sub2ind (sz, repmat ((1:sz(1))(:), 1, sz(2)) , c_idx);
## All you need is to index
octave> A = A(idx)
A =
8 3 2 7 4 5
9 1 4 4 7 3
1 6 3 9 2 3
5 7 4 1 9 5
% A and v as above. These could be function input arguments
A = [1 0 0; 1 0 0; 1 0 0];
v = [0 1 2];
assert (all (size (v) == [1, size(A, 1)]), ...
'v needs to be a horizontal vector with as many elements as rows of A');
% Calculate shifted indices
[r, c] = size (A);
tmp = mod (repmat (0 : c-1, r, 1) - repmat (v.', 1, c), c) + 1;
Out = A(sub2ind ([r, c], repmat ([1 : r].', 1, c), tmp))
Out =
1 0 0
0 1 0
0 0 1
If performance is an issue, you can replace repmat with an equivalent bsxfun call which is more efficient (I use repmat here for simplicity to demonstrate the approach).
With focus on performance, here's one approach using bsxfun/broadcasting -
[m,n] = size(A);
idx0 = mod(bsxfun(#plus,n-v(:),1:n)-1,n);
out = A(bsxfun(#plus,(idx0*m),(1:m)'))
Sample run -
A =
1 7 5 7 7
4 8 5 7 6
4 2 6 3 2
v =
3 1 2
out =
5 7 7 1 7
6 4 8 5 7
3 2 4 2 6
Equivalent Octave version to use automatic broadcasting would look something like this -
[m,n] = size(A);
idx0 = mod( ((n-v(:)) + (1:n)) -1 ,n);
out = A((idx0*m)+(1:m)')
Shift vector with circshift in loop, iterating row index.
For example, I have a 4x6 matrix A:
A =
0 0 0 0 4 3
0 2 1 0 0 0
0 5 0 8 7 0
8 9 10 3 0 2
I want to find the lowest location within the rows of A where non-zero elements are found for each column. It should go like this:
column 1 => row 4
column 2 => row 2
column 3 => row 2
column 4 => row 3
column 5 => row 1
column 6 => row 1
And the result should look like the following vector:
Result = [4, 2, 2, 3, 1, 1]
Anyone has any idea how to obtain this?
One approach to solve for a generic case -
[valid,idx] = max(A~=0,[],1)
out = idx.*valid
Sample run -
A =
0 0 0 0 -4 3
0 2 1 0 0 0
0 5 0 8 7 0
0 9 10 3 1 2
out =
0 2 2 3 1 1
As seen from the sample run, for a case when there are all zeros (column-1), we get output of zero to indicate that there are no non-zeros in that column.
It also takes care of negative numbers (column-5).
This should do it:
A = [0, 0, 0, 0, 4, 3;
0, 2, 1, 0, 0, 0;
0, 5, 0, 8, 7, 0;
8, 9, 10, 3, 0, 2];
indices = repmat((1:size(A))', [1, size(A, 2)]);
indices(A == 0) = NaN;
min(indices, [], 1)
Here indices is:
indices =
1 1 1 1 1 1
2 2 2 2 2 2
3 3 3 3 3 3
4 4 4 4 4 4
We then set every element of indices to NaN wherever A is zero, which gives us:
indices =
NaN NaN NaN NaN 1 1
NaN 2 2 NaN NaN NaN
NaN 3 NaN 3 3 NaN
4 4 4 4 NaN 4
We then simply take the minimum of each column
I can think of one combination that uses ind2sub and unique with find, I'm sure there's a better way.
With your given A, assuming it's an array of integers:
[r, c] = ind2sub(size(A), find(A ~= 0));
[~, ia, ~] = unique(c);
result = r(ia)';
Returns:
result =
4 2 2 3 1 1
I did it this way because find returns the linear indices of the array it's searching. MATLAB arrays are stored column-major in memory, so it's as if each column is stacked on top of the other. ind2sub converts these linear indices back to their subscripts based on the size of the original array. Then I use unique to find the first instance of each column and return those row numbers only.
One possible solution, using sum and cumsum:
Result = sum(cumsum(A ~= 0) == 0) + 1;
What is the fastest and the simplest way to generate an array like
[0, 1, 3, 4, 6, 7, 9, 10, ...]
in MATLAB?
You can obtain the cumulative sum of the vector of steps (in your case it is [1 2 1 2 1 2 1 2 ...]). For example:
x = cumsum([0, repmat([1 2], 1, 4)])
x =
0 1 3 4 6 7 9 10 12
You can generate matrix with two rows: top row for odd array elements, bottom row for even elements. Than transform matrix into array with reshape.
>> a=[0:3:15; 1:3:16]
a =
0 3 6 9 12 15
1 4 7 10 13 16
>> a=reshape(a,1,12)
a =
0 1 3 4 6 7 9 10 12 13 15 16
Not one line but will work for either an odd or even number of total elements, and could be expanded if you wanted more than two different steps:
a = zeros(1,8);
a(1:2:end) = 0:3:10;
a(2:2:end) = 1:3:10;
Here is a simple and compact way:
A = 0:20;
A(3:3:end) = []
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I sort a 2-D array in MATLAB with respect to one column?
Sort a matrix with another matrix
I have a vector 'A' of 429 values and a matrix 'B' of 429x200 values. Rows in A and B are share the same indices. My vector 'A' contains values 1:1:429 but they are randomly ordered throughout the vector. I want to reorder A so that it indexes in order from 1 to 429 and I also want to sort the rows in matrix 'B' in the same order as the newly sorted 'A'.
Can this be done quick and easy without a for-loop?
Here's an example to illustrate my point:
A =
5
3
1
2
4
B =
3 7 0 4 6
1 2 5 0 8
4 0 2 0 0
3 0 1 0 5
2 2 3 4 4
sortedA =
1
2
3
4
5
sortedB =
4 0 2 0 0
3 0 1 0 5
1 2 5 0 8
2 2 3 4 4
3 7 0 4 6
Thank you everyone!
The example data:
A = [ 5, 3, 1, 2, 4 ]';
B = [ 3, 7, 0, 4, 6; 1, 2, 5, 0, 8; 4, 0, 2, 0, 0; 3, 0, 1, 0, 5; 2, 2, 3, 4, 4 ]
Sort the matrices:
[sortedA,IX] = sort(A);
sortedB = B(IX,:);
sortedA =
1
2
3
4
5
sortedB =
4 0 2 0 0
3 0 1 0 5
1 2 5 0 8
2 2 3 4 4
3 7 0 4 6