在python中检查列表中是否正好有n个项与条件匹配的最快方法

在python中检查列表中是否正好有n个项与条件匹配的最快方法,python,Python,如果列表中有m个项目,那么检查列表中的n个项目是否满足特定条件的最快方法是什么?例如: l = [1,2,3,4,5] In [16]: import numpy as np In [17]: a = np.arange(5) In [18]: a Out[18]: array([0, 1, 2, 3, 4]) In [19]: np.sum(a % 2 == 0) Out[19]: 3 def two_evens(iterable): 'Return true if exac

如果列表中有m个项目,那么检查列表中的n个项目是否满足特定条件的最快方法是什么?例如:

l = [1,2,3,4,5]
In [16]: import numpy as np 
In [17]: a = np.arange(5)

In [18]: a
Out[18]: array([0, 1, 2, 3, 4])

In [19]: np.sum(a % 2 == 0)
Out[19]: 3
def two_evens(iterable):
    'Return true if exactly two values are even'
    matched = 0
    for x in s:
            if x % 2 == 0:
                matched += 1
                if matched > 2:
                    return False
    return matched == 2
如何检查列表中的任何两项是否符合条件
x%2==0

最简单的方法是使用嵌套for循环:

for i in l:
    for j in l:
        if not i%2 and not j%2:
            return True

但这是一种效率极低的检查方法,如果我想检查200-1000万个项目列表中的任何50000个项目,这将变得特别难看。

[编辑以反映精确匹配,我们仍然可以通过短路来完成!]

我想你会希望它短路(确定时停止,而不仅仅是在最后):

如果您想在同一输入数据上多次更快地执行查询,则应改用NumPy(无短路):


这不会造成短路,但一旦构建了输入数组,它将非常快,因此如果您有一个大的输入列表和大量的查询要处理,并且查询不能很早短路,那么这可能会更快。可能是一个数量级。

[编辑以反映精确匹配,我们仍然可以通过短路来实现!]

我想你会希望它短路(确定时停止,而不仅仅是在最后):

如果您想在同一输入数据上多次更快地执行查询,则应改用NumPy(无短路):

这不会造成短路,但一旦构建了输入数组,它将非常快,因此如果您有一个大的输入列表和大量的查询要处理,并且查询不能很早短路,那么这可能会更快。可能是一个数量级。

这项工作:

>>> l = [1,2,3,4,5]
>>> n = 2
>>> a = 0  # Number of items that meet the condition
>>> for x in l:
...     if x % 2 == 0:
...         a += 1
...         if a > n:
...             break
...
>>> a == n
True
>>>
它的优点是只在列表中运行一次。

这样做:

>>> l = [1,2,3,4,5]
>>> n = 2
>>> a = 0  # Number of items that meet the condition
>>> for x in l:
...     if x % 2 == 0:
...         a += 1
...         if a > n:
...             break
...
>>> a == n
True
>>>

它的优点是只在列表中运行一次。

我将使用while循环:

l=[1,2,3,4,5]

mods, tgt=0,2
while mods<tgt and l:
    if l.pop(0)%2==0:
        mods+=1

print(l,mods)  
l=[1,2,3,4,5]
mods,tgt=0,2

而mods我会使用while循环:

l=[1,2,3,4,5]

mods, tgt=0,2
while mods<tgt and l:
    if l.pop(0)%2==0:
        mods+=1

print(l,mods)  
l=[1,2,3,4,5]
mods,tgt=0,2

当mods时,您可以使用您的条件内置的
sum
,并检查它是否等于您的
n

l = [1, 2, 3, 4, 5]
n = 2
if n == sum(1 for i in l if i % 2 == 0):
    print(True)

您可以使用您的条件内置的
sum
,并检查它是否等于您的
n

l = [1, 2, 3, 4, 5]
n = 2
if n == sum(1 for i in l if i % 2 == 0):
    print(True)

为什么不直接使用filter()

例:检查列表中偶数整数的数量:

>>> a_list = [1, 2, 3, 4, 5]
>>> matches = list(filter(lambda x: x%2 == 0, a_list))
>>> matches
[2, 4]
然后,如果需要匹配的数量:

>>> len(matches)
2
>>> s = [False, True, False, True, False, False, False]
>>> sum(islice(ifilter(None, s), 0, 3)) == 2
True
最后,你的回答是:

>>> if len(matches) == 2:
        do_something()

为什么不直接使用filter()

例:检查列表中偶数整数的数量:

>>> a_list = [1, 2, 3, 4, 5]
>>> matches = list(filter(lambda x: x%2 == 0, a_list))
>>> matches
[2, 4]
然后,如果需要匹配的数量:

>>> len(matches)
2
>>> s = [False, True, False, True, False, False, False]
>>> sum(islice(ifilter(None, s), 0, 3)) == 2
True
最后,你的回答是:

>>> if len(matches) == 2:
        do_something()

sum
解决方案将
True
值相加是正确的,可能比显式循环更有效,而且绝对是最简洁的:

if sum(i % 2 == 0 for i in lst) == n:
然而,它依赖于理解在整数上下文(如加法)中,
True
算作
1
False
算作
0
。你可能不想指望这一点。在这种情况下,您可以重写它(squiguy的答案):

但您可能希望将其分解为一个函数:

def count_matches(predicate, iterable):
    return sum(predicate(i) for i in iterable)
在这一点上,列表可能更具可读性,并计算结果筛选iterable的长度:

def ilen(iterable):
    return sum(1 for _ in iterable)

def count_matches(predicate, iterable):
    return ilen(filter(predicate, iterable))
然而,所有这些变体的缺点是,与使用
map
filter
一样,谓词必须是一个函数,而不仅仅是一个表达式。当您只想检查
某个函数(x)
是否返回True时,这很好,但是当您想检查
x%2==0
时,您必须执行额外的步骤,将其包装到函数中,如下所示:

if count_matches(lambda x: x %2 == 0, lst) == n
…在这一点上,我认为你失去的可读性比获得的要多


由于您要求的是最快的,尽管这可能是错误的,因为我确信这些解决方案中的任何一个对于几乎任何应用来说都足够快,而且这也不可能成为热点,因此,在我的计算机上,长度为250的64位CPython 3.3.2有一些测试:

32.9 µs: sum(not x % 2 for x in lst)
33.1 µs: i=0\nfor x in lst: if not x % 2: i += 1\n
34.1 µs: sum(1 for x in lst if not x % 2)
34.7 µs: i=0\nfor x in lst: if x % 2 == 0: i += 1\n
35.3 µs: sum(x % 2 == 0 for x in lst)
37.3 µs: sum(1 for x in lst if x % 2 == 0)
52.5 µs: ilen(filter(lambda x: not x % 2, lst))
56.7 µs: ilen(filter(lambda x: x % 2 == 0, lst))
因此,事实证明,至少在64位CPython 3.3.2中,无论您使用显式循环,将False和True相加,或者如果True,则将1s相加,差别都很小;在某些情况下,使用
not
代替
==0
会产生更大的差异;但即使是最差的也只比最好的差12%

所以我会用你觉得最可读的。而且,如果最慢的一个不够快,那么最快的一个也可能不够快,这意味着你可能需要重新安排你的应用程序使用NumPy,用pypypy而不是CPython运行你的应用程序,编写自定义Cython或C代码,或者做一些比仅仅重新组织这个微不足道的算法更激烈的事情

为了比较,这里有一些NumPy实现(假设
lst
np.ndarray
而不是
列表
):

即使是最明显的NumPy翻译速度也几乎是NumPy的两倍;只要稍加努力,你就能把速度提高3倍

下面是在PyPy(3.2.3/2.1b1)中而不是在CPython中运行完全相同代码的结果:

14.6 µs: sum(not x % 2 for x in lst)

在代码完全不变的情况下,速度是原来的两倍以上。

A
sum
解决方案相加
True
值是正确的,可能比显式循环更有效,而且绝对是最简洁的:

if sum(i % 2 == 0 for i in lst) == n:
然而,它依赖于理解在整数上下文(如加法)中,
True
算作
1
False
算作
0
。你可能不想指望这一点。在这种情况下,您可以重写它(squiguy的答案):

但您可能希望将其分解为一个函数:

def count_matches(predicate, iterable):
    return sum(predicate(i) for i in iterable)
在这一点上,列表可能更具可读性,并计算结果筛选iterable的长度:

def ilen(iterable):
    return sum(1 for _ in iterable)

def count_matches(predicate, iterable):
    return ilen(filter(predicate, iterable))
然而,所有这些变化的不利方面,就像使用
map
filter
一样,都是您的谓词
def two_evens(iterable):
    'Return true if exactly two values are even'
    matched = 0
    for x in s:
            if x % 2 == 0:
                matched += 1
                if matched > 2:
                    return False
    return matched == 2
>>> list(ifilter(None, [False, True, True, False, True]))
[True, True, True]
>>> list(islice(range(10), 0, 3))
[0, 1, 2]
>>> sum([True, True, True])
3
>>> s = [False, True, False, True, False, False, False]
>>> sum(islice(ifilter(None, s), 0, 3)) == 2
True
check_exact(lambda x: x%2==0 and x<10 and f(x)==3, dataset, matches=2)
check_exact(lambda x: x<10 and f(x)==3, dataset, matches=4)
check_exact(lambda x: x%2==0 and f(x)==3, dataset, matches=6)
evens = map(lambda x: x%2==0, dataset)
under_tens = map(lambda x: x<10, dataset)
f_threes = map(lambda x: x%2==0 and f(x)==3, dataset)
def length_is(iter, size):
    for _ in xrange(size - 1):
        next(iter, None)

    try:
        next(iter)
    except StopIteration:
        return False  # too few

    try:
        next(iter)
        return False  # too many
    except StopIteration:
        return True
length_is((i for i in data if x % 2 == 0), 2)
class count(object):
    def __init__(self, iter):
        self.iter = iter

    __eq__ = lambda self, n: length_is(self.iter, n)
count(i for i in data if x % 2 == 0) == 2
from itertools import islice

data = [1,2,3,4,5]
N = 2
items = islice((1 for el in data if el % 2 == 0), N + 1)
has_N = sum(items) == N