创建可排序字符串的哈希值

时间:2010-01-04 23:24:31

标签: language-agnostic sorting hash

无论如何都要创建字符串的哈希值,其中哈希值可以排序并且结果与字符串本身的排序结果相同?

7 个答案:

答案 0 :(得分:9)

这是不可能的,至少如果你允许字符串长于散列大小。你有256 ^(最大字符串大小)可能的字符串映射到256 ^(散列大小)哈希值,所以你最终会得到一些未排序的字符串。

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

答案 1 :(得分:6)

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

答案 2 :(得分:2)

正如其他人所指出的,完全按照你的要求去做是不切实际的。你必须使用字符串本身作为哈希,它会限制可能被“散列”的字符串的长度,等等。

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

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

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

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

答案 3 :(得分:1)

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

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

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

但是对于一般情况,它无法完成。您最终会将每个源密钥“散列”到自身中。

答案 6 :(得分: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 大,它就不能真正正确地对字符串进行排序。有了这个新算法,现在应该没问题了。

相关问题