【问题标题】:Which string hashing algorithm produces 32-bit or 64-bit signed integers?哪种字符串散列算法产生 32 位或 64 位有符号整数?
【发布时间】:2017-07-24 11:35:53
【问题描述】:

我想将可变长度(6-60 个字符长)的字符串散列为 32 位 signed integers,以节省 PostgreSQL 中的磁盘空间。

我不想加密任何数据,并且散列函数需要可从 Python 重现和调用。问题是我只能找到产生 无符号整数(如 CityHash)的算法,因此产生的值最多为 2^32 而不是 2^31。

这是我目前所拥有的:

import math
from cityhash import CityHash32

string_ = "ALPDAKQKWTGDR"
hashed_string = CityHash32(string_)
print(hashed_string, len(str(hashed_string)))
max_ = int(math.pow(2, 31) - 1)
print(hashed_string > max_)

【问题讨论】:

  • 只是将无符号整数解释为有符号整数?
  • @Ryan 结果必须介于 -2147483648 和 +2147483647 之间。你的建议有什么帮助?你能举个例子吗?
  • 减去 2147483648。
  • @Ryan 看起来像一个简单的解决方案。我猜它会导致与原始值相同数量的冲突,并且不会截断更长的哈希?
  • 嗯,它可以为您提供 32 位。不过,还有其他几点需要考虑。你如何处理那些让你扔掉它们并只存储它们的哈希值的字符串?

标签: python postgresql hash hashlib signed-integer


【解决方案1】:

Ryan 在 cmets 中回答了这个问题。只需从哈希结果中减去 2147483648 (= 2^31)。

CityHash32(string_) - math.pow(2, 31)

CityHash64(string_) - math.pow(2, 63)

Ryan 还提到,与上述方法相比,使用 SHA-512 并将结果截断为所需的位数将导致更少的冲突。

【讨论】:

    【解决方案2】:
    create or replace function int_hash(s text)
    returns int as $$
    
        select ('x' || left(md5(s), 8))::bit(32)::int
        ;
    $$ language sql immutable;
    
    select int_hash('1');
      int_hash  
    ------------
     -993377736
    

    【讨论】:

    • @Scoots 它被破坏的方式与问题无关,除了4字节只有碰撞率会很高。
    【解决方案3】:

    我通常不会使用 32 位散列,除非基数非常低,因为它当然比 64 位散列更容易发生冲突。数据库很容易支持 bigint 8 字节(64 位)整数。考虑this table 的一些哈希冲突概率。

    如果你使用 Python ≥3.6,你绝对不需要为此使用第三方包,你也不需要减去偏移量,因为你可以直接生成一个签名的64 位或variable bit-length hash 使用shake_128

    import hashlib
    from typing import Dict, List
    
    
    class Int8Hash:
    
        BYTES = 8
        BITS = BYTES * 8
        BITS_MINUS1 = BITS - 1
        MIN = -(2**BITS_MINUS1)
        MAX = 2**BITS_MINUS1 - 1
    
        @classmethod
        def as_dict(cls, texts: List[str]) -> Dict[int, str]:
            return {cls.as_int(text): text for text in texts}  # Intentionally reversed.
    
        @classmethod
        def as_int(cls, text: str) -> int:
            seed = text.encode()
            hash_digest = hashlib.shake_128(seed).digest(cls.BYTES)
            hash_int = int.from_bytes(hash_digest, byteorder='big', signed=True)
            assert cls.MIN <= hash_int <= cls.MAX
            return hash_int
    
        @classmethod
        def as_list(cls, texts: List[str]) -> List[int]:
            return [cls.as_int(text) for text in texts]
    

    用法:

    >>> Int8Hash.as_int('abc')
    6377388639837011804
    >>> Int8Hash.as_int('xyz')
    -1670574255735062145
    
    >>> Int8Hash.as_list(['p', 'q'])
    [-539261407052670282, -8666947431442270955]
    >>> Int8Hash.as_dict(['i', 'j'])
    {8695440610821005873: 'i', 6981288559557589494: 'j'}
    

    要改为生成 32 位哈希,请将 Int8Hash.BYTES 设置为 4。

    免责声明:我没有编写统计单元测试来验证此实现是否返回均匀分布的整数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-22
      • 2016-03-08
      • 2011-01-17
      • 1970-01-01
      • 1970-01-01
      • 2014-05-15
      • 2010-09-11
      相关资源
      最近更新 更多