Reputation: 51
I have a vector A
, that is
A = [300; 165; 150; 150; 400; 300; 80; 250; 165; 80; 200]
I am trying to find a set of vectors that are composed of the elements of this vector A
so that their elements sum up to a value as close as possible to 400 and so that all elements of vector A
are included in the disjoint set of vectors.
For example, 400 is already 400, so this is first set of vectors without a slack.
Another set would be the vector of [250 150]
, their sum is 400.
Another two can be two sets of the vector [300 80]
, their sum is 380, so a slack of 20 is compromised.
Another would be [165 165]
, they sum up to 330, with a slack of 70. The last one would be 200 and 150, with a slack of 50. The total slack is 20+20+70+50=160.
I'm trying to find a heuristic or an algorithm (not a programming model) that would minimize the slack. I'm coding in Matlab.
Upvotes: 0
Views: 142
Reputation: 1033
You could try something like this:
v = [300; 165; 150; 150; 400; 300; 80; 250; 165; 80; 200];
binarystr = dec2bin(1:(2^(length(v))-1));
bincell = mat2cell(binarystr,ones(size(binarystr ,1),1),ones(size(binarystr ,2),1));
bin = cellfun(@(x) str2double(x),bincell);
Now you can multiply to find all the combinations:
comb = b*v;
Find the minimum
target = 400;
[val,index] = min(abs(comb-target));
if you want to know what the combination was the you can look for the indexes:
idxs = find(bin(index,:));
and the values are:
disp(idxs)
disp(v(idxs))
Hope this helps.
Upvotes: 1
Reputation: 4510
So I thought this was a very interesting problem and started it at work (I hope my boss won't find out), but I am missing a part. The code is pretty much horrible, but I wanted to show the concept I guess.
A = [300; 165; 150; 150; 400; 300; 80; 250; 165; 80; 200] ;
P = (1 - (sum(A) /400 - floor(sum(A)/400))) * 400; %//minimum slack to be achieved
%//Round 1
G1 = zeros(floor(sum(A)/400)+1,3)
for t = 1:floor(sum(A)/400)+1
if size(A,1) > 1
%//single combination
[F indF] = min(abs(A-400));
%//double combination
if size(A >1)
D = combntns(A,2);
sumD = sum(D,2);
[F2 indF2] = min(abs(sumD-400));
end
%//triple combination
if size(A >2)
T = combntns(A,3);
sumT = sum(T,2);
[F3 indF3] = min(abs(sumT-400));
end
%remove 1
[R removeInd] = min([F,F2,F3]);
if removeInd == 1
G1(t,1) = A(indF);
A(indF) =[];
else if removeInd ==2
G1(t,1:2) = D(indF2,:) ;
[tmp,tmp2] = intersect(A,G1(t,:));
A(tmp2) = [];
else removeInd == 3
G1(t,:) = T(indF3,:) ;
[tmp,tmp2] = intersect(A,G1(t,:));
A(tmp2) = [] ;
end
end
else if size(A,1) == 1
G1(t,1) = A;
end
end
end
</pre></code>
the results:
>>400 0 0
150 250 0
165 150 80
300 80 0
165 200 0
300 0 0
The reason the results were wrong is because I searched for subsets with length of 1,2 and 3. 4 is not possible, since it produce huge results (but you can include it anyways). If I switch to subsets with length of 1 and 2 I get the right answer. So I think the step I am missing is how long my subsets can be.
results when max length of subset is set to 2:
>>400 0 0
150 250 0
300 80 0
300 80 0
165 200 0
165 150 0
All you have to do is % out the triple combination, and change this line : [R removeInd] = min([F,F2]); without F3
Upvotes: 0