【问题标题】:Persistent Hashing of Strings in PythonPython中字符串的持久散列
【发布时间】:2013-07-01 16:49:07
【问题描述】:

如何将任意字符串转换为唯一整数,这在 Python 会话和平台之间是相同的?例如,hash('my string') 不起作用,因为每个 Python 会话和平台都会返回不同的值。

【问题讨论】:

  • 如果您能澄清您是否想要保证唯一性,或者您是否对唯一性的高概率感到满意(如哈希函数),这将很有帮助。您在谈论 hash() 的事实表明后者......?您是否需要能够反转映射?

标签: python


【解决方案1】:

使用MD5或SHA1等哈希算法,然后通过int()转换hexdigest

>>> import hashlib
>>> int(hashlib.md5('Hello, world!').hexdigest(), 16)
144653930895353261282233826065192032313L

【讨论】:

  • 这是一个很好的答案,但从技术上讲,产生的整数不是唯一的。 MD5 哈希值少于可用字符串。但是,发生碰撞的机会非常低
  • 这适用于任何哈希方法。
  • “非常低”是什么意思?当需要唯一性时,在生产中使用此算法是否不明智?
  • 如果需要唯一性,则不要使用散列;请改用顺序编号或 UUID。
  • 稍作修改:如果要约束int的大小:int(hashlib.md5('Hello, world!').hexdigest()[:8], 16)将32,int(hashlib.md5('Hello, world!').hexdigest()[:16], 16)将64。
【解决方案2】:

如果哈希函数真的不适合你,你可以把字符串变成一个数字。

my_string = 'my string'
def string_to_int(s):
    ord3 = lambda x : '%.3d' % ord(x)
    return int(''.join(map(ord3, s)))

In[10]: string_to_int(my_string)
Out[11]: 109121032115116114105110103L

这是可逆的,通过将每个三元组映射到chr

def int_to_string(n)
    s = str(n)
    return ''.join([chr(int(s[i:i+3])) for i in range(0, len(s), 3)])

In[12]: int_to_string(109121032115116114105110103L)
Out[13]: 'my string'

【讨论】:

  • 这会将 '\0' 和 '\0\0' 映射到同一个东西 - 你应该在前面加上一个 '1'。这也有点低效,可以使用十六进制表示,所以你会有更小的数字(这相当于使用字符串的二进制表示并将其解释为数字)。
【解决方案3】:

这里是我的 python27 算法实现:http://www.cse.yorku.ca/~oz/hash.html。 不知道它们是否有效。

from ctypes import c_ulong

def ulong(i): return c_ulong(i).value  # numpy would be better if available

def djb2(L):
  """
  h = 5381
  for c in L:
    h = ((h << 5) + h) + ord(c) # h * 33 + c
  return h
  """
  return reduce(lambda h,c: ord(c) + ((h << 5) + h), L, 5381)

def djb2_l(L):
  return reduce(lambda h,c: ulong(ord(c) + ((h << 5) + h)), L, 5381)

def sdbm(L):
  """
  h = 0
  for c in L:
    h = ord(c) + (h << 6) + (h << 16) - h
  return h
  """
  return reduce(lambda h,c: ord(c) + (h << 6) + (h << 16) - h, L, 0)

def sdbm_l(L):
  return reduce(lambda h,c: ulong(ord(c) + (h << 6) + (h << 16) - h), L, 0)

def loselose(L):
  """
  h = 0
  for c in L:
    h += ord(c);
    return h
  """
  return sum(ord(c) for c in L)

def loselose_l(L):
  return reduce(lambda h,c: ulong(ord(c) + h), L, 0)

【讨论】:

    【解决方案4】:

    首先,您可能真的不希望整数实际上是唯一的。如果你这样做,那么你的号码可能是无限的。如果这确实是您想要的,那么您可以使用 bignum 库并将字符串的位解释为(可能非常大)整数的表示。如果您的字符串可以包含 \0 字符,那么您应该在前面加上 1,这样您就可以区分例如"\0\0" 来自 "\0"。

    现在,如果您更喜欢有限大小的数字,您将使用某种形式的散列。 MD5 会起作用,但对于所述目的来说它是矫枉过正的。我建议改用 sdbm,它工作得很好。在 C 中它看起来像这样:

    static unsigned long sdbm(unsigned char *str)
    {
        unsigned long hash = 0;
        int c;
    
        while (c = *str++)
            hash = c + (hash << 6) + (hash << 16) - hash;
    
        return hash;
    }
    

    来源http://www.cse.yorku.ca/~oz/hash.html 还提供了一些其他哈希函数。

    【讨论】:

    • 你说的很对。如果我试图将整个文档转换为数字,这肯定是一个问题。但是,对于我的应用程序,我只会转换短字符串,通常少于几十个字符。
    【解决方案5】:

    这是另一种选择,非常粗糙(可能有很多碰撞)并且不是很清晰。

    它的作用是为不同的字符串生成一个 int(以及后来的随机颜色):

    aString = "don't panic"
    reduce( lambda x,y:x+y, map( lambda x:ord(x[0])*x[1],zip( aString, range( 1, len( aString ) ) ) ) )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-04
      • 1970-01-01
      • 2011-09-30
      • 2020-03-17
      • 1970-01-01
      • 2011-12-25
      • 2011-08-17
      相关资源
      最近更新 更多