Python 在数组的两个不同部分中计算重复整数的最快方法

Python 在数组的两个不同部分中计算重复整数的最快方法,python,algorithm,performance,Python,Algorithm,Performance,在这段Python代码中 fun遍历数组arr并计算每个节对的两个数组节中相同整数的数量。(它模拟一个矩阵。)这使得总的比较n*(n-1)/2*m,时间复杂度为O(n^2) 是否有编程解决方案或重新定义此问题的方法可以产生相同的结果,但降低了时间复杂性 # n > 500000, 0 < i < n, m = 100 # dim(arr) = n*m, 0 < arr[x] < 4294967311 arr = mp.RawArray(ctypes.c_uint,

在这段Python代码中

fun
遍历数组
arr
并计算每个节对的两个数组节中相同整数的数量。(它模拟一个矩阵。)这使得总的比较
n*(n-1)/2*m
,时间复杂度为
O(n^2)

是否有编程解决方案或重新定义此问题的方法可以产生相同的结果,但降低了时间复杂性

# n > 500000, 0 < i < n, m = 100
# dim(arr) = n*m, 0 < arr[x] < 4294967311

arr = mp.RawArray(ctypes.c_uint, n*m)

def fun(i):
    for j in range(i-1,0,-1):
        count = 0
        for k in range(0,m):
            count += (arr[i*m+k] == arr[j*m+k])
        if count/m > 0.7:
            return (i,j)
    return ()
将提供矢量化功能,但将总运行时间增加了一个数量级-250秒,而n=1500时为30秒,这相当于733%


由于您根本无法更改阵列特性,因此我认为您必须使用O(n^2)
numpy
将获得一些矢量化,但会更改共享阵列的其他人的访问权限。从最里面的操作开始:

    for k in range(0,m):
        count += (arr[i][k] == arr[j][k])
将此更改为单行指定:

    count = sum(arr[i][k] == arr[j][k] for k in range(m))
现在,如果这确实是一个数组,而不是一个列表列表,请使用数组包的矢量化来简化循环,一次一个:

    count = sum(arr[i] == arr[j])   # results in a vector of counts

现在可以返回
j
索引,其中
count[j]/m>0.7
。请注意,没有必要为每一个返回
i
:它在函数中是常量,并且调用程序已经具有该值。您的数组包可能有一对矢量化索引操作,可以返回这些索引。如果你使用的是
numpy
,那么在这个网站上很容易找到它们。

因此,在多做了一些修改之后,在numpy的矢量化和Numba的JIT编译器的帮助下,我能够大大缩短运行时间。返回原始代码:

arr = mp.RawArray(ctypes.c_uint, n*m)

def fun(i):
    for j in range(i-1,0,-1):
        count = 0
        for k in range(0,m):
            count += (arr[i*m+k] == arr[j*m+k])
        if count/m > 0.7:
            return (i,j)
return ()
我们可以省去底部的
return
语句,也可以完全放弃使用
count
的想法,剩下的是:

def fun(i):
    for j in range(i-1,0,-1):
        if sum(arr[i*m+k] == arr[j*m+k] for k in range(m)) > 0.7*m:
            return (i,j)
然后,我们将数组
arr
更改为NumPy格式:

np_arr = np.frombuffer(arr,dtype='int32').reshape(m,n)
这里需要注意的重要一点是,我们不使用NumPy数组作为从多个进程写入的共享内存数组,从而避免了开销陷阱

最后,我们应用Numba的decorator并以向量形式重写
sum
函数,以便它与新数组一起工作:

import numba as nb
@nb.njit(fastmath=True,parallel=True)
def fun(i):
    for j in range(i-1, 0, -1):
        if np.sum(np_arr[i] == np_arr[j]) > 0.7*m:
            return (i,j)

这将运行时间减少到了7.9s,这对我来说无疑是一个胜利。

让我们看看——您希望保持原始列表的完整性,并使用极小的额外内存。您还想解决一些抽象计数问题,这些问题只在四个索引中描述,没有相关的语义。。。。。。正如张贴指南所说,请让人们更容易地帮助你。用实际的语言描述你试图解决的问题。从这里循环的相互作用来看,我希望有一种更简单的方法可以通过numpy的矢量化来实现这一点,但我不打算用我的眼睛去勾画出你的目标。@ppw0:稍微好一点。仍然不太符合标准,因为代码没有按照给定的方式运行。您一直将其称为数组,但您的代码不包含任何具有数组类型的内容。这真的是数组还是嵌套列表?您缺少完整的示例,这使它变得模棱两可。@Prune它被声明为RawArray,我现在补充说。但是由于它是1D,没有为每一个计数复制数组部分,我不能为每一对“提取”一个计数向量。下一步尝试C扩展是否明智?我想是的。我自己不在那里工作——如果这项工作需要那么多优化,我们就把口译员留在后面,转而使用完全编译的语言。
import numba as nb
@nb.njit(fastmath=True,parallel=True)
def fun(i):
    for j in range(i-1, 0, -1):
        if np.sum(np_arr[i] == np_arr[j]) > 0.7*m:
            return (i,j)