如何快速找到列表中与条件匹配的第一个元素?

时间:2013-09-24 18:18:12

标签: python optimization

我在一个大型程序中有两个子程序,它们搜索列表中第一个与条件匹配的项并返回其索引。出于某种原因,这些占用了程序执行时间的很大一部分,我想知道如何优化它们。

def next_unignored(start_index, ignore, length):
    """Takes a start index, a set of indices to ignore, 
       and the length of the list and finds the first index
       not in the set ignore."""
    for i in xrange(start_index+1, length):
        if i not in ignore:
            return i

def last_unignored(start_index, ignore, lower_limit):
    """The same as the other, just backwards."""
    for i in xrange(start_index-1, lower_limit-1, -1):
        if i not in ignore:
            return i

有没有人有关于优化这些例程或其他类似模式的提示?

示例输入:

from sample import next_unignored, last_unignored

start_index = 0
ignore = set([0,1,2,3,5,6,8])
length = 100
print next_unignored(start_index, ignore, length) # => 4

start_index = 8
ignore = set([0,1,2,3,5,6,8])
lower_limit = 0
print last_unignored(start_index, ignore, lower_limit) # => 7

3 个答案:

答案 0 :(得分:1)

>>> import numpy as np
>>> start,length = 0,100
>>> ignore = set([0,1,2,3,5,6,8])
>>> x = np.arange(start,start+length)
>>> x[~np.in1d(x,ignore)][0]
4

没有numpy

>>> start,length = 0,100
>>> ignore = set([0,1,2,3,5,6,8])
>>> my_list = set(range(start,start+length))
>>> my_list2 = my_list - ignore
>>>
>>> print max(my_list2) # highest number not in ignore list
99
>>> print min(my_list2)  #lowest value not in ignore list
4

如果你的忽略集是稀疏的,你的方法可能会更快,因为它会短路,但如果忽略集是密集的,这应该会加快它的速度

答案 1 :(得分:0)

如果您要查看的列表已排序,您可以重写该功能以分解问题,例如二进制搜索。例如,如果您在某个长度的列表中查找该值的下一个实例,您可以这样处理它:

if(value > (length/2)):
    start_index == (length/2)
elif(value <= (length/2)):
    start_index == length
    length == (length/2)
next_unignored(start_index, ignore, length)

您可能会或可能不想根据您正在做的事情来防止将奇数值分成两部分。 (我认为Python使用floor函数,所以3/2 = 1,而不是1.5)

为了进一步提高速度,您可以检查值是否小于或大于忽略列表的中间值以及您正在搜索的列表。

如果您搜索的列表未排序,则您必须访问列表中的每个项目(忽略除外)以验证它不是您要查找的内容。 (根据您计划搜索列表的频率,偶尔对其进行排序或以排序方式添加项目可能是值得的。)除非Python中的函数比其他函数更快,否则您将不会能够非常快地加速你的代码。

答案 2 :(得分:0)

为什么所有套装等?从开始向上或向下计数,直到找到不在ignore中的第一个元素:

def next_unignored(start_index, ignore, length):
    while start_index < length:
        if start_index not in ignore:
            return start_index
        start_index += 1

def last_unignored(start_index, ignore, lower_limit):
    """The same as the other, just backwards."""
    while start_index >= lower_limit:
        if start_index not in ignore:
            return start_index
        start_index -= 1

如果所有索引都在None

,这些将返回ignore