【问题标题】:Constant-time hash for strings?字符串的恒定时间哈希?
【发布时间】:2010-12-24 03:14:30
【问题描述】:

另一个关于 SO 的问题提出了一些语言的工具来散列字符串,以便在表中快速查找。这方面的两个示例是 .NET 中的字典 和 Python 中的 {} 存储结构。其他语言当然支持这种机制。 C++ 有它的映射,LISP 有一个等价物,就像大多数其他现代语言一样。

在对字符串的哈希算法可以在恒定时间内执行的问题的答案中争辩说,一位拥有 25 年编程经验的 SO 成员声称任何东西都可以在恒定时间内进行哈希处理。我个人的观点是,这不是真的,除非您的特定应用程序在字符串长度上设置了边界。这意味着某个常数 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 成员声称的长度。

谢谢。

【问题讨论】:

    标签: string associative-array string-algorithm


    【解决方案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/

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 一个好主意,但值得注意的是,插入字符串表的过程会增加与表中字符串数量成正比的时间,除非表是基于哈希的,在这种情况下问题被还原到原始状态。
      • 嗯,使用 trie,插入其中的时间与最长公共前缀成正比,这是另一种选择。 :)
      • @Nick Johnson 你误会我了,我想。我正在寻找一种恒定时间的方法来唯一标识字符串。这意味着,如果我向您提供 2 个新字符串,您可以在恒定时间内“散列”它们,这样如果一个字符串是 500 个字符,下一个是 5 个字符,它们需要相同的理论时间来确定唯一性。
      • 是的,如果字符串每次都是新的,显然这是不可能的(除非像只散列前 n 个字符这样的无聊),但是如果您需要对同一组字符串进行多次比较,实习将每个哈希的时间减少到 O(1)。
      【解决方案3】:

      如果不冒严重的哈希冲突的风险,您无法轻松地为字符串实现通用的恒定时间哈希算法。

      如果它是恒定的时间,您将无法访问字符串中的每个字符。举个简单的例子,假设我们取前 6 个字符。然后来了一个人并尝试对一组 URL 进行哈希处理。 has 函数将看到每个字符串的“http://”。

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

      【讨论】:

        【解决方案4】:

        虽然我无法想象无限长度字符串的固定时间哈希函数,但确实没有必要。

        使用散列函数背后的想法是生成散列值的分布,使得 许多字符串不太可能发生冲突 - 对于所考虑的域。该密钥将允许直接访问数据存储。这两者相结合会产生恒定时间查找 - 平均而言

        如果发生这样的冲突,查找算法将退回到更灵活的查找子策略。

        【讨论】:

        • 我同意,但是对于像关联数组这样的语言结构,您不想尽可能保证唯一性吗?
        【解决方案5】:

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

        在哈希函数的质量和它所需要的计算量之间折衷任何事情都是可能的,而且长字符串上的哈希函数无论如何都必须有冲突。

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

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

            【解决方案7】:

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

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

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

            【讨论】:

            • 问题在于非常相似的字符串很可能具有相同的哈希值。通常,单个位更改应该会更改哈希中的所有位,因此两个字符串冲突的可能性与它们的相似性无关。 -- 也就是说,如果你不需要担心紧密的字符串碰撞,你的想法就会奏效。
            猜你喜欢
            • 2016-12-28
            • 2020-04-18
            • 1970-01-01
            • 1970-01-01
            • 2015-10-11
            • 1970-01-01
            • 2013-01-20
            • 2011-08-18
            • 2012-08-23
            相关资源
            最近更新 更多