多个范围列表的相交和补码

问题描述

我有这样一个问题:我有多个区间列表,现在我想获取所有可能的交集和补集。例如,如果有两个列表:

a = [[0,2],[5,10],[13,23],[24,25]]
b = [[1,5],[8,12],[15,18],[20,24]]

The output should be
output_a = [[0,1],[1,8],15],[18,20],25]]
output_b = [[1,[2,[10,[23,24]]

依次类推n个列表

为了可视化:

    List_a           |-----------|     |----------|
    List_b                  |------------|   |-------|
    List_c           |---------|     |------------|

       Set           |------|--|-|---|-|-|---|----|--|

  Output_a           |------|--|-|     |-|---|----|
  Output_b                  |--|-|---|-|-|   |----|--|
  Output_c           |------|--|     |-|-|---|----|

我的带有Python set()的伪代码

Get all (start,end) of all intervals in all list into a set()
for each list:
    for intervals in list:
        for each element in set:
            if element in intervals:
                ->store element in dict

但是这似乎不是高效而优雅的,因为它需要3个for循环

能否请您为这种情况建议一个更好的算法?非常感谢

解决方法

您的示例输出似乎与您的图片不匹配:根据您的图片,元素[24,25]不应位于output_b中,因为b停在24。图片[5,8]不应位于output_b中,因为b不会与该间隔相交。

这是一个根据您的图片生成列表的代码。该代码还具有三个嵌套循环,但是对于每个列表,它仅遍历该列表,并且仅一次遍历该列表,因此两个最里面的循环仅具有线性复杂度,而不是二次的。想法是对断点的并集进行排序,然后同时扫描两个列表。假定原始列表中的间隔不重叠并且按递增顺序排列。

a = [[0,2],[5,10],[13,23],[24,25]]
b = [[1,5],[8,12],[15,18],[20,24]]

lists = [a,b]

# collect all endpoints first into a set and the into a sorted sequence
breaks = set()
for l in lists:
    for i in l:
        breaks.update(i)
breaks = sorted(breaks)

for l in lists:
    output = []
    b = 0
    # For each interval
    for start,end in l:
        # Advance b until it falls into this interval:
        while breaks[b] <= start: b += 1
        # Now collect all sub-intervals from
        while b < len(breaks) and breaks[b] <= end:
            output.append([start,breaks[b]])
            start = breaks[b]
            b += 1
    print(output)

输出为

[[0,1],[1,8],15],[18,20],25]]
[[1,[2,[10,[23,24]]
,

(注意:我不完全理解这个问题以及您如何构成output_aoutput_b。在此“解决方案”中,我将使用我开发的库来提供建筑用砖,希望对您有所帮助,但请记住,此“解决方案”的输出与您提供的内容不完全匹配!)

我是portion的维护者,这是一个Python库,提供间隔(和间隔集)的数据结构和操作。该库特别支持间隔的自动简化,从而导致输出结果与建议的结果不同。

让我们导入库,并定义您的列表:

>>> import portion as P
>>> a = [[0,25]]
>>> b = [[1,24]]

现在,我们将这些列表转换为间隔的(分离):

>>> a = P.Interval(*[P.closed(x,y) for x,y in a])
>>> b = P.Interval(*[P.closed(x,y in b])
>>> a
[0,2] | [5,10] | [13,23] | [24,25]
>>> b
[1,5] | [8,12] | [15,18] | [20,24]

让我们计算交集及其补数:

>>> intersection = a & b
>>> complement = ~intersection
>>> intersection
[1,2] | [5] | [8,10] | [15,23] | [24]
>>> complement
(-inf,1) | (2,5) | (5,8) | (10,15) | (18,20) | (23,24) | (24,+inf)

由于您并不在乎无限性,因此我们将补数限制为ab的范围。为此,我们使用a | b的包围(包围是包含给定间隔的最小原子间隔):

>>> (a | b).enclosure
[0,25]
>>> complement = complement & (a | b).enclosure
>>> complement
[0,25]

基于intersectioncomplement,您将拥有所有可能的“断点”(及其“原点”)。