我不确定这是否真的可行,因此我在这里问。有谁知道一个允许这样的算法吗?
const values = ['a', 'b', 'c', 'd'];
const hash = createHash(values); // => xjaks14sdffdghj23h4kjhgd9f81nkjrsdfg9aiojd
hash.includes('b'); // => true
hash.includes('v'); // => false
这个代码片段的作用是,它首先从值列表中创建某种hash
,然后检查某个值是否属于该哈希值。
答案 0 :(得分:2)
散列函数的主要思想是减小空间,即函数不是单射,因为它们从较大的域映射到较小的域。
因此他们产生碰撞。也就是说,有不同的元素x
和y
被映射到相同的哈希值:
h(x) = h(y)
所以基本上你丢失了给定参数x
的信息。
但是,为了回答是否包含所有值的问题,您需要保留所有信息(或至少所有非重复信息)。对于几乎所有实际的哈希函数来说,这显然是不可能的。
可能的哈希函数是身份功能:
h(x) = x for all x
但这不会减少空间,不实用。
一个自然的想法是计算各个元素的哈希值,然后将它们连接起来,比如
h(a, b, c) = (h(a), h(b), h(c))
但是这再次没有减少空间,哈希值只要是消息,不实用。
另一种可能性是删除所有重复项,因此给定值[a, b, c, a, b]
我们只保留[a, b, c]
。但是,在大多数例子中,这只是略微缩小了空间,再次不实用。
但无论你做什么,你都不能减少超过非重复的数量。否则你将无法回答某些值的问题。例如,如果我们使用[a, b, c, a]
但仅保留[a, b]
,则我们无法正确回答“c
包含”。
但是,有完美哈希函数(Wikipedia)的领域。那些是 injective 的哈希函数,它们不会产生冲突。
在某些领域,他们很感兴趣。
对于那些你可能能够回答这个问题的人,例如,如果计算逆是容易。
如果您谈论加密哈希函数,答案是否。
那些需要有三个属性(Wikipedia):
h
,应该很难找到m : hash(m) = h
m
应该很难找到m' : hash(m) = hash(m')
(m, m') : hash(m) = hash(m')
非正式地,你特别有:
对消息进行小的更改应该如此广泛地更改散列值,以使新散列值与旧散列值不相关。
如果你现在有这样的哈希值,你可以通过询问是否包含某些值来轻松地重建它。使用它你可以轻松地构建碰撞和类似的东西。
然而,细节将取决于特定的散列算法。
对于玩具示例,让我们使用之前的算法,只删除所有重复项:
[a, b, c, a, a] -> [a, b, c]
在这种情况下,我们会找到像
这样的消息[a, b, c]
[a, b, c, a]
[a, a, b, b, c]
...
所有映射到相同的哈希值。
答案 1 :(得分:1)
如果哈希函数产生冲突(几乎所有哈希函数都这样做),这是不可能的。
以这种方式考虑,例如h('abc') = x
和h('abd') = x
,如果原始字符串包含x
,您如何根据'd'
决定?
你可以说可以决定使用身份作为一个有功能,这将起到作用。
答案 2 :(得分:1)
简单的解决方案将是一个简单的哈希连接。
func createHash(values) {
var hash;
foreach (v in values)
hash += MD5(v);
return hash;
}
可以使用固定长度哈希和变量输入吗?我敢打赌这是不可能的。
如果是字符串哈希(例如在HashMaps中使用),因为它是加法的,我认为我们可以部分匹配(前缀匹配但不是后缀)。
const values = ['a', 'b', 'c', 'd'];
const hash = createStringHash(values); // => xjaks14sdffdghj23h4kjhgd9f81nkjrsdfg9aiojd
hash.includes('a'); // => true
hash.includes('a', 'b'); // => true
hash.includes('a', 'b', 'v'); // => false
答案 3 :(得分:0)
如果您不关心生成的哈希是什么样的,我建议只使用位数组。
对于每个可能的值,这将需要1位(对于大范围,这可能是很多位)。
注意:此表示在使用的位数方面是最佳的,假设您可以拥有的元素数量没有限制(超出每个可能值的1) - 如果可能的话要使用任何更少的位,您将拥有一种能够保证对任何数据进行压缩的算法,the pigeonhole principle是不可能的。
如果你的范围是a-z,你可以将其映射到0-25,然后[a,d,g,h]将映射到:
10010011000000000000000000 = 38535168 = 0x24c0000
(abcdefghijklmnopqrstuvwxyz)
如果你关心哈希的样子,你可以从上面得到输出并在其上执行perfect hash,将它映射到相同的长度哈希或更长的哈希值。
这种映射的一个简单示例是通过随机选择但确定性的值递增结果散列(即,对于我们转换的每个散列,它都是相同的) - 您也可以为每个字节执行此操作(使用环绕)如果你想(例如byte0 =(byte0 + 5)%255,byte1 =(byte1 + 18)%255)。
要确定元素是否出现,最简单的方法是反转上述操作(减去而不是添加),然后检查是否设置了相应的位。根据您的操作,也可能只转换单个字节。
如果您不介意误报,我可能会建议您只使用bloom filter。
简而言之,这为每个值设置了多个位,然后检查每个位以检查值是否在我们的集合中。但是为一个值设置的位可以与其他值的位重叠,这使我们能够以少量误报的成本显着减少所需的位数(假设元素的总数不是太大)