说我有一个像foo
,bar
,baz
,hello
,world
等多达256个唯一字符串字符串,所以不是很多。它可以很容易地为200串或32串的所有意图和目的。希望该解决方案能够处理任意大小的套。
所以你把这个字符串并以某种方式将其映射到一个整数0-255。如果不只是这样做:
strings[currentString] = ID++
// strings['foo'] = 0
// strings['bar'] = 1
// strings['baz'] = 2
// ...
这将取决于它们所插入的顺序。理想情况下,他们将不会发生任何单个字符或字节莫名其妙的哈希唯一可能产生的,我不知道。但是,如果是不带记忆功能,从一组已知大小的需要任意字符串,并将其映射到一个整数,所以更喜欢:
// strings['foo'] = 6 + 15 + 15 = 36
// strings['bar'] = 2 + 1 + 16 = 19
// ...
虽然这不会因为碰撞的工作。我不知道如何去设计这样的哈希函数。所以,在某种程度上别的东西会在哪里工作从未有冲突的担心。
function hash(string, size) {
// return unique integer within size
}
hash('foo', 256) // something like 123
hash('bar', 256) // something like 101
hash('foo', 100) // something else like 50
hash('bar', 100) // something else like 25
我很想知道太多一般如何去建立这样一个功能,因为它似乎很困难,但不是绝对必要的问题。
此外,寻求与基本的JavaScript,没有任何特殊的辅助方法或特定浏览器的东西,做到这一点。
该组可能字符串是事先知道的。
我不相信你在找什么是不可能的,除非你知道所有256个字符串的时间提前。粗略地说,这里是证明了这一点:
假设存在一些f : S^* → [0, 255]
(注:S^*
意味着所有的有限长度的字符串)S.T.所有256长亚S ⊆ S^*
,s_1, s_2 ∈ S, f(s_1) = f(s_2) <=> s_1 = s_2
。由于f
不得持有的也有过任何输入内存,它必须在确定性映射[0, 255]
串相同的号码,不管是什么这个子集在不在。
然而,由Pigeonhole Principle,因为有超过256个字符串,我们必须至少有两个字符串映射到[0, 255]
之间的相同值。特别是,这意味着如果我们需要包含两个字符串的一个子集S
,为f
上述财产受到侵犯,矛盾。因此,f
不能存在。
如果你被允许知道256串散列,这是绝对有可能的。在一般情况下,你要找的是一个perfect hash function。
此链接提供的算法:https://www.cs.cmu.edu/~avrim/451f11/lectures/lect1004.pdf(参见56-57页)
引用:
方法1:一个
O(N^2)
空间溶液说我们都愿意有一个表,其大小是在我们的字典
N
的大小S
二次。于是,这里是构建一个完美的散列函数的简单方法。让H
普及和M=N^2
。然后,只需选择一个随机h
从H
并尝试一下!该要求是至少有50%的几率将有没有冲突。方法2:一个
O(N)
空间溶液我们将首先散列到大小的表
N
使用通用散列。这会产生一些冲突(除非我们是非常幸运的话)。不过,我们会再老调重弹使用方法各1箱,现蕾仓的大小来获得零次冲突。因此,认为该方案的方式是,我们有一个一级哈希函数h
和第一级表A
,然后N
二级散列函数h_1, ..., h_N
和N
第二级表A_1, ..., A_N
。为了查找一个元素x
,我们首先计算i=h(x)
,然后找到在A_i[h_i(x)]
的元素。
如果不只是这样做:[...]这将取决于它们所插入的顺序。
该组可能字符串是事先知道的。
如果你是罚款,要求被称为前期的字符串,但你就是不喜欢使用它们发生的顺序的随意性已经被插入,那么一个简单的方法是将字符串收集到一个数组,排序该数组(以获得确定性顺序),然后使用所得到的顺序:
var stringArray = [];
stringArray.push('foo');
stringArray.push('bar');
stringArray.push('baz');
// ...
stringArray = stringArray.sort();
var strings = {};
for (var i = 0; i < stringArray.length; ++i) {
strings[stringArray[i]] = i;
}
这里有一个想法,可能对各种输入了良好的效果的草图。下面的代码假定小写英文字母,无空格,只允许任何信件多达9个重复。
这个想法是,长度n
的任何排列可以通过检测多少次置换必须转化成身份置换之前必须施加到自身被映射到整数模n
。它的“威力”如果你愿意。美中不足的是,与相同的置换周期(描述这些无序整数分区)的任何置换,将导致在相同的“功率”,这我们使用作为最终散列。
为了产生置换,每个字母被分配给九个桶26,这取决于它是否是一个重复的一个,并推到一个阵列,随后丢失的索引从0到255。
像许多散列函数,这可能会导致冲突(这有可能通过基于输入的分析功能设置一些标志得到改善,虽然我还没有更仔细地考虑)。
function seq(n){
return [...Array(n)].map((_,i) => i);
}
function permute(p1, p){
return p1.map(x => p[x]);
}
function areEqual(p1, p){
for (let i=0; i<p.length; i++)
if (p1[i] != p[i])
return false;
return true;
}
function findPower(p1){
let count = 0;
const p = seq(p1.length);
let p2 = p1.slice();
for (let i=0; i<p.length; i++){
if (!areEqual(p, p2)){
p2 = permute(p2, p1);
count++;
} else {
return count;
}
}
return count;
}
// Returns the permutation based on
// the string, s
function hash(s){
// Each letter is in one of
// 9 buckets of 26, depending
// on if it's a duplicate.
let fs = new Array(26).fill(0);
let result = [];
for (let i=0; i<s.length; i++){
let k = s.charCodeAt(i) - 97;
result.push(26 * fs[k] + k);
fs[k]++;
}
const set = new Set(result);
for (let i=0; i<256; i++)
if (!set.has(i))
result.push(i);
return result;
}
function h(s){
return findPower(hash(s));
}
var strings = [
'foo',
'bar',
'baz',
'hello',
'world',
'etc'];
for (let s of strings)
console.log(`${ s }: ${ h(s) }`);