聪明的 any() 之类的函数来检查是否至少有 n 个元素为真?
clever any() like function to check if at least n elements are True?
假设我有一个可迭代对象(在我的例子中是一个列表):
l = [True, False, False, True]
我知道检查这些元素中至少有一个是否为 True 的最简单和最快的方法就是使用 any(l)
,这将 return True
.
但是如果我想检查至少有两个元素是 True
怎么办?
我的目标是以最快的方式处理它。
我的代码现在看起来像这样(对于两个元素):
def check_filter(l):
if len([i for i in filter(None, l)]) > 1:
return True
return False
这比 any()
慢了大约 10 倍,而且对我来说似乎不是很 pythonic。
您可以简单地在序列上使用迭代器并检查迭代器 returns 上的 any
在 n
次中始终为真:
def check(it, num):
it = iter(it)
return all(any(it) for _ in range(num))
>>> check([1, 1, 0], 2)
True
>>> check([1, 1, 0], 3)
False
这里的关键点是迭代器会记住它最后的位置,所以每个 any
调用都将从最后一个结束的地方开始。并将其包装在 all
中确保它在 any
为 False
时尽早退出。
至少在性能方面,这应该比大多数其他方法更快。但是以可读性为代价。
如果您想比基于 map
和 itertools.repeat
的解决方案更快:
from itertools import repeat
def check_map(it, num):
return all(map(any, repeat(iter(it), num)))
以其他答案为基准:
# Second "True" element is in the last place
lst = [1] + [0]*1000 + [1]
%timeit check_map(lst, 2) # 10000 loops, best of 3: 20.3 µs per loop
%timeit check(lst, 2) # 10000 loops, best of 3: 23.5 µs per loop
%timeit many(lst, 2) # 10000 loops, best of 3: 153 µs per loop
%timeit sum(l) >= 2 # 100000 loops, best of 3: 19.6 µs per loop
# Second "True" element is the second item in the iterable
lst = [1, 1] + [0]*1000
%timeit check_map(lst, 2) # 100000 loops, best of 3: 3.05 µs per loop
%timeit check(lst, 2) # 100000 loops, best of 3: 6.39 µs per loop
%timeit many(lst, 2) # 100000 loops, best of 3: 5.02 µs per loop
%timeit sum(lst) >= 2 # 10000 loops, best of 3: 19.5 µs per loop
使用sum
:
sum(l) >= 2
# True
大概 any
遍历迭代器,直到找到一个元素 True
,然后停止。
您的解决方案扫描所有元素以查看是否至少有 2 个。相反,它应该在找到第二个 True
元素后立即停止扫描。
L = [True, False, False, True]
这只会执行所需的迭代:
def many(iterable, n):
if n < 1:
return True
counter = 0
for x in iterable:
if x:
counter += 1
if counter == n:
return True
return False
现在:
>>> many(L, 2)
True
假设我有一个可迭代对象(在我的例子中是一个列表):
l = [True, False, False, True]
我知道检查这些元素中至少有一个是否为 True 的最简单和最快的方法就是使用 any(l)
,这将 return True
.
但是如果我想检查至少有两个元素是 True
怎么办?
我的目标是以最快的方式处理它。
我的代码现在看起来像这样(对于两个元素):
def check_filter(l):
if len([i for i in filter(None, l)]) > 1:
return True
return False
这比 any()
慢了大约 10 倍,而且对我来说似乎不是很 pythonic。
您可以简单地在序列上使用迭代器并检查迭代器 returns 上的 any
在 n
次中始终为真:
def check(it, num):
it = iter(it)
return all(any(it) for _ in range(num))
>>> check([1, 1, 0], 2)
True
>>> check([1, 1, 0], 3)
False
这里的关键点是迭代器会记住它最后的位置,所以每个 any
调用都将从最后一个结束的地方开始。并将其包装在 all
中确保它在 any
为 False
时尽早退出。
至少在性能方面,这应该比大多数其他方法更快。但是以可读性为代价。
如果您想比基于 map
和 itertools.repeat
的解决方案更快:
from itertools import repeat
def check_map(it, num):
return all(map(any, repeat(iter(it), num)))
以其他答案为基准:
# Second "True" element is in the last place
lst = [1] + [0]*1000 + [1]
%timeit check_map(lst, 2) # 10000 loops, best of 3: 20.3 µs per loop
%timeit check(lst, 2) # 10000 loops, best of 3: 23.5 µs per loop
%timeit many(lst, 2) # 10000 loops, best of 3: 153 µs per loop
%timeit sum(l) >= 2 # 100000 loops, best of 3: 19.6 µs per loop
# Second "True" element is the second item in the iterable
lst = [1, 1] + [0]*1000
%timeit check_map(lst, 2) # 100000 loops, best of 3: 3.05 µs per loop
%timeit check(lst, 2) # 100000 loops, best of 3: 6.39 µs per loop
%timeit many(lst, 2) # 100000 loops, best of 3: 5.02 µs per loop
%timeit sum(lst) >= 2 # 10000 loops, best of 3: 19.5 µs per loop
使用sum
:
sum(l) >= 2
# True
大概 any
遍历迭代器,直到找到一个元素 True
,然后停止。
您的解决方案扫描所有元素以查看是否至少有 2 个。相反,它应该在找到第二个 True
元素后立即停止扫描。
L = [True, False, False, True]
这只会执行所需的迭代:
def many(iterable, n):
if n < 1:
return True
counter = 0
for x in iterable:
if x:
counter += 1
if counter == n:
return True
return False
现在:
>>> many(L, 2)
True