[QUOTE=Omphaloskeptic]
You want to generate sums upward from the bottom. Maintain two data structures: a list minimal-sums (which will become your desired output, and can just be a linked list) and a set possible-next-sums (which should be able to efficiently maintain a sorted state on random-access insertion and deletion). Begin with both of these structures empty. Start at the bottom vertex <0…0>=0.
Now repeat, M+1 times (the first time through just gives you 0; the next M times give you the lowest M sums):[ol][li]Append <v>=s to minimal-sums.[/li][li]Insert all upward neighbors of <v>=s into possible-next-sums (unless already present).[/li][li]Find a minimal sum <v>=s in possible-next-sums.[/li][li]Remove <v>=s from possible-next-sums.[/ol]You can end the final iteration after step 1 if you want.[/li]
The set possible-next-sums will usually get large, eventually. But since each vertex has only O(N) upward neighbors, in M steps it will only get to size O(MN). Efficient access, insertion, and deletion into such a set should be O(log MN). So each iteration should take[ol][li]O(1)[/li][li]O(N log MN)[/li][li]O(log MN)[/li][li]O(log MN)[/ol]and the overall algorithm should run in O(MN log MN).[/li][/QUOTE]
As I mentioned above, the problem reduces to looking at the sequence for x[k] where k = 1 to M, so, effectively, N = M, which means that your algorithm takes O(M^2 log(M^2))
I came up with a similar algorithm, but I think it is takes less time.
I have a list of ‘candidates’ (similar to your possible-next-sums) that are separated into two parts: a ‘node’ and an ‘x_idx’ (an index into the x[k] sequence.) Each candidate also contains a ‘value’ field, which is the value of the sum using the indexes in ‘node’ and ‘x_idx’.
For example, for a candidate 3-tuple, the ‘node’ might be [2 5] and the x_idx may be 7, which means that, if this 3-tuple is chosen, the next sum in the sequence is the 3-tuple [2 5 7] (i.e. x[2]+x[5]+x[7]), and this candidate’s x_idx is then incremented to 8.
Below is the code in Matlab
function [sorted_sums] = sort_sums(sorted_values, M)
% Add the first candidate
candidates(1).node = [];
candidates(1).x_idx = 1;
candidates(1).value = sorted_values(1);
for I=1:M,
[junk,min_idx] = min([candidates.value]);
node = candidates(min_idx).node;
x_idx = candidates(min_idx).x_idx;
value = candidates(min_idx).value;
% Update sorted_sums
sorted_sums(I).indexes = [node x_idx];
sorted_sums(I).value = value;
% Update candidates
% First, update the one currently chosen
candidates(min_idx).x_idx = x_idx + 1;
candidates(min_idx).value = value + ...
sorted_values(x_idx+1) - sorted_values(x_idx);
% Next, add a new candidate
candidates(I+1).node = sorted_sums(I).indexes;
candidates(I+1).x_idx = x_idx + 1;
candidates(I+1).value = value + sorted_values(candidates(I+1).x_idx);
end
We go through the code M times, and in each pass, we have to find the min of a sequence. I assume this can be done in O(length of sequence) time. Since one candidate is added to the sequence in each pass, that means that after M passes, the sequence will be length M, which means that finding the min should take O(M) time.
So, overall, this algorithm should take O(M^2)
Also, if we’re careful and the ‘candidates’ list is structured properly, maybe the min can be found in O(log(M)), which would make the overall time O(M log(M))
I think the primary difference between my algorithm and yours is that ‘candidates’ grows to size M, while ‘possible-next-sums’ grows to M^2
Using the example you gave of {2,3,5,7,15} and M = 6, we get the following output
Step 1:
Candidates: ([] -> 1)
sum = 2 (indexes = [1])
Step 2:
Candidates: ([] -> 2), ([1] -> 2)
sum = 3 (indexes = [2])
Step 3:
Candidates: ([] -> 3), ([1] -> 2), ([2] -> 3)
sum = 5 (indexes = [3])
Step 4:
Candidates: ([] -> 4), ([1] -> 2), ([2] -> 3), ([3] -> 4)
sum = 5 (indexes = [1 2])
Step 5:
Candidates: ([] -> 4), ([1] -> 3), ([2] -> 3), ([3] -> 4), ([1 2] -> 3)
sum = 7 (indexes = [4])
Step 6:
Candidates: ([] -> 5), ([1] -> 3), ([2] -> 3), ([3] -> 4), ([1 2] -> 3), ([4] -> 5)
sum = 7 (indexes = [1 3])