views:

126

answers:

3

I have a collection of 43 to 50 numbers ranging from 0.133 to 0.005 (but mostly on the small side). I would like to find, if possible, all combinations that have a sum between L and R, which are very close together.*

The brute-force method takes 243 to 250 steps, which isn't feasible. What's a good method to use here?

Edit: The combinations will be used in a calculation and discarded. (If you're writing code, you can assume they're simply output; I'll modify as needed.) The number of combinations will presumably be far too large to hold in memory.

* L = 0.5877866649021190081897311406, R = 0.5918521703507438353981412820.

+1  A: 

You haven't given us enough information. But it sounds like you are in trouble if you actually want to OUTPUT every possible combination. For example, consistent with what you told us are that every number is ~.027. If this is the case, then every collection of half of the elements with satisfy your criterion. But there are 43 Choose 21 such sets, which means you have to output at least 1052049481860 sets. (too many to be feasible)

Certainly the running time will be no better than the length of the required output.

Aaron
+1 This is a valid proof.
Alin Purcaru
And 50 choose 25 is about 126 trillion... :-)
Aaron
But if you could come up with an algorithm that is O(the size of the output), then for problems with only a small number of solution sets it might be entirely tractable. Which would be nice. If the input demands a very large output, then of course you're right that it will take a long time, but arguably that's the caller's problem.
Steve Jessop
There might be a trillion combinations, that would be an acceptable number.
Charles
2^21.5 operations would be easy, it won't be that quick.
Charles
@Aaron: I hope to not go up to 50, but if I had to 200 trillion would be acceptable.
Charles
A: 

Actually, there is a quicker way around this:

(python)

sums_possible = [(0, [])]
# sums_possible is an array of tuples like this: (number, numbers_that_yield_this_sum_array)
for number in numbers:
    sums_possible_for_this_number = []
    for sum in sums_possible:
        sums_possible_for_this_number.insert((number + sum[0], sum[1] + [number]))
    sums_possible = sums_possible + sums_possible_for_this_number

results = [sum[1] for sum in sums_possible if sum[0]>=L and sum[1]<=R]

Also, Aaron is right, so this may or may not be feasible for you

Gabi Purcaru
This is not good because sums_possible grows exponentially.
Alin Purcaru
I need to generate all the combinations, but I can't hold them all in memory at a time. Can the code be modified to output them as they're calculated, using only (say) 1 GB of memory?
Charles
you could make some small improvements, like `sums_possible = [sum in sums_possible if sum <= R]` (removing sums that are already too high) in the first loop, but I don't know how much of an improvement it would be. Other than that, I can't see what can be done ..
Gabi Purcaru
+1  A: 

The basic idea is to convert it to an integer knapsack problem (which is easy).

Choose a small real number e and round numbers in your original problem to ones representable as k*e with integer k. The smaller e, the larger the integers will be (efficiency tradeoff) but the solution of the modified problem will be closer to your original one. An e=d/(4*43) where d is the width of your target interval should be small enough.

If the modified problem has an exact solution summing to the middle (rounded to e) of your target interval, then the original problem has one somewhere within the interval.

Rafał Dowgird
Finding one solution is a knapsack problem, but the question asks to find them all.
Aaron
Agreed, but even if there really are that many (on average there won't), then at least you can generate them effectively.
Rafał Dowgird