字符串的常量哈希?

时间:2009-12-07 18:31:43

标签: string associative-array string-algorithm

另一个关于SO的问题提出了一些语言中的设施来对字符串进行哈希处理,以便在表格中快速查找。两个例子是字典<>在.NET中和Python中的{}存储结构。其他语言当然支持这种机制。 C ++有它的地图,LISP和大多数其他现代语言一样具有等价物。

在问题的答案中争论说,字符串上的哈希算法可以在一个持续时间内进行,其中一个SO成员具有25年的编程经验,声称任何事物都可以在恒定时间内进行哈希处理。我个人的论点是,这不是真的,除非您的特定应用程序在字符串长度上设置边界。这意味着一些常数K将决定字符串的最大长度。

我熟悉使用哈希函数进行操作的Rabin-Karp算法,但是这个算法没有规定要使用的特定哈希函数,作者建议的那个是O(m),其中m是散列字符串的长度。

我看到其他一些页面(如http://www.cse.yorku.ca/~oz/hash.html)显示了一些哈希算法,但似乎每个页面都在字符串的整个长度上迭代以得到它的值。

根据我对该主题的相对有限的阅读,似乎大多数字符串类型的关联数组实际上是使用散列函数创建的,该函数在引擎盖下使用某种树进行操作。这可能是一个AVL树或红/黑树,它指向键/值对中值元素的位置。

即使使用这种树结构,如果我们要保持theta(log(n))的顺序,n是树中元素的数量,我们需要有一个恒定时间哈希算法。否则,我们会对字符串进行迭代处理。尽管对于包含许多字符串的索引,theta(m)会被theta(log(n))黯然失色,但如果我们处于这样一个域中,我们搜索的文本将非常大,我们就不能忽略它。

我知道后缀树/数组和Aho-Corasick可以将搜索降低到theta(m)以获得更大的内存开销,但是我要问的是,如果存在任意字符串的常量哈希方法其他SO成员声称的长度。

感谢。

7 个答案:

答案 0 :(得分:7)

哈希函数不必(也不能)为每个字符串返回唯一值。

您可以使用前10个字符初始化随机数生成器,然后使用它从字符串中提取100个随机字符,并将其哈希。这将是恒定的时间。

你也可以只返回常量值1.严格来说,这仍然是一个哈希函数,虽然不是很有用。

答案 1 :(得分:5)

一般来说,我认为任何完整的字符串哈希必须使用字符串的每个字符,因此需要增长为n个字符的O(n)。但是我认为对于实际的字符串哈希,你可以使用很容易为O(1)的近似哈希值。

考虑一个始终使用Min(n,20)个字符来计算标准哈希的字符串哈希。显然,这会随着字符串大小增长为O(1)。它会可靠地运作吗?这取决于你的域名......

答案 2 :(得分:3)

您不能轻易地为字符串实现一般的常量时间哈希算法,而不会有严重的哈希冲突案例。

要使它成为常量时间,您将无法访问字符串中的每个字符。举个简单的例子,假设我们取前6个字符。然后有人试图散列一系列URL。 has函数将为每个字符串看到“http:/”。

其他角色选择方案可能会出现类似情况。您可以根据前一个字符的值伪随机地选择字符,但如果由于某种原因字符串具有“错误”模式并且许多字符串最终具有相同的散列值,您仍然会冒失败的风险。

答案 3 :(得分:1)

如果您使用ropes而不是字符串并且具有允许您跳过某些计算的共享,则希望渐近地小于线性散列时间。但显然哈希函数不能分离它没有读取的输入,所以我不会认真对待“一切都可以在恒定时间内进行哈希”。

哈希函数的质量和计算量之间的折衷是有可能的,并且长字符串上的哈希函数无论如何都必须有冲突。

必须确定如果散列函数只查看前缀,算法中可能出现的字符串是否会经常发生冲突。

答案 4 :(得分:1)

虽然我无法想象无限长度字符串的固定时间散列函数,但实际上并不需要它。

使用散列函数背后的想法是生成散列值的分布,使得不太可能发生许多字符串碰撞 - 用于所考虑的域。该密钥允许直接访问数据存储。这两个组合导致恒定时间查找 - 平均

如果发生此类冲突,查找算法将依赖于更灵活的查找子策略。

答案 5 :(得分:1)

当然,这是可行的,只要你确保所有的字符串都被“插入”,然后再将它们传递给需要散列的内容。实习是将字符串插入字符串表的过程,这样所有具有相同值的实习字符串实际上都是同一个对象。然后,您可以简单地将(固定长度)指针散列到实习字符串,而不是散列字符串本身。

答案 6 :(得分:1)

您可能对我去年提出的以下数学结果感兴趣。

考虑将无限数量的键(例如任何长度的所有字符串的集合)散列到{1,2,...,b}中的数字集的问题。随机散列通过首先在H函数族中随机选取散列函数h来进行。

我将证明总是存在无限数量的键肯定会碰撞所有H函数,也就是说,它们对于所有散列函数总是具有相同的散列值。

选择任何散列函数h:至少有一个散列值y使得集合A = {s:h(s)= y}是无限的,也就是说,你有无限多个字符串碰撞。选择任何其他散列函数h'并散列集合A中的密钥。至少有一个散列值y'使得集合A'= {s在A:h'(s)= y'}中是无限的,也就是说,在两个散列函数上存在无限多个字符串冲突。你可以多次重复这个论点。重复H次。然后你有一组无限的字符串,其中所有字符串都会碰撞你的所有H哈希函数。 CQFD。

进一步阅读: 可变长度字符串的合理散列是不可能的 http://lemire.me/blog/archives/2009/10/02/sensible-hashing-of-variable-length-strings-is-impossible/

相关问题