显而易见的事情是排序然后过滤,或者过滤然后排序。
如果您每次都有相同的列表,那么首先排序显然是一种胜利,因为这样您只需要排序一次而不是每次都排序。这也意味着您可以使用二进制搜索而不是线性遍历进行过滤(如ventsyv 的回答中所解释的那样- 尽管除非您的列表比这个列表长得多,否则这可能不会得到回报。
如果您每次都有不同的列表,那么首先过滤可能是一种胜利,因为排序可能是缓慢的部分,而您正在以这种方式对较小的列表进行排序。
但是,让我们停止猜测并开始测试。
使用包含数千个浮点数的列表,其中大约一半在范围内:
In [1591]: flist = [random.random()*10 for _ in range(5000)]
In [1592]: %timeit sorted(x for x in flist if 3 <= x < 8)
100 loops, best of 3: 3.12 ms per loop
In [1593]: %timeit [x for x in sorted(flist) if 3 <= x < 8]
100 loops, best of 3: 4 ms per loop
In [1594]: %timeit l=sorted(flist); l[bisect.bisect_left(l, 3):bisect.bisect_right(l, 8)]
100 loops, best of 3: 3.36 ms per loop
因此,过滤然后排序获胜;ventsyn 的算法确实弥补了部分差异,但不是全部。但是当然,如果我们只有一个列表要排序,那么排序一次而不是数千次是明显的胜利:
In [1596]: l = sorted(flist)
In [1597]: %timeit l[bisect.bisect_left(l, 3):bisect.bisect_right(l, 8)]
10000 loops, best of 3: 29.2 µs per loop
因此,如果您一遍又一遍地拥有相同的列表,显然将其排序一次。
否则,您可以对您的真实数据进行测试……但我们正在谈论将需要几毫秒的时间减少高达 22% 的费用。即使您执行了数千次,也可以为您节省不到一秒钟的时间。仅仅键入不同实现的成本——更不用说理解它们、概括它们、调试它们以及对它们进行性能测试——远不止这些。
但实际上,如果您要在数十万个值上进行数百万次操作,并且速度很重要,那么您首先不应该使用列表,而应该使用NumPy数组。NumPy 可以只存储原始float
值,而不会将它们装箱为 Python 对象。除了节省内存(和改善缓存局部性)之外,这意味着 in 的内部循环np.sort
比 in 的内部循环更快sorted
,因为它不必进行最终涉及拆箱两个数字的 Python 函数调用,它只需直接做对比。
假设您首先将值存储在数组中,它是如何叠加的?
In [1607]: flist = np.random.random(5000) * 10
In [1608]: %timeit a = np.sort(flist); a = a[3 <= a]; a = a[a < 8]
1000 loops, best of 3: 742 µs per loop
In [1611]: %timeit c = b[3 <= b]; d = c[c < 8]
10000 loops, best of 3: 29.8 µs per loop
因此,对于“不同列表”的情况,它比过滤和排序快大约 4 倍,即使使用笨拙的算法(我一直在寻找可以塞进%timeit
一行的东西,而不是最快或最易读的……)。对于“一遍又一遍的相同列表”的情况,即使没有平分,它也几乎与平分解决方案一样快(当然你也可以使用 NumPy 平分)。