为什么 numpy 数组不记得它们已被排序?



这可能不是numpy特有的问题,但当我试图利用numpy数组优化一段代码时,我想到了这个问题,我认为这是一个很好的例子。

我的问题是为什么numpy数组不"记住"它们是否已经排序。在检查由标准关系运算符表示的条件时,这难道不是一个明显的提高性能的机会吗?

举例说明,实例化一个显式无序数组。

import numpy as np
x = np.arange(30000)
# unsorted array
y = np.random.choice(x, x.size, replace=False)

然后测试一个简单的>条件…

%timeit y > 20
# 15.1 µs ± 870 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit y > 25000
# 14.8 µs ± 349 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

这对于任何值都花费相同的时间,正如您所期望的那样(它必须针对数组中的每个值检查条件)。

然而,如果我们显式地对数组排序,然后运行相同的测试…

y.sort()
%timeit y > 20
# 14.8 µs ± 737 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit y > 25000
# 14.8 µs ± 515 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

结果或多或少是相同的,这表明仍然针对数组中的每个值检查条件。

在我看来,如果numpy数组有一个布尔属性来指示数组是否已显式排序,那么通过运行这样的操作将有机会获得性能提升:

def sorted_greater_than(arr, z):
l = len(arr)
for i,v in enumerate(arr):
if v > z:
return np.array([False]*i + [True]*(l-i))
return np.full(l,False)

。我们知道I之后的每个值都大于I处的值,所以如果I处的值大于z,那么I之后的所有其他值也都大于z(对于其他操作符也是如此)。

我当然不是说numpy优化得很差,我只是想知道我在这里错过了什么?物体"记忆"这个概念在逻辑上是否不一致?如果已经排序了?

当您需要知道列表/数组是否排序时,它可能会提高性能,但它会减慢您在列表上执行的每个操作,例如追加或插入元素,替换子集。对于最常见的用例(与排序状态无关),这种权衡将是非常糟糕的。

在最好的情况下,列表可以保留自上次排序以来未被更改的事实,但即使这样也会增加几乎永远不需要的额外内存。

如果你需要一个保持排序状态的列表,你可以创建一个你自己的类,并在其中管理这个状态。(了解所涉及的开销可能是一个很好的练习)。