这可行,但在算法上不是最优的,因为我不需要在函数解析数组时存储最小值:
def is_non_negative(m):
return np.min(m) >= 0
编辑:根据数据,最佳函数确实可以节省很多,因为它会在第一次遇到负值时终止。如果预期只有一个负值,则时间将平均缩短两倍。然而,在 numpy 库之外构建最佳算法将付出巨大的代价(Python 代码与 C++ 代码)。
一个可能的解决方案是使用 C: 中的函数
#include <stdio.h>
#include <stdlib.h>
int is_negative(double* data, int num_elems) {
for (int i = 0; i < num_elems; i++) {
if (data[i] < 0) {
return 1;
}
}
return 0;
}
编译:
gcc -c -fPIC is_negative.c -o is_negative.o
链接:
gcc -shared is_negative.o -o libis_negative.so
然后,在 Python 中:
import numpy as np
import ctypes
lib = ctypes.cdll.LoadLibrary('/tmp/libis_negative.so')
a = np.array([1.0, 2.0, -3.0, 4.0])
num_elems = a.size
lib.is_negative.restype = ctypes.c_int
lib.is_negative.argtypes = [
np.ctypeslib.ndpointer(dtype=np.float64),
ctypes.c_int,
]
result = lib.is_negative(a, num_elems)
if result:
print("It has negative elements")
else:
print("It does not have negative elements")
一个纯 Numpy 解决方案是使用基于块的策略:
def is_non_negative(m):
chunkSize = max(min(65536, m.size/8), 4096) # Auto-tunning
for i in range(0, m.size, chunkSize):
if np.min(m[i:i+chunkSize]) < 0:
return False
return True
这个解决方案只有在数组很大时才有效,并且块足够大以使得 Numpy 调用开销小到足以将全局数组分成许多部分(以便从早期切割中受益)。块大小需要相当大,以平衡
np.min
在小阵列上相对较大的开销。
这是一个 Numba 解决方案:
import numba as nb
# Eagerly compiled funciton for some mainstream data-types.
@nb.njit(['(float32[::1],)', '(float64[::1],)', '(int_[::1],)'])
def is_non_negative_nb(m):
for e in m:
if e < 0:
return False
return True
事实证明,这比在我的机器上使用
np.min
更快,尽管 LLVM-Lite(Numba 的 JIT)代码没有很好地自动矢量化(即不使用 SIMD 指令)。
对于更快的代码,您需要使用 C/C++ 代码并使用基于块的 SIMD 友好代码,并且如果编译器未生成高效代码,则可能使用 SIMD 内在函数,不幸的是,在这种情况下相当频繁。