代码之家  ›  专栏  ›  技术社区  ›  Mr. W

为什么numpy.array在编辑内部数据时如此缓慢?

  •  2
  • Mr. W  · 技术社区  · 5 月前

    我写了一个算法,它使用长 list .由于 numpy.array 在处理长数据时应该表现更好,我用 numpy.array .

    列表版本:

    import math
    def PrimeTable(n:int) -> list[int]:
       nb2m1 = n//2 - 1
       l = [True]*nb2m1
       for i in range(1,(math.isqrt(n)+1)//2):
          if l[i-1]:
             for j in range(i*3,nb2m1,i*2+1):
                l[j] = False
       return [2] + [i for i,v in zip(range(3,n,2), l) if v]
    

    阵列版本:

    import math, numpy
    def PrimeTable2(n:int) -> list[int]:
       nb2m1 = n//2 - 1
       l = numpy.full(nb2m1, True)
       for i in range(1,(math.isqrt(n)+1)//2):
          if l[i-1]:
             for j in range(i*3,nb2m1,i*2+1):
                l[j] = False
       return [2] + [i for i,v in zip(range(3,n,2), l) if v]
    

    事实证明 numpy.array 版本比 列表 版本。图表: graph 其中y0是PrimeTable,y1是PrimeTable2

    为什么是 numpy.array 太慢了,我该怎么改进呢?

    1 回复  |  直到 5 月前
        1
  •  4
  •   no comment Pratyush Goutam    5 月前

    在代码中,您正在访问和修改NumPy中的单个元素 array (l[j] = False) .NumPy数组针对整个数组或切片的操作进行了优化,而不是针对单个元素。

    NumPy数组由固定大小的连续内存支持。对于涉及许多随机访问模式的小规模操作,访问这些内存位置的额外开销可能会使它们比Python列表慢,Python列表是指向对象的指针。

    为了利用NumPy的优势,您应该致力于消除循环并使用矢量化操作。这是一个优化版本:

    import numpy as np
    
    def PrimeTableOptimized(n: int) -> list[int]:
        if n < 2:
            return []
        if n == 2:
            return [2]
        
        sieve = np.ones(n//2, dtype=bool)  # Only consider odd numbers
        sieve[0] = False  # 1 is not prime
        
        for i in range(1, int(n**0.5) // 2 + 1):
            if sieve[i]:
                start = 2*i*(i + 1)
                sieve[start::2*i+1] = False
        
        primes = [2] + (2*np.nonzero(sieve)[0] + 1).tolist()
        return primes
    

    sieve[start::2*i+1] = False 利用NumPy的切片功能,在单个操作中直接标记所有非素数索引。 通过仅存储奇数 sieve ,我们减小了数组的大小,这提高了内存局部性并加快了操作速度。

    使用 np.nonzero(sieve) 检索筛选器所在的索引 True ,以及矢量化算法 2*indices+1 生成素数。

    由于Python循环的开销减少,这个优化版本应该更接近或更快地实现基于列表的实现;使用NumPy的高效切片和矢量化操作。