【问题标题】:Creating a hash of a string thats sortable创建可排序的字符串的哈希
【发布时间】:2011-01-01 11:06:08
【问题描述】:

有没有办法创建字符串的哈希值,这些哈希值可以被排序并且与字符串本身被排序时具有相同的结果?

【问题讨论】:

  • 为什么不简单地将排名顺序添加到哈希中?

标签: language-agnostic sorting hash


【解决方案1】:

我偶然发现了这一点,尽管每个人的答案都是正确的,但我需要一个完全类似的解决方案才能在 elasticsearch 中使用(不要问为什么)。有时我们并不需要针对所有情况的完美解决方案,我们只需要一个处理可接受的约束即可。我的解决方案能够为字符串的第一个 n 字符生成可排序的哈希码,我做了一些初步测试并且没有任何冲突。您需要预先定义使用的charset 并与n 一起玩,使其成为排序所需的第一个字符的被认为可接受的值,并尝试将结果哈希码保持在定义类型的正间隔中以使其工作,就我而言,对于 Java Long 类型,我最多可以使用 13 个字符。 以下是我的 Java 代码,希望它能帮助其他需要它的人。

String charset = "abcdefghijklmnopqrstuvwxyz";

public long orderedHash(final String s, final String charset, final int n) {
  Long hash = 0L;

  if(s.isEmpty() || n == 0)
    return hash;

  Long charIndex = (long)(charset.indexOf(s.charAt(0)));
  if(charIndex == -1)
    return hash;

  for(int i = 1 ; i < n; i++)
    hash += (long)(charIndex * Math.pow(charset.length(), i));

  hash += charIndex + 1 + orderedHash(s.substring(1), charset, n - 1);

  return hash;
}

例子:

orderedHash("a", charset, 13)              // 1
orderedHash("abc", charset, 13)            // 4110785825426312
orderedHash("b", charset, 13)              // 99246114928149464
orderedHash("google", charset, 13)         // 651008600709057847
orderedHash("stackoverflow", charset, 13)  // 1858969664686174756
orderedHash("stackunderflow", charset, 13) // 1858969712216171093
orderedHash("stackunderflo", charset, 13)  // 1858969712216171093 same, 13 chars limitation 
orderedHash("z", charset, 13)              // 2481152873203736576
orderedHash("zzzzzzzzzzzzz", charset, 13)  // 2580398988131886038
orderedHash("zzzzzzzzzzzzzz", charset, 14) // -4161820175519153195 no good, overflow
orderedHash("ZZZZZZZZZZZZZ", charset, 13)  // 0 no good, not in charset

如果需要更高的精度,例如使用无符号类型或由两个 long 组成的复合类型,并使用子字符串计算哈希码。

编辑:虽然以前的算法足以满足我的使用,但我注意到如果字符串的长度没有大于所选的n,它并没有真正正确地排序字符串。有了这个新算法,现在应该没问题了。

【讨论】:

    【解决方案2】:

    正如其他人指出的那样,完全按照您的要求去做是不切实际的。您必须将字符串本身用作哈希,这将限制可能被“哈希”的字符串的长度等等。

    维护“排序哈希”数据结构的明显方法是维护排序列表(例如堆或二叉树)和数据的哈希映射。插入和删除将是 O(log(n)),而检索将是 O(1)。我不确定这是否值得额外的复杂性和开销。

    如果您有一个特别大的数据集,主要是只读的并且对数时间检索过于昂贵,那么我想它可能很有用。请注意,更新的成本实际上是常数时间(散列)和对数时间(二叉树或堆)操作的总和。然而,在渐近分析期间,O(1) + O(log(n)) 减少为两项中较大的一项。 (基本成本仍然存在 --- 与任何实施工作相关,无论其理论上不相关)。

    对于很大范围的数据集大小,维护这种假设的混合数据结构的成本可以估计为维护任何一个纯数据结构的成本的“两倍”。 (换句话说,二叉树的许多实现可以在时间成本上扩展到数十亿个元素(2^~32 左右),这与典型哈希函数的成本相当)。因此,我很难说服自己,这种增加的代码复杂性和(混合数据结构的)运行时成本实际上会对给定项目有益。

    (注意:我看到 Python 3.1.1 添加了“有序”字典的概念......这类似于排序,但不完全相同。从我收集的信息来看,有序字典保留了排序的顺序元素被插入到集合中。我似乎还记得一些关于“视图”的讨论......语言中的对象可以以某种特定方式访问字典的键(排序,反转,反向排序,......)在(可能)比通过内置的“sorted()”和“reversed()”传递一组键的成本更低。我没有使用这些,也没有查看实现细节。我猜其中一个“视图”就像一个惰性评估的索引,在调用时执行必要的排序,并使用某种标志或触发器(观察者模式或侦听器)存储结果,这些标志或触发器在后端源集合更新时会重置。在该方案中对“视图”的调用将更新其索引;子序列调用将能够使用这些资源ults 只要没有对字典进行插入或删除。在关键更改之后对视图的任何调用都会产生更新视图的成本。然而,这都是我的纯粹猜测。我之所以提到它,是因为它还可能提供一些解决问题的替代方法的见解。

    【讨论】:

      【解决方案3】:

      您实质上是在询问是否可以将键字符串压缩为更小的键,同时保留它们的排序顺序。所以这取决于你的数据。例如,如果您的字符串仅由十六进制数字组成,则可以将它们替换为 4 位代码。

      但是对于一般情况,是做不到的。您最终会将每个源密钥“散列”到自身中。

      【讨论】:

        【解决方案4】:

        是的。它使用整个输入字符串作为哈希调用。

        【讨论】:

          【解决方案5】:

          没有。散列必须包含与其要替换的字符串相同的信息量。否则,如果两个字符串映射到同一个哈希值,你怎么可能对它们进行排序?

          另一种思考方式是:如果我有两个字符串“a”和“b”,那么我使用这种保留散列函数对它们进行散列并得到 f(a) 和 f(b)。但是,有 无限 个字符串大于“a”但小于“b”。这需要将字符串散列到任意精度的实数值(因为基数)。最后,您基本上只是将字符串编码为数字。

          【讨论】:

            【解决方案6】:

            这是不可能的,至少如果您允许的字符串长度超过哈希大小。您有 256^(最大字符串大小)可能的字符串映射到 256^(哈希大小)哈希值,因此您最终会得到一些未排序的字符串。

            想象一下最简单的散列:将每个字符串截断为(散列大小)字节。

            【讨论】:

              【解决方案7】:

              除非字符串少于散列,并且散列是perfect,否则不会。即使这样,您仍然必须确保哈希顺序与字符串顺序相同,除非您提前知道所有字符串,否则这可能是不可能的。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-06-05
                • 2010-11-15
                • 1970-01-01
                • 1970-01-01
                • 2015-10-11
                • 2012-09-18
                相关资源
                最近更新 更多