【问题标题】:How to implement a 3-bit based hash function for hashtable lookup key?如何为哈希表查找键实现基于 3 位的哈希函数?
【发布时间】:2021-04-06 13:49:28
【问题描述】:

我想在一个 URL 中有 8 个可能的字符,我们称它们为 1、2、3、4、5、6、7、8。然后我想将这些用作哈希表查找的键。我想知道如何实现基于 3 位的平衡/高度随机散列算法(8 个字符可以编码为 3 位),而不是使用 typical 基于 8 位的哈希算法。所以一些东西(在 JS 中用于演示目的)在给定输入是 3 位长的倍数的情况下会产生一个平衡良好的哈希表。所以你可能有:

8
88
888
18
81
8181

尽可能多的哈希键。字符来自 8 个字符集。所以我要做的就是从这个开始:

function hashBinary(bin) {
  // iterate through 3 bits at a time
  // build a nice random hash
}

function hashString(str) {
  let binary8Bit = str.split('').map(x => parseInt(x))
  // do something? to convert the 8-bit-chunk list to a 3-bit chunk list.
  let binary3Bit = new ArrayBuffer()
  // ... something
  return hashBinary(binary3Bit)
}

hashString('8181')
hashString('88')

你将如何开始正确地做到这一点?

尝试学习如何从头开始实现一个好的哈希算法,以及如何在非 8 位字符串上实现。

【问题讨论】:

  • 如果“典型的基于 8 位的哈希算法”设计得很好,如 siphash,没有理由产生不令人满意的分布。即使每个字符只有两种可能性(1 位)也是如此。你打算把return hash(binary3Bit) 变成return hashBinary(binary3Bit) 吗?
  • "尝试学习如何实现一个好的散列算法" - 在我们开始之前,请先定义一下“好”的含义。快速地?加密安全?一些other property?
  • 如果选择的哈希算法好的话,它在每一步只传递 3 位数据而不是 8 位数据时表现同样好。
  • @Bergi 这用于内存中的哈希表,例如实现 javascript 对象功能。它不需要任何安全性,它需要快速而且我猜是均匀分布的?无论您认为最适合实现性能快速的简单哈希映射。
  • 你的意思是“hash”是指它有一个固定(或最大)的大小,并且可能会发生冲突吗?

标签: javascript hash binary bit-manipulation


【解决方案1】:

正如 cmets 中所指出的,任何好的通用哈希函数都应该同样适用于您的输入类型——这就是哈希函数好的原因。但如果您真的需要,您可以将字符串子集双射映射到自然数中,然后对数字进行散列处理

一个非常简单的转换是将您的字符串解析为八进制数字,只是八进制数字是不同于01234567(此处为12345678)的字符。

function urlToInt(url) {
  // adapt `urlDigitToOct` if you use something different than `12345678`
  function urlDigitToOct = urlDigit => urlDigit - 1;
  const oct = url.replace(/./g, urlDigitToOct);
  return parseInt(oct, 8);
}

但是,这会认为您的某些 url 字符串是相等的。前导1s 被解释为前导零,可以在不改变含义的情况下从头开始添加或剪切。例如1 = 11 = 11141 = 141 = 1141(但不是4!)。

如果不同的字符串应该总是产生不同的数字,那么每个 url 字符三个位就不再足够了。您还需要存储有关字符串长度的信息。这是一个更通用的解决方案,密集枚举由一组给定的唯一字符组成的所有字符串。
枚举的工作方式如下:0 是空字符串,1 是给定集合中的第一个字符,2 是第二个字符,依此类推。考虑完所有字符后,我们继续处理长度为 2 的字符串,然后是长度为 3 的字符串,以此类推。

// use `digits = "12345678"` in your case
const digits = "abc";
const digitToVal = Array.from(digits).reduce((map, digit, idx) =>
  map.set(digit, idx), new Map());
const base = digitToVal.size;

function urlToInt(url) {
  // base^0 + base^1 + ... + base^(url.length-1) is a geometric series
  const countShorterUrls = (1 - base ** url.length) / (1 - base);
  return Array.from(url).reduce((acc, digit, idx) =>
acc * base + digitToVal.get(digit), 0) + countShorterUrls;
}

// manual test
["", "a", "b", "c", "aa", "cc", "aaa", "ccc", "aaaa"]
  .forEach(url => console.log(`url "${url}" has number ${urlToInt(url)}`));

现在您处理的是所有自然数而不是所有字符串的子集,您可以像往常一样实现散列算法。

【讨论】:

    猜你喜欢
    • 2016-03-25
    • 1970-01-01
    • 1970-01-01
    • 2012-06-03
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    • 2022-01-07
    • 2015-07-06
    相关资源
    最近更新 更多