Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 查找点的相应间隔_Python_Loops_Search_Intervals - Fatal编程技术网

Python 查找点的相应间隔

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

我有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 = (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我没意见,但请注意,如果你遇到了一种情况