Python 查找点的相应间隔
我有5个元组Python 查找点的相应间隔,python,loops,search,intervals,Python,Loops,Search,Intervals,我有5个元组A、B、C、D和E表示区间。它们的交点是空的(对于每一对),并且它们是排序的,例如一个区间的上限小于下一个区间的下限 例如: A = (5, 10) B = (21, 29) C = (134, 160) D = (900, 1050) E = (1080, 1100) intervals = [A, B, C, D, E] X = [6, 28, 130, 1000, 1129] 我还有一个按递增顺序排序的点列表X 例如: A = (5, 10) B = (21, 29) C
A、B、C、D
和E
表示区间。它们的交点是空的(对于每一对),并且它们是排序的,例如一个区间的上限小于下一个区间的下限
例如:
A = (5, 10)
B = (21, 29)
C = (134, 160)
D = (900, 1050)
E = (1080, 1100)
intervals = [A, B, C, D, E]
X = [6, 28, 130, 1000, 1129]
我还有一个按递增顺序排序的点列表X
例如:
A = (5, 10)
B = (21, 29)
C = (134, 160)
D = (900, 1050)
E = (1080, 1100)
intervals = [A, B, C, D, E]
X = [6, 28, 130, 1000, 1129]
如您所见,X
中的每一个数字都可以或不可以属于一个区间。由于区间交叉点为空,每个数字最多只能属于一个区间。此外,根据构造,每个间隔只有一个数字。 我想知道
X
中的每个数字都属于哪个区间,如果有的话。因此,对于我的示例,输出应该是:
output = [(6, A), (28, B), (None, C), (1000, D), (None, E)]
这意味着数字6、28、1000分别属于区间A、B、D
,没有数字属于区间C
和E
为了找出X
中的每个数字都属于哪个区间,我做了以下操作:
output = []
for interval in intervals:
for number in X:
if interval[0] <= number and number <= interval[1]:
found_interval = True
output.append((number, interval))
break
if not found_interval:
output.append((None, interval))
output=[]
对于间隔中的间隔:
对于X中的数字:
if interval[0][编辑时:我的原始代码没有正确处理x
是右端点之一的情况。修订后的代码修复了这一问题,并扩展了测试示例以显示它如何处理其他边缘情况]
也许这会有所帮助:创建一个按排序顺序排列的所有端点的列表,如
ends = [5,10,21,29,134,160,900,1050,1080,1100]
并使用对分
模块查找点x在该列表中的位置。这是一个二进制搜索,因此比线性搜索更有效。如果它落在两个指数(i-1,i)之间,其中i为奇数,那么x在相应的区间内。否则就没有了
此外,使用元组列表间隔
加载已排序的端点列表也很容易:
from bisect import bisect
def place(x,endpoints):
i = bisect(endpoints,x)
if i%2 == 0:
if x == endpoints[i-1]:
return endpoints[i-1],endpoints[i]
else:
return None
else:
return endpoints[i-1],endpoints[i]
A = (5, 10)
B = (21, 29)
C = (134, 160)
D = (900, 1050)
E = (1080, 1100)
intervals = [A, B, C, D, E]
ends = []
for interval in intervals:
ends.extend(interval)
xs = [3, 5, 6, 10, 28, 130, 1000, 1129]
for x in xs:
print(str(x),':',str(place(x,ends)))
输出:
3 : None
5 : (5, 10)
6 : (5, 10)
10 : (10, 21)
28 : (21, 29)
130 : None
1000 : (900, 1050)
1129 : None
您可以在线性时间内查找此问题的交点:
- 创建两个变量以跟踪当前值和间隔索引
- 只要您没有检查这两个列表中的每个元素,请检查
如果当前值属于间隔
- 如果该值属于当前区间,则将当前值和当前区间指数增加一个。
由于值和间隔都已排序,且间隔不重叠,当前间隔中不能有其他元素,因此我们可以安全地移动
- 如果该值小于当前区间的下限,则增加当前值索引,因为该值也不能属于任何下限较大的区间
- 如果该值大于当前间隔的上限,则增加当前间隔索引,因为任何其他值也会大于该上限
def find_intersection(values, intervals):
output = []
value_index = 0
interval_index = 0
while value index < len(values) and interval_index < len(intervals):
current_value = values[value_index]
current_interval = intervals[interval_index]
lower_bound, upper_bound = current_interval
if current_value < lower_bound:
output.append((None, current_value))
# This value cannot belong to any greater interval.
value_index += 1
elif current_value > upper_bound:
# No other value can belong to this interval either.
interval_index += 1
else:
output.append((current_interval, current_value))
# At most one value per interval and one interval per value.
value_index += 1
interval_index += 1
# If we ran out of intervals all remaining values do not belong to any.
for v in values[value_index:]:
output.append((None, v))
return output
def find_交叉点(值、间隔):
输出=[]
价值指数=0
区间指数=0
当值指数上限:
#也不能有其他值属于此间隔。
区间指数+=1
其他:
output.append((当前\u间隔,当前\u值))
#每个间隔最多一个值,每个值最多一个间隔。
值_索引+=1
区间指数+=1
#如果时间间隔用完,所有剩余的值都不属于任何值。
对于v值[值索引:]:
output.append((无,v))
返回输出
最坏的情况是,没有数字属于任何间隔,我们必须完全迭代每个列表(while循环中的间隔
,for循环中的值
),因此复杂性为O(n+m)
,其中,n
是值的数量,m
是区间的数量。在您的符号中,您使用的是开放区间,如(5,10)-这将排除端点,但在您的代码中,您使用@John:我认为该符号是Python语法,而不是开放/封闭区间符号。托马斯,这是正确的吗?只有五个音程,还是你需要把它扩大到一个大的音程?如果它只有5,那么使用更高级的算法几乎没有什么好处,即使它更快;这是你的“升级解决方案”。@tomasyany——这是我的想法,但我想确定一下。我不太确定对分方法的作用。我会寻找它,计算这个算法的复杂度,然后回复;谢谢你完成这个例子!不用担心理论的复杂性,只需使用%timeit@tomasyany二进制搜索是O(log(n))
与O(n)
线性搜索相比。我还添加了一些代码来获取间隔列表并创建端点的排序列表,尽管毫无疑问,一个优雅的理解可以在一行中完成。@JulienBernu是和否。这是出于学术目的,因此理解复杂性对我来说很重要。此外,由于我还不知道间隔列表的确切大小等,我更喜欢相信数学。话虽如此,我最终还是会使用%timeit并进行比较。这是一个很好的、优雅的解决方案。我已经接受了@JohnColeman的回答,但这更好(连约翰都认为这是一个很好的解决方案!)。所以我改变了这个问题的公认答案。@Tomasy我没意见,但请注意,如果你遇到了一种情况