Reputation: 105
Problem : I have a list of tuples like [ (10,20) , (20,30) , (22,24) , (26,30) , (10,28) ] and I need to find a sublist of tuples with the minimal overlap which covers from 10,30 ( max and min are given )
I have some ideas about how to approach this problem but I was wondering if anyone can suggest any solution.
Upvotes: 0
Views: 240
Reputation: 4449
Interesting problem :-) Not trivial to solve. I don't see why it gets downvoted.
The following code will check all subsets, compute the continuous cover, overlap and the list of tuples that generated that cover + overlap.
Finally it gets sorted by overlap. So the first element in the output should be the one with the desired cover and the least overlap.
from itertools import *
import operator
def allsubsets(l):
for s in chain( *map(lambda r: combinations(l,r), xrange(1,len(l))) ):
yield s
# only count tuples that make consecutive ranges
def mk_cover(acc, item):
((curlo, curhi), tuplist, ovl) = acc
(newlo, newhi) = item
if newlo<=curhi:
# ok, the new item possibly extends the range
if newhi>=curhi:
overlap = curhi - newlo
else:
overlap = newhi - newlo
newhi = curhi
return ((curlo, newhi), tuplist+[item], ovl+overlap)
else:
# it doesn't so return the old accumulator
return acc
# return a function that will inspect lists-of-tuples
# to see if they cover the range lo -> hi. If they do,
# append the covering list and the overlap to an
# accumulator; a list of solutions found so far
def mk_finder(lo, hi):
def overlapper(acc, tuples):
# inspect the subset of tuples, wether they
# cover lo -> hi
# sort by start value
tuples = list(sorted(tuples, key=operator.itemgetter(0)))
((covlo, covhi), tl, ovl) = reduce(mk_cover, tuples[1:], (tuples[0], [tuples[0]], 0))
if covlo<=lo and covhi>=hi:
acc.append( ((covlo, covhi), tl, ovl) )
return acc
return overlapper
With the following input:
inp = [ (10,20) , (20,30) , (22,24) , (26,30) , (10,28) ]
# find all that cover 10,30
found = reduce(mk_finder(10, 30), allsubsets(inp), [])
# sort by overlap
found = [(tl, ovl) for (cov, tl, ovl) in sorted(found, key=operator.itemgetter(2))
print found
Yields this as output (yes, there are many subsets covering the range 10-30) showing that the combination (10,20) + (20,30) covers the range 10-30 with zero overlap:
[([(10, 20), (20, 30)], 0),
([(10, 28), (26, 30)], 2),
([(10, 20), (20, 30), (22, 24)], 2),
([(10, 20), (20, 30), (26, 30)], 4),
([(10, 28), (22, 24), (26, 30)], 4),
.... <snip>
]
Upvotes: 4