Python 3.10+
二分查找对于有序数据快速,并且Python的bisect
模块已经完成了这项工作。它希望是递增的数据,而你的数据是递减的,但我们可以虚拟使其递增。只需使用其闪亮新功能的key
参数来取反O(log n)访问的元素(并搜索所取反的阈值)即可。
from bisect import bisect_left
from operator import neg
i = bisect_left(orgin_lst, -threshold, key=neg)
lst = orgin_lst[:i]
另外,您可以使用一个关键函数,如果值大于阈值,则返回False
,否则返回True
。由于False
小于True
(它们像0
和1
一样),因此我们再次获得了一个单调递增的序列,并且可以使用bisect
进行搜索:
from bisect import bisect
i = bisect(orgin_lst, False, key=lambda x: x <= threshold)
lst = orgin_lst[:i]
如果您不需要一个单独的新列表,可以使用del orgin_lst[i:]
来删除不需要的元素。
Python 3.10之前
以前我会编写一个代理类来执行现在通过更方便的键参数完成的工作:
from bisect import bisect_left
class Negate:
def __getitem__(_, i):
return -orgin_lst[i]
i = bisect_left(Negate(), -threshold, 0, len(orgin_lst))
lst = orgin_lst[:i]
或者我本来可以自己编写二分搜索算法,但是我已经这样做了很多次,到了某个时候就开始讨厌它了。
指数搜索
在您的Method1下,使用列表推导式比较每个元素,您写道:“如果有很少的满足元素,但原始列表非常大,则性能不佳”。 如果这不仅仅是反对该列表推导式的论据,而且实际上您确实有大量非常少的满足元素和非常长的列表,那么指数搜索可能比二分搜索更好。 但它需要更多代码(除非你找到一个包)。
像您的Method2(顺便说一句,我认为它很pythonic)或Chris的答案或使用itertools.takewhile
的简单迭代搜索在这种极端情况下也会很快,但是对于有大量满足元素的情况,它们比二分搜索和指数搜索慢得多。
itertools.takewhile
正如我所说,它通常会更慢,但对于那些最佳情况来说,它非常快,并且相当简单和干净:
from itertools import takewhile
lst = list(takewhile(lambda x: x > threshold, orgin_lst))
更快的循环
就像我说的,我认为你的循环非常Pythonic,并且在最佳情况下表现良好。但是,调用append
将元素单独附加到结果中相当昂贵。要更快,最好先找到第一个太小的元素,然后找到它的索引并进行切片操作:
for i in orgin_lst:
if i <= threshold:
lst = orgin_lst[:orgin_lst.index(i)]
break
else:
lst = orgin_lst[:]
如果您只想从现有列表中删除不需要的元素,可以在 if
内使用 del
,这样就不需要在这里使用 else
部分。
我为另一个问题撰写的类似解决方案在那里的基准测试中排名第二快。
替代实现:
cut = None
for i in orgin_lst:
if i <= threshold:
cut = orgin_lst.index(i)
break
lst = orgin_lst[:cut]
O(1)
时间内得到结果为空的列表。然而,这种方法仍然是O(n)
。 - maplemaple