“Python的效率”;在;排序列表的关键字

“Python的效率”;在;排序列表的关键字,python,sortedlist,Python,Sortedlist,如果我有一个已排序的列表,并在关键字中使用,例如: a = [1,2,5,6,8,9,10] print 8 in a 我认为这应该做一个顺序搜索,但是我可以通过做二进制搜索来加快搜索速度吗? 是否有一种pythonic方法可以在排序列表中搜索?标准库具有支持按排序序列搜索的模块 然而,对于小列表,我敢打赌in操作符后面的C实现将击败bisect。您必须使用一系列常见情况进行测量,以确定目标硬件上的实际盈亏平衡点 值得注意的是,如果你可以不使用一个无序的iterable(即集合),那么你平均

如果我有一个已排序的列表,并在关键字中使用,例如:

a = [1,2,5,6,8,9,10]
print 8 in a
我认为这应该做一个顺序搜索,但是我可以通过做二进制搜索来加快搜索速度吗?
是否有一种pythonic方法可以在排序列表中搜索?

标准库具有支持按排序序列搜索的模块

然而,对于小列表,我敢打赌
in
操作符后面的C实现将击败
bisect
。您必须使用一系列常见情况进行测量,以确定目标硬件上的实际盈亏平衡点



值得注意的是,如果你可以不使用一个无序的iterable(即
集合
),那么你平均可以在
O(1)
时间内进行查找(使用
in
操作符),而不是在
O(logN)
的序列上进行二等分,在
O(N)
的序列上使用
in操作符。而且,使用集合还可以避免排序的成本:-)。

标准库具有支持按排序顺序搜索的模块

然而,对于小列表,我敢打赌
in
操作符后面的C实现将击败
bisect
。您必须使用一系列常见情况进行测量,以确定目标硬件上的实际盈亏平衡点



值得注意的是,如果你可以不使用一个无序的iterable(即
集合
),那么你平均可以在
O(1)
时间内进行查找(使用
in
操作符),而不是在
O(logN)
的序列上进行二等分,在
O(N)
的序列上使用
in操作符。而且,使用一个集合,您还可以避免首先排序的成本:-)。

在模块中的标准库中对Python进行二进制搜索。它不支持
/
中的
按原样包含
,但您可以编写一个小函数来处理它:

from bisect import bisect_left
def contains(a, x):
    """returns true if sorted sequence `a` contains `x`"""
    i = bisect_left(a, x)
    return i != len(a) and a[i] == x
然后

不过这不会很快,因为
bisect
是用Python编写的,而不是用C编写的,所以在相当多的情况下,您可能会发现
中的sequential
更快<代码>对分
自Python 2.4以来,在CPython中有一个可选的C加速

很难在CPython计算出确切的盈亏平衡点。这是因为代码是用C编写的;如果您检查的值大于或小于序列中的任何值,则CPU的分支预测将欺骗您,您将得到:

In [2]: a = list(range(100))
In [3]: %timeit contains(a, 101)
The slowest run took 8.09 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 370 ns per loop
这里,3的最佳值并不代表算法的运行时间

但是通过调整测试,我得出结论,对于只有30个元素的列表,对分可能比
中的
更快


但是,如果您在
操作中执行了很多
,则应该使用
集合
;您可以将列表一次性转换为一个集合(甚至不进行排序),并且
中的
操作将比任何二进制搜索都要快:

>>> a = [10, 6, 8, 1, 2, 5, 9]
>>> a_set = set(a)
>>> 10 in a_set
True

另一方面,对列表进行排序比构建一个集合的时间复杂度要大,因此大多数情况下,一个集合是可行的。

在模块中的标准库中对Python进行二进制搜索。它不支持
/
中的
按原样包含,但您可以编写一个小函数来处理它:

from bisect import bisect_left
def contains(a, x):
    """returns true if sorted sequence `a` contains `x`"""
    i = bisect_left(a, x)
    return i != len(a) and a[i] == x
然后

不过这不会很快,因为
bisect
是用Python编写的,而不是用C编写的,所以在相当多的情况下,您可能会发现
中的sequential
更快<代码>对分
自Python 2.4以来,在CPython中有一个可选的C加速

很难在CPython计算出确切的盈亏平衡点。这是因为代码是用C编写的;如果您检查的值大于或小于序列中的任何值,则CPU的分支预测将欺骗您,您将得到:

In [2]: a = list(range(100))
In [3]: %timeit contains(a, 101)
The slowest run took 8.09 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 370 ns per loop
这里,3的最佳值并不代表算法的运行时间

但是通过调整测试,我得出结论,对于只有30个元素的列表,对分可能比
中的
更快


但是,如果您在
操作中执行了很多
,则应该使用
集合
;您可以将列表一次性转换为一个集合(甚至不进行排序),并且
中的
操作将比任何二进制搜索都要快:

>>> a = [10, 6, 8, 1, 2, 5, 9]
>>> a_set = set(a)
>>> 10 in a_set
True

另一方面,对列表进行排序比构建一个集合的时间复杂度要大,所以大多数情况下,一个集合是可行的。

“我认为这应该进行顺序搜索”。你认为这是为什么?将其转换为一个集合,然后使用“in”@Lutz,因为解释器无法神奇地判断列表是否已排序?@Lutz
def is_in(some_arr,val):在some_arr中返回val
-你认为解释器应该如何判断
some_arr
是否已排序。显然这是不可能的,所以它不能这样做。它可以在那里进行额外的检查,以确定列表是否已排序,然后使用二进制搜索-但由于这需要遍历整个列表,因此无法达到目的。@Benjamin:只有当您想在
测试中执行多个
时,转换为集合才有帮助。如果列表被排序,二分法(O(logN))将优先转换为集合(O(N))。“我认为这应该进行顺序搜索”。你认为这是为什么?将其转换为一个集合,然后使用“in”@Lutz,因为解释器无法神奇地判断列表是否已排序?@Lutz
def is_in(some_arr,val):在some_arr中返回val
-你认为解释器应该如何判断
some_arr
是否已排序。显然这是不可能的,所以它不能这样做。嗯,它可能会