MATLAB: Print all possible matrix combinations for a given matrix

MATLAB and Simulink Student Suitematrix permutations manipulations combinations perms

Hi everybody, i am going mad at trying to solve this problem:
For a given input vector P, for example in my case
P=[4.5; 4.5; 5.25; 5.25; 2.25; 0.6; 0.5; 0.6; 0.5; 0.3; 1; 2; 0.5; 0.5]
i create a matrix that HAS TO HAVE 3 columns and contain all P elements (order isn't important) and zero in the free places, for example in my case
G =
4.5000 4.5000 5.2500
5.2500 2.2500 0.6000
0.5000 0.6000 0.5000
0.3000 1.0000 2.0000
0.5000 0.5000 0
my goal is to swap matrix elements between columns to reach the disposition that satisfies this property: say that the sum of all elements in vector P is S.
The disposition should satisfy the property that the sum of all elements in column 1 must be as close as possible to S/3 and the same for column 2 and column 3.
; ;
my first attempt was trying to generate all possible matrix disposition or permutation an find this memorizing the matrix that get closer to my goal by while cycle with this condition
the problem is that using perms(G) for generating it unfortunately I get out of memory because perms generates ALL possible permutation but i need only combination of n element i 3 groups that is much less than all possible combination but i don't know how to create it
Example of one possible output found by hand (order in elements of the same column doesn't matter):
G =
4.5000 5.2500 5.2500
4.5000 2.2500 0.6000
0.5000 0.6000 1.0000
0.3000 0.5000 2.0000
0 0.5000 0.5000
sum_colon1=9.8
sum_colon2=9.1
sum_colon3=9.35
eps_colon1=0.383
eps_colon2=0.316
eps_colon3=0.066
minimum=0.766

Best Answer

My comment to John made me realise what an efficient way to go about it would be. The crux of the problem is creating the unique permutations of repelem(1:numberofsets, setsize) or finding unique permutations of a set with repetition. I'm sure that there's something to do that already on the file exchange but I can't find it.
I've left my original answer below, but don't use it. This is much faster and more efficient. Note that the code for finding unique permutations is heavily inspired by this post. First the helper function that generates the unique permutations
function partitions = uniquesets(setcount, setlength)
%generate unique partitions of P sets of length N
%number of partitions = factorial(P*N) / factorial(N)^P
%setcount: the number of sets in the partition
%setlength: the length of each set in the partition
%partitions: a K*L array, where K = setcount*setlength and L = factorial(setcount*setlength) / factorial(setlength)^setcount. Each column is a partition. Rows are set indices from 1 to setcount
lefttoplace = setcount * setlength;
partitions = zeros(lefttoplace, 1);
nperms = 1;
for subset = 1:setcount
locs = nchoosek(1:lefttoplace, setlength).';
[emptyrows, ~] = find(~partitions);
emptyrows = reshape(emptyrows, [], nperms);
destrows = emptyrows(locs, :);
nperms = nperms * size(locs, 2);
destcols = repelem((1:nperms)', setlength);
partitions = repelem(partitions, 1, size(locs, 2)) + accumarray([destrows(:), destcols], subset);
lefttoplace = lefttoplace - setlength;
end
end
Then the code to use it:
P = [4.5; 4.5; 5.25; 5.25; 2.25; 0.6; 0.5; 0.6; 0.5; 0.3; 1; 2; 0.5; 0.5];
phase = 3;
P = [P; zeros(mod(-numel(P), phase), 1)];
partitions = uniquesets(phase, numel(P)/phase);
[~, order] = sort(partitions, 1);
allmatrices = reshape(P(order), numel(P)/phase , phase, []);
colsum = sum(allmatrices, 1);
distance = sum(abs(colsum - sum(P)/3), 2);
[minsum, whichmatrix] = min(distance)
chosenmatrix = allmatrices(:, :, whichmatrix)
mincolsum = colsum(1, :, whichmatrix)
This is very quick, about half a second on my machine and will work for much longer P.
It still does create a few duplicate matrices as duplicate numbers in P are still considered distinct for the purpose of partitioning. However, it does not creat unecessary partitions.
-------
Original answer, which wasn't efficient at all
Bearing in mind this is not my domain at all (this is a problem for John), here is a solution that works for this problem size but is probably very inefficient.
It uses this fileexchange entry to find all partitions of size 3 of your P vector. This is very inefficient since of these, you only want the partitions where each subset has size 5. Still, it's better than computing all permutations. In addition, duplicate numbers are considered distinct for the partitioning problem, again giving you more partitions than necessary:
P = [4.5; 4.5; 5.25; 5.25; 2.25; 0.6; 0.5; 0.6; 0.5; 0.3; 1; 2; 0.5; 0.5];
parts = partitions([P;zeros(mod(-numel(P), 3), 1)], 3); %generates 2375101 partitions, takes a little while
parts = parts(cellfun(@(c) all(cellfun('length', c) == 5), parts)); %only keep partitions with subsets of length 5, there are 126126 of these. Also takes a while
allmatrices = cellfun(@(c) [c{:}], parts, 'UniformOutput', false); %convert each partition into a 5x3 matrix
allmatrices = cat(3, allmatrices{:}); %and convert into a 5x3x126126 matrix.
%note that some matrices are duplicated since as said, duplicate numbers were considered distinct for the partitioning purpose
[minsum, which] = min(sum(abs(sum(allmatrices, 1) - sum(P)/3), 2))
selectedmatrix = allmatrices(:, :, which)
columnsum = sum(selectedmatrix, 1)
columneps = columnsum - sum(P)/3
minsum =
0.76667
which =
29036
selectedmatrix =
4.5 5.25 5.25
4.5 2.25 1
0.5 0.6 2
0.3 0.6 0.5
0 0.5 0.5
columnsum =
9.8 9.2 9.25
columneps =
0.38333 -0.21667 -0.16667