问题描述
我有这样一个问题:我有多个区间列表,现在我想获取所有可能的交集和补集。例如,如果有两个列表:
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_a
和output_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)
由于您并不在乎无限性,因此我们将补数限制为a
和b
的范围。为此,我们使用a | b
的包围(包围是包含给定间隔的最小原子间隔):
>>> (a | b).enclosure
[0,25]
>>> complement = complement & (a | b).enclosure
>>> complement
[0,25]
基于intersection
和complement
,您将拥有所有可能的“断点”(及其“原点”)。