从一个区间列表中,找出一组中的每一个区间与该区间中的所有区间重叠的所有区间集

2024-04-29 15:57:30 发布

您现在位置:Python中文网/ 问答频道 /正文

与其查询具有开始日期和结束日期的间隔列表以从仅与搜索开始日期和结束日期重叠的列表中检索所有时间间隔,最好的方法是:

From a list of date intervals, 
Find all unique sets of intervals
Where every interval in each set overlaps with each other interval in that set

使用一个整数示例,以整数间隔列表[{1,3},{2,4},{4,5},{5,7},{6,8}]。从该列表中,以下是所有唯一的间隔集,其中每个间隔彼此重叠

^{pr2}$

下面是DateInterval的类:

from datetime import datetime
class DateInterval(object):
    def __init__(self, start_time, end_time):
        self.start_time = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
        seld.end_time = datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S')

    ''' eq, gt, hash methods removed for clarity '''

我将收到按开始时间升序排列的间隔列表,如下所示:

intervals = [DateInterval(start_time='2015-01-01 08:00:00', end_time='2015-01-01 08:30:00'),
             DateInterval(start_time='2015-01-01 08:00:00', end_time='2015-01-01 10:00:00'),
             DateInterval(start_time='2015-01-01 09:00:00', end_time='2015-01-01 11:00:00'),
             DateInterval(start_time='2015-01-01 10:00:00', end_time='2015-01-01 12:00:00'),
             DateInterval(start_time='2015-01-01 13:00:00', end_time='2015-01-01 16:00:00'),
             DateInterval(start_time='2015-01-01 14:00:00', end_time='2015-01-01 17:00:00'),
             DateInterval(start_time='2015-01-01 15:00:00', end_time='2015-01-01 18:00:00'),
             DateInterval(start_time='2015-01-01 20:00:00', end_time='2015-01-01 22:00:00'),
             DateInterval(start_time='2015-01-01 20:00:00', end_time='2015-01-01 22:00:00')
             ]

(在这个示例列表中,开始和结束日期总是均匀地落在一个小时上。然而,它们可以在任何一秒着陆(或者可能是毫秒)。在搜索了stackoverflow上关于重叠间隔的问题的详尽列表后,I found the Interval Tree to be unsuitable for Date Intervals)。在

我稍微优化的蛮力方法包括三个任务

  1. 识别区间的所有非唯一集,其中每个区间中至少有一个区间与该区间内的所有其他区间重叠
  2. 对步骤1的结果进行重复数据消除,以找到间隔中的所有唯一集,其中每个集中至少有一个间隔与该集中的所有其他间隔重叠
  3. 从1的结果中,只找出一个集合中的每个间隔与该集合中所有其他间隔重叠的集合

1.

下面通过天真地将间隔列表中的每个间隔与所有其他间隔进行比较,查找每个集中只有一个间隔与该集中的其他间隔重叠的所有非唯一集。它假定间隔列表是按日期时间升序排序的,这样可以实现break优化

def search(intervals, start_date, end_date):
    results = []
    for interval in intervals:
        if end_date >= interval.start_time:
            if start_date <= interval.end_time:
                results.append(interval)
        else:
            break # This assumes intervals are sorted by date time ascending

search的用法如下:

brute_overlaps = []
for interval in intervals:
    brute_overlaps.append(search(intervals, interval.start_time, interval.end_time))

2.

以下是对集合列表的重复数据消除:

def uniq(l):
    last = object()
    for item in l:
        if item == last:
            continue
        yield item
        last = item

def sort_and_deduplicate(l):
    return list(uniq(sorted(l, reverse=True)))

3.

下面通过简单地将一个集合中的每个间隔与该集合中的其他间隔进行比较,找出每个集合中的每个间隔与该集合中所有其他间隔重叠的所有集合:

def all_overlap(overlaps):
    results = []
    for overlap in overlaps:
        is_overlap = True
        for interval in overlap:
            for other_interval in [o for o in overlap if o != interval]:
                if not (interval.end_time >= other_interval.start_time and interval.start_time <= other_interval.end_time):
                    is_overlap = False
                    break # If one interval fails
             else:        # break out of
                 continue # both inner for loops
             break        # and try next overlap

        if is_overlap: # all intervals in this overlap set overlap with each other
            results.append(overlap)
    return results

Tags: in列表fordate间隔iftimestart
1条回答
网友
1楼 · 发布于 2024-04-29 15:57:30

一组区间,其中每个区间必须与该区间中的其他区间重叠,这将有一个共同点:它们都重叠。相反,查询一个点上的所有间隔将得到一组所有相互重叠的间隔。在

考虑到这一点,您的问题就归结为“通过更改查询点,我可以得到哪些不同的间隔子集?”。获取所有这些不同子集的一个简单方法是找到重叠间隔必须更改的事件的位置,并在每对事件之间的某个点进行查询。在

在间隔的情况下,事件对应于任何间隔开始或任何间隔结束。所以,你只需扫描从左到右开始和停止的间隔,同时跟踪一组已经开始但没有结束的间隔。这给了你所有最大的相互重叠的子集。在

在伪代码中。。。在

maximalMutuallyOverlappingSubsets =
    intervals
    .flatMap(e => [(e.start, e, true),
                   (e.end, e, false)])
    .sortedBy(e => e[0]).
    .scan({}, (prevSet, (x, interval, add)) =>
        if add
        then prevSet + {interval}
        else prevSet - {interval})
    .distinct() - {{}}

O(n lg n)时间内运行,排序是最昂贵的步骤。在

如果您不熟悉,flatMap将列表的每个项投影到结果集合中,然后将所有这些结果集合的项连接在一起。Scan从给定的累加器开始,并将下一个项与给定函数组合到累加器中,同时生成中间结果。在

相关问题 更多 >