【问题标题】:Python: Faster Universal Hashing function with built in libsPython:具有内置库的更快的通用散列函数
【发布时间】:2020-10-08 05:12:37
【问题描述】:

我正在尝试仅使用基本库来实现通用散列函数:

我遇到了问题,因为我无法在有效时间内运行它。我知道 % 很慢,所以我尝试了以下方法:

((a * x + b) % P) % n

divmod(divmod(a * x + b, P)[1], n)[1]

subeq = pow(a * x + b, 1, P)
hash = pow(subeq, 1, self.n)

所有这些功能对于我想要做的事情来说都太慢了。有没有更快的方法只使用我不知道的基础库来进行 mod 划分?

编辑 详细地说,我将运行这个函数大约 200000 次(或更多),我需要在 4 秒内完成所有 200000 次运行。这些方法都没有在那个球场上(需要几分钟)

【问题讨论】:

  • 您测量了哪些性能,您需要什么?
  • 请查看我的编辑
  • 用numpy怎么样?您可以创建一个 x 的向量(如果需要,可能还可以是 a 或 b)并一次运行几个 10000(或更多)计算作为向量算术。
  • 必须是基础python。不允许额外的库
  • 你的数字有多大?您需要任意精度,还是它们都适合 32 位或 64 位机器整数? FWIW,Python 函数调用相对较慢 - 它们比简单的 C 函数调用具有更多开销。许多标准函数是用 C 编写的,因此它们比调用 Python 实现的代码要快,但即便如此,调用函数的代码通常比只使用运算符的等效代码慢。 IOW、powdivmod 并不比使用 **% 快。

标签: python hash


【解决方案1】:

在纯 Python 代码中你不会比((a * x + b) % P) % m 做得更好; Python 解释器的开销比其他任何事情都更会成为你的瓶颈;是的,如果您确保 m 是 2 的幂,您可以预先计算 mm1 = m - 1 并将计算更改为 ((a * x + b) % P) & mm1,用更便宜的位掩码操作替换更昂贵的余数运算,但除非 P 很大(最少数百位),解释器开销可能会超过余数和位掩码之间的差异。

如果您真的需要性能,并且您正在使用的类型适合 C 级原始类型,您可能会受益于编写一个 Python C 扩展,将所有值转换为 @987654326 @,Py_hash_tuint64_t,或任何适合您的问题,并将数学作为一组批量转换为 C 类型、C 级别数学,然后单次转换回 Python int,节省一堆字节码和中间值(立即被丢弃)。

如果值太大而无法容纳在 C 原语中,则可以选择 GMP 类型(查看 mpz_importmpz_export 以了解从 PyLongmpz_t 并返回的有效转换),但看到的几率大笔储蓄减少;一般来说,GMP 的数学运算速度更快,并且可以在适当的位置改变数字,而不是创建和销毁大量临时变量,但即使使用 mpz_importmpz_export,在 Python 和 GMP 类型之间转换的成本可能会消耗掉大部分节省。

【讨论】:

    【解决方案2】:
    from math import ceil, log2
    from primesieve import nth_prime #will get nth prime number [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
    from random import randint
    
    class UniversalHashing:
      """ N = #bins
          p = prime number st: p >= N 
        nth_prime(1, 1 << max(32, ceil(log2(N))))
        nth_prime(1,1<<max(32),ceil(log2(2)))))
        nth_prime(1,2**32)
        nth_prime(1,4294967296)
        =4294967311   
        assert:- Returns Error if condition not satisfied 
        << operatior:-  multiply with 2the power like 2<<2 =2*2'2=8 or 7*2'3=56 and ceil will give the exact value or next vlue ceil(1)=1 , ceil(1.1)=2
        randint:- Return a random integer N such that a <= N <= b. Alias for randrange(a, b+1). """
      def __init__(self, N, p = None):
        self.N = N
        if p is None:
          p = nth_prime(1, 1 << max(32, ceil(log2(N))))  
        assert p >= N, 'Prime number p should be at least N!'
        self.p = p
    
      def draw(self):
        a = randint(1, self.p - 1)
        b = randint(0, self.p - 1)
        return lambda x: ((a * x + b) % self.p) % self.N
    
    if __name__ == '__main__':
      N = 50       #bins
      n = 100000   #elements
      H = UniversalHashing(N)
      h = H.draw()
    
      T = [0] * N
      for _ in range(n):
        x = randint(0, n * 10)
        T[h(x)] += 1
    
      for i in range(len(T)):
        print(T[i] / n)    # This should be approximately equal
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-23
      • 1970-01-01
      • 2014-09-14
      • 2014-11-20
      • 1970-01-01
      • 2018-04-06
      • 1970-01-01
      • 2011-01-08
      相关资源
      最近更新 更多