如何在python中查找范围重叠?

如何在python中查找范围重叠?,python,range,Python,Range,Python中确定两个范围中哪些值重叠的最佳方法是什么 例如: x = range(1,10) y = range(8,20) (The answer I am looking for would be the integers 8 and 9.) 给定一个范围x,迭代另一个范围y并输出两个范围共享的所有值的最佳方式是什么?提前谢谢你的帮助 编辑: 作为跟进,我意识到我还需要知道x是否与y重叠。我正在寻找一种方法来遍历范围列表,并对重叠的范围做一些额外的事情。是否有一个简单的对/错语句来完成

Python中确定两个范围中哪些值重叠的最佳方法是什么

例如:

x = range(1,10)
y = range(8,20)

(The answer I am looking for would be the integers 8 and 9.)
给定一个范围x,迭代另一个范围y并输出两个范围共享的所有值的最佳方式是什么?提前谢谢你的帮助

编辑:


作为跟进,我意识到我还需要知道x是否与y重叠。我正在寻找一种方法来遍历范围列表,并对重叠的范围做一些额外的事情。是否有一个简单的对/错语句来完成此操作?

尝试设置交叉点:

x = range(1,10)
y = range(8,20)
xs = set(x)
xs.intersection(y)   
输出:

set([8, 9])
请注意,
intersection
接受任何iterable作为参数(
y
不需要转换为操作的集合)。
有一个运算符相当于
交叉点
方法:
&
,但在本例中,它是。

一个选项是仅使用列表理解,如:

x = range(1,10) 
y = range(8,20) 

z = [i for i in x if i in y]
print z
您可以使用s来完成此操作,但请注意,
set(list)
会从
列表中删除所有重复的条目:

>>> x = range(1,10)
>>> y = range(8,20)
>>> list(set(x) & set(y))
[8, 9]
如果步骤始终为+1(这是范围的默认值),则以下操作应比将每个列表转换为一个集合或迭代任一列表更有效:

range(max(x[0], y[0]), min(x[-1], y[-1])+1)
对于“如果x与y重叠或不重叠”:

编辑1 速度比较:

from time import clock

x = range(-12,15)
y = range(-5,3)
te = clock()
for i in xrange(100000):
    w = set(x).intersection(y)
print '                     set(x).intersection(y)',clock()-te


te = clock()
for i in xrange(100000):
    w = range(max(x[0], y[0]), min(x[-1], y[-1])+1)
print 'range(max(x[0], y[0]), min(x[-1], y[-1])+1)',clock()-te
结果

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [10, 11, 12, 13]
    x does not overlap y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [9, 10, 11, 12, 13]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6, 7, 8, 9]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6, 7, 8]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [1, 2, 3, 4, 5, 6]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-3, -2, -1, 0, 1]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-3, -2, -1, 0]
    x does not overlap y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-11, -10, -9, -8, -7, -6]
    x does not overlap y
                     set(x).intersection(y) 0.951059981087
range(max(x[0], y[0]), min(x[-1], y[-1])+1) 0.377761978129

这些执行时间的比率为2.5

假设您专门处理范围,步骤为
1
,您可以通过数学快速完成

def range_intersect(range_x,range_y):
    if len(range_x) == 0 or len(range_y) == 0:
        return []
    # find the endpoints
    x = (range_x[0], range_x[-1]) # from the first element to the last, inclusive
    y = (range_y[0], range_y[-1])
    # ensure min is before max
    # this can be excluded if the ranges must always be increasing
    x = tuple(sorted(x))
    y = tuple(sorted(y))
    # the range of the intersection is guaranteed to be from the maximum of the min values to the minimum of the max values, inclusive
    z = (max(x[0],y[0]),min(x[1],y[1]))
    if z[0] < z[1]:
        return range(z[0], z[1] + 1) # to make this an inclusive range
    else:
        return [] # no intersection
def range\u intersect(range\u x,range\u y):
如果len(范围x)==0或len(范围y)==0:
返回[]
#找到端点
x=(范围x[0],范围x[-1])#从第一个元素到最后一个元素,包括
y=(范围为y[0],范围为y[-1])
#确保最小值在最大值之前
#如果范围必须始终增大,则可以排除此情况
x=元组(已排序(x))
y=元组(已排序(y))
#交叉点的范围保证从最小值的最大值到最大值的最小值(包括最小值)
z=(最大值(x[0],y[0]),最小值(x[1],y[1]))
如果z[0]

在一对范围内,每个范围内有超过10^7个元素,这需要不到一秒钟的时间,与重叠的元素数量无关。我尝试了10^8左右的元素,但我的电脑冻结了一段时间。我怀疑你是否会处理这么长的列表。

如果你想找到任意步骤的重叠范围,你可以使用我提供的包 一个与Python Range()兼容的Range()类,以及一些好东西,包括交叉点:

>>> from rangeplus import Range
>>> Range(1, 100, 3) & Range(2, 100, 4)
Range(10, 100, 12)
>>> Range(200, -200, -7) & range(5, 80, 2)  # can intersect with Python range() too
Range(67, 4, -14)
Range()也可以解除绑定(当stop为None时,范围继续+/-无穷大):


交集是计算的,而不是迭代的,这使得实现的效率独立于Range()的长度。

如果您查找两个实值有界区间之间的重叠,那么这非常好:

def overlap(start1, end1, start2, end2):
    """how much does the range (start1, end1) overlap with (start2, end2)"""
    return max(max((end2-start1), 0) - max((end2-end1), 0) - max((start2-start1), 0), 0)

我在任何地方都找不到这个,所以我提出了这个,我在这里发布。

这是step=1情况下的简单范围的答案(99%的时间),当使用集合比较长范围时(当您只想知道是否有重叠时),它可以比基准测试快2500倍

视觉帮助:

|  |           |    |
  |  |       |    |
基准:

x = range(1,10)
y = range(8,20)

In [151]: %timeit set(x).intersection(y)
2.74 µs ± 11.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [152]: %timeit range_overlapping(x, y)
1.4 µs ± 2.91 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
结论:即使是小范围,速度也要快一倍

x = range(1,10000)
y = range(50000, 500000)

In [155]: %timeit set(x).intersection(y)
43.1 ms ± 158 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [156]: %timeit range_overlapping(x, y)
1.75 µs ± 88.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

结论:在这种情况下,您希望使用范围重叠功能,因为它比快2500倍(我个人的加速记录)

上述答案似乎过于复杂。这个一行程序在Python3中工作得非常好,它将范围作为输入和输出。它还处理非法范围。要获得这些值,只需在结果上迭代(如果没有)

# return overlap range for two range objects or None if no ovelap
# does not handle step!=1
def range_intersect(r1, r2):
    return range(max(r1.start,r2.start), min(r1.stop,r2.stop)) or None

此解决方案生成的整数位于
O(1)
内存中任意数量的
range
对象的交点处

披露:我是在尝试其他东西后从中的一个用户那里得到的。。。不那么优雅

解决方案 测试 OP的问题

x = range(1,10)
y = range(8,20)
list(range_intersection(x, y))

[8, 9]
我的例子

limit = 10_000
list(range_intersection(
    range(2, limit, 2),
    range(3, limit, 3),
    range(5, limit, 5),
    range(41, limit, 41),
))

[1230, 2460, 3690, 4920, 6150, 7380, 8610, 9840]

指定范围的特征(步长始终等于+1?或者可以是-2?)我的范围都在+1步长中。嗯,
range
s无论如何都不包含重复项。此外,在施工期间使用
xrange
可以节省一些内存(根据范围大小,可能会节省很多内存)。我发现与操作员
&
的交集更直观。@dr.bunsen这个答案不值得所有这些投票,也不应该被接受。1) 这个“解决方案”比Andrew的解决方案慢2.5(参见我答案中的比较)2)结果没有排序,因为它是一个集合;这可能是某些国家的一个缺点cases@eyquem也许OP对这种方法的简单性(你想要交集?然后你需要交集)
intersection
、可读性(这里不需要注释)和通用性感到更舒服(对于两个范围,它不假定步骤1或相同的步骤,您可以将其用于其他iterables,而不仅仅是范围)。速度并不总是更重要的因素(尽管
itersection
方法比plaes
&
快,比列表理解快得多(python 2.6),这两种方法仍然非常有趣,imho。无论如何,我们这里讨论的是几微秒来执行代码)。感谢您的评论和讨论。我最终选择这个答案是因为可读性和使用
set()删除重复项的附加好处
。您不希望这样做来查找
范围(02198762198598125721534)
范围(21984762198598125721123345823914893546328945328456)之间的交点。
。存在一个O(1)Andrew Clark回答的解决方案,这将是一条路。就我所能看到的xrange而言。_包含来自Python2的_uu。x没有这种优化。这是slo
x = range(1,10000)
y = range(50000, 500000)

In [155]: %timeit set(x).intersection(y)
43.1 ms ± 158 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [156]: %timeit range_overlapping(x, y)
1.75 µs ± 88.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
# return overlap range for two range objects or None if no ovelap
# does not handle step!=1
def range_intersect(r1, r2):
    return range(max(r1.start,r2.start), min(r1.stop,r2.stop)) or None
def range_intersection(*ranges):
    ranges = set(ranges)  # `range` is hashable so we can easily eliminate duplicates
    if not ranges: return
    
    shortest_range = min(ranges, key=len)  # we will iterate over one, so choose the shortest one
    ranges.remove(shortest_range)          # note: `range` has a length, so we can use `len`
    
    for i in shortest_range:
        if all(i in range_ for range_ in ranges): yield i  # Finally, `range` implements `__contains__`
                                                           # by checking if an iteger satisfies it's simple formula
x = range(1,10)
y = range(8,20)
list(range_intersection(x, y))

[8, 9]
limit = 10_000
list(range_intersection(
    range(2, limit, 2),
    range(3, limit, 3),
    range(5, limit, 5),
    range(41, limit, 41),
))

[1230, 2460, 3690, 4920, 6150, 7380, 8610, 9840]