Algorithm 列表哈希函数

Algorithm 列表哈希函数,algorithm,hash,Algorithm,Hash,我正在尝试创建一个哈希函数,这样我就可以判断相同大小的列表是否包含相同的元素 例如,这就是我想要的: f((1233))=f((1232))=f((213))=f((231))=f((3112))=f((322))=f((321)) 你知道我怎样才能解决这个问题吗?我试过计算所有元素的平方和,但结果证明存在碰撞,例如f((2 2 5))=33=f((1 4 4)),这是错误的,因为列表不同 我正在寻找一种简单的方法,如果有的话。对列表进行排序,然后: list.each do |current_

我正在尝试创建一个哈希函数,这样我就可以判断相同大小的列表是否包含相同的元素

例如,这就是我想要的:
f((1233))=f((1232))=f((213))=f((231))=f((3112))=f((322))=f((321))

你知道我怎样才能解决这个问题吗?我试过计算所有元素的平方和,但结果证明存在碰撞,例如f((2 2 5))=33=f((1 4 4)),这是错误的,因为列表不同


我正在寻找一种简单的方法,如果有的话。

对列表进行排序,然后:

list.each do |current_element|
  hash = (37 * hash + current_element) % MAX_HASH_VALUE
end

如果所有元素都是数字,并且它们都有一个最大值,这并不太复杂,您可以对这些元素进行排序,然后在最大值+1的基础上将它们一个接一个地放在一起

难以用语言描述。。。 例如,如果您的最大值为9(这使其易于理解),您将有:

f(2 3 9 8)=f(3 8 9 2)=2389

如果最大值为99,则会:

f(162768)=(0)2081676

在使用2、2和5的示例中,如果您知道永远不会得到高于5的任何结果,您可以在基数6中“合成”结果,因此:

f(25)=2*6^2+2*6+5=89
f(14)=1*6^2+4*6+4=64

所以你正在寻找提供这些属性的东西

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的乘法方法

在Knuth的《计算机编程的艺术》第6.4节中,介绍了一种乘法散列方案,作为编写散列函数的方法。将密钥乘以黄金分割比2^32(2654435761)以生成哈希结果

hash(i)=i*2654435761 mod 2^32
由于2654435761和2^32没有公共因子,因此乘法生成密钥到散列结果的完整映射,没有重叠。如果键具有较小的值,则此方法非常有效。如果密钥在高位发生变化,则会产生错误的哈希结果。与所有乘法一样,高位的变化不会影响乘法结果的低位

Robert Jenkins的96位混合函数

Robert Jenkins开发了一个基于减法、异或和位移位序列的哈希函数

本文中的所有源代码都是以Java方法编写的,其中运算符“>>>”表示无符号右移的概念。如果要将源代码转换为C,则应将Java“int”数据类型替换为C“uint32_t”数据类型,并将Java“long”数据类型替换为C“uint64_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;
}
intmix(inta,intb,intc)
{
a=a-b;a=a-c;a=a^(c>>13);
b=b-c;b=b-a;b=b^(a>>13);
a=a-b;a=a-c;a=a^(c>>12);
b=b-c;b=b-a;b=b^(a>>5);
a=a-b;a=a-c;a=a^(c>>3);
b=b-c;b=b-a;b=b^(a>>15);
返回c;
}

您可以从

中阅读详细信息。如果您真的不想发生碰撞,那么您可能运气不好。有N个大小为k的choose k集合,元素为1..N(如果允许重复,则更糟)。假设你有N=256,k=8,然后选择k是~4x10^14。您需要一个非常大的整数来清楚地散列所有这些集合

可能你有N,k,这样你仍然可以做这件事。祝你好运


如果你允许偶尔的碰撞,你有很多选择。从简单的事情,如您的建议(添加元素的平方)和计算元素的异或,到复杂的事情,如排序,打印到字符串,并在其上计算MD5。但由于仍然可能发生冲突,因此您必须通过比较原始列表来验证任何哈希匹配(如果您对它们进行排序,这很容易)。

组合哈希值很难,我在以下范围内找到了这种方法(没有解释,尽管可能有人会识别它):

模板
无效哈希组合(大小、t和种子、t常数和v)
{
seed^=散列值(v)+0x9e3779b9+(seed>2);
}
它应该很快,因为只有移位、加法和异或发生(除了实际的散列)

但是,列表顺序的要求并不影响最终结果,这意味着您必须首先对其进行排序,这是一个O(N logn)操作,因此它可能不适合

此外,由于没有更严格的边界就不可能提供无冲突哈希函数,因此如果哈希值相等,您仍然必须实际比较排序列表

我正在尝试创建一个哈希函数,这样我就可以判断两个大小相同的列表是否包含相同的元素

[…]但事实证明存在碰撞

这两句话表明你在工作中使用了错误的工具。散列的要点(除非它是“完美散列”,这似乎不适合这个问题)不是保证相等,或者为每个给定的输入提供唯一的输出。在一般情况下,它不能,因为潜在的输入比潜在的输出多

无论您选择什么样的散列函数,您的散列系统都必须处理冲突的可能性。虽然不同的哈希值意味着不平等,但并不意味着相等的哈希值意味着平等

至于您的实际问题:一个开始可能是按升序对列表排序,然后使用排序后的值,就像它们是整数素数分解中的素数幂一样。重新构造这个整数(以最大散列值为模),就会有一个散列值

例如:

2 1 3
分类成为

1 2 3
将其视为素数幂给出

2^1.3^2.5^3
哪种结构

2.9.125 = 2250

2250
作为散列值,该散列值将与
1 2 3
的任何其他顺序的散列值相同,也不同于计算时不会溢出最大散列值的任何其他三个数字序列的散列值。

一种解决基本问题的天真方法(以顺序不敏感的方式比较列表)是将所有要比较的列表转换为一个集合(Python中的集合或J中的HashSet)
2.9.125 = 2250