重叠间隔

时间:2014-10-22 03:40:22

标签: python algorithm intervals overlap

我试图写一些代码去重叠(开放)间隔。我在Algorithm to shift overlapping intervals until no overlap is leftPossible Interview Question: How to Find All Overlapping Intervals中找到了灵感。

间隔代表物理实体。他们的位置是通过某种方式估计的,但这种不完美的估计导致这些物理实体的重叠位置。但是,实际上这些物理实体不能占用相同的空间,因此我使用此代码重新调整其位置。这种调整应尽可能少地移动这些物理实体,并尽可能保持其估计的相对位置。由于这些是物理实体,因此每个间隔的长度不能改变。

代码在大多数情况下运行良好,但在某些情况下会挂起:

intervals = [(0, 8), (9, 13), (11, 14), (15, 21)]

这是我的python代码。有什么建议吗?

def intervalLength(interval):
'''
Finds the length of an interval, supplied as a tupple
'''
return interval[1]-interval[0]

def findOverlappingIntervals(intervals):
# https://stackoverflow.com/questions/4542892/possible-interview-question-how-to-find-all-overlapping-intervals?rq=1
# Throw the endpoints of the intervals into an array, marking them as either start- or end-points.
# Sort them by breaking ties by placing end-points before start-points if the intervals are closed, or the other way around if they're half-open.
'''
Takes a list of intervals and returns the intervals that overlap.
List returned has nested list composed of the intervals that overlap with each other
Assumes list is ordered by the start position of the intervals
'''
end_points = []
for n,i in enumerate(intervals):
    end_points.append((i[0],'b',n)) #'b' = beginning of interval
    end_points.append((i[1],'e',n)) #'e' = end of interval
end_points.sort()
b = 0
e = 0
overlapping = [set()]
open_intervals = set()
for ep,sORe,i in end_points:
    if sORe == 'b':
        b += 1
        open_intervals.add(i)
        if b-e > 1 and i in open_intervals:
            overlapping[-1].update(open_intervals)
        elif len(overlapping[-1]) > 0:
            overlapping.append(set())
    else:
        e += 1
        open_intervals.remove(i)

overlapping = [o for o in overlapping if len(o) > 0]
overlapping = [[intervals[i] for i in o] for o in overlapping]

return overlapping

def deOverlap(intervals):
'''
Takes a list of overlapping intervals and returns a new list with updated postions
without overlap and intervals separated by 1, maintaining the previous center
'''
# Find center of overlapping intervals
avg = (reduce(lambda x,y: x+y, [a+b for a,b in intervals])+len(intervals)-1)/(len(intervals)*2.0)

# Find the total length of the ovrlapping intervals
tot_length = reduce(lambda x,y: x+y, [intervalLength(i) for i in intervals]) + len(intervals) - 1

# Find new start position for the overlapping intervals
new_start = int(round(avg-(tot_length/2.0)))

# Place first interval in new position
non_over_intervals = [(new_start, new_start+intervalLength(intervals[0]))]

# Place rest of intervals in new positions
for i in intervals[1:]:
    start = non_over_intervals[-1][1]+1
    non_over_intervals.append((start,start+intervalLength(i)))

return non_over_intervals

def deOverlapIntervals(intervals):
'''
Takes a list of intervals and returns a list with the same intervals with no overlap and
located as close to the original locations as possible
'''
non_over_intervals = intervals
i = 0
while len(findOverlappingIntervals(non_over_intervals)) > 0:
    if i >= 10000:
        print 'Tried 10,000 times and did not finish de-overlapping. Returning best I could do'
        return non_over_intervals
    overlapping_intervals = findOverlappingIntervals(non_over_intervals)
    non_over_intervals = set(non_over_intervals) - set([oi for group in overlapping_intervals for oi in group])
    for oi in overlapping_intervals:
        non_overlapping = deOverlap(oi)
        non_over_intervals.update(non_overlapping)
    non_over_intervals = list(non_over_intervals)
    non_over_intervals.sort()
    i += 1
return non_over_intervals

这些间隔运行良好

intervals = [(-5,-1), (0,6), (7,11), (9,14), (12,17), (21,24), (27,32), (32,36), (39,41)]

这些不是。它们挂起是因为间隔与左端或右端不断移动和重叠

intervals = [(0, 8), (9, 13), (11, 14), (15, 21)]

non_over_intervals = deOverlapIntervals(intervals)

3 个答案:

答案 0 :(得分:1)

你能做得更简单吗?像这样:

result = intervals[:1]

for begin, end in intervals[1:]:
    if begin <= result[-1][1]:
        result[-1] = (result[-1][0], end)
    else:
        result.append((begin, end))

答案 1 :(得分:1)

喜欢这个吗?

intervals = [(0, 8), (9, 13), (11, 14), (15, 21)]
intervals = [list(t) for t in intervals]

for i in range(len(intervals) - 1):
    offset = intervals[i + 1][0] - intervals[i][1]
    if offset < 1:
        diff = intervals[i + 1][1] - intervals[i + 1][0]
        intervals[i + 1][0] = intervals[i][1] + 1
        intervals[i + 1][1] = intervals[i + 1][0] + diff

print(intervals)

答案 2 :(得分:0)

我发现如果移动一个间隔会使它与另一个间隔重叠,那么包括原始重叠间隔和在单个deOverlapIntervals运行中与移位重叠的间隔将解决问题。