我正在尝试创建一个哈希函数,因此我可以判断具有相同大小的列表是否包含相同的元素。
例如,这就是我想要的:
f((1 2 3))= f((1 3 2))= f((2 1 3))= f((2 3 1))= f((3 1 2))= f((3 2 1))。
任何想法我怎么能解决这个问题?我已经尝试过所有元素的平方和,但事实证明存在碰撞,例如f((2 2 5))= 33 = f((1 4 4))这是错误的,因为列表不是相同。
如果有的话,我正在寻找一种简单的方法。
答案 0 :(得分:2)
对列表进行排序,然后:
list.each do |current_element|
hash = (37 * hash + current_element) % MAX_HASH_VALUE
end
答案 1 :(得分:1)
所以你正在寻找提供这些属性的东西,
1. If h(x1) == y1, then there is an inverse function h_inverse(y1) == x1
2. Because the inverse function exists, there cannot be a value x2 such that x1 != x2, and h(x2) == y1.
Knuth's Multiplicative Method
在Knuth的“计算机编程艺术”第6.4节中,引入了乘法散列方案作为写入散列函数的方法。密钥乘以2 ^ 32(2654435761)的黄金比率以产生哈希结果。
hash(i)=i*2654435761 mod 2^32
由于2654435761和2 ^ 32没有共同的共同因素,因此乘法产生密钥到散列结果的完整映射而没有重叠。如果键值很小,则此方法非常有效。如果密钥在高位中变化,则产生错误的散列结果。与所有乘法一样,高位数的变化不会影响乘法结果的低位数。
罗伯特詹金斯的96位混音功能
罗伯特詹金斯基于一系列减法,异或,和位移开发了一个哈希函数。
本文中的所有资源都是用Java方法编写的,其中运算符'>>>'代表无符号右移的概念。如果要将源转换为C,则应将Java'int'数据类型替换为C'int32_t'数据类型,并且应将Java'long'数据类型替换为C'intint64_t'数据类型。
以下来源是散列函数的混合部分。
int mix(int a, int b, int c)
{
a=a-b; a=a-c; a=a^(c >>> 13);
b=b-c; b=b-a; b=b^(a << 8);
c=c-a; c=c-b; c=c^(b >>> 13);
a=a-b; a=a-c; a=a^(c >>> 12);
b=b-c; b=b-a; b=b^(a << 16);
c=c-a; c=c-b; c=c^(b >>> 5);
a=a-b; a=a-c; a=a^(c >>> 3);
b=b-c; b=b-a; b=b^(a << 10);
c=c-a; c=c-b; c=c^(b >>> 15);
return c;
}
您可以阅读here
的详细信息答案 2 :(得分:1)
如果你真的不想碰撞,你可能会失败。有N个选择k组大小为k的元素在1..N(更糟糕的是,如果你允许重复)。所以想象你有N = 256,k = 8,那么N选择k是~4 x 10 ^ 14。你需要一个非常大的整数来清楚地散列所有这些集合。
可能你有N,k这样你仍然可以做这项工作。祝你好运。
如果您允许偶尔发生碰撞,您有很多选择。从简单的事情,如你的建议(添加元素的方块)和计算xor元素,到复杂的事情,如排序,打印到字符串,并在它们上计算MD5。但是,由于仍然可以进行冲突,您必须通过比较原始列表来验证任何哈希匹配(如果您对它们进行排序,这很容易)。
答案 3 :(得分:0)
如果所有元素都是数字并且它们具有最大值,那么这不是太复杂,您可以对这些元素进行排序,然后将它们一个接一个地放在最大值+ 1的基础上。
很难用语言描述...... 例如,如果您的最大值为9(这使其易于理解),您将拥有:
f(2 3 9 8)= f(3 8 9 2)= 2389
如果您的最大值是99,那么您将拥有:
f(16 2 76 8)=(0)2081676
在你的2,2和5的例子中,如果你知道你永远不会得到高于5的任何东西,你可以在基数6中“组合”结果,这样就是:
f(2 2 5)= 2 * 6 ^ 2 + 2 * 6 + 5 = 89 f(1 4 4)= 1 * 6 ^ 2 + 4 * 6 + 4 = 64
答案 4 :(得分:0)
组合哈希值很难,我在Boost内找到了这种方式(没有解释,但也许有人会认出来):
template <class T>
void hash_combine(size_t& seed, T const& v)
{
seed ^= hash_value(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
它应该很快,因为只有移位,添加和xor发生(除了实际的散列)。
但是,除了列表顺序之外的要求不会影响最终结果,这意味着您首先必须对它进行排序,这是一个O(N log N)操作,因此它可能不合适。
此外,由于没有更严格的边界来提供无碰撞散列函数是不可能的,如果散列等于......你仍然必须实际比较排序列表...
答案 5 :(得分:0)
我正在尝试创建一个哈希函数,因此我可以判断两个具有相同大小的列表是否包含相同的元素。
[...]但事实证明存在碰撞
这两句话表明你正在使用错误的工具来完成工作。散列点(除非它是'完美散列',这似乎不适合此问题)不是保证相等,或者为每个给定的输入提供唯一的输出。在通常情况下,它不能,因为潜在的输入多于潜在的输出。
无论您选择哪种哈希函数,您的哈希系统总是必须处理冲突的可能性。虽然不同的哈希意味着不等式,但它不遵循相等的哈希意味着平等。
关于你的实际问题:一个开始可能是按升序对列表进行排序,然后使用排序值,就好像它们是整数的素数分解中的主要权力一样。重建此整数(以最大散列值为模)并且存在散列值。
例如:
2 1 3
排序变为
1 2 3
将此视为主要权力给予
2^1.3^2.5^3
构造
2.9.125 = 2250
给出2250
作为您的哈希值,它与1 2 3
的任何其他顺序的哈希值相同,也不同于任何其他三个数字序列的哈希值计算时不会溢出最大哈希值。
答案 6 :(得分:0)
解决基本问题的一种天真的方法(以顺序不敏感的方式比较列表)是将所有被比较的列表转换为集合(在Python中设置或在Java中使用HashSet)。这比制作哈希函数更有效,因为完美哈希似乎对您的问题至关重要。对于几乎任何其他方法,根据输入,碰撞是不可避免的。