如何在python中查找范围重叠?
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重叠。我正在寻找一种方法来遍历范围列表,并对重叠的范围做一些额外的事情。是否有一个简单的对/错语句来完成
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]