偏移独立散列函数

时间:2013-08-20 08:30:00

标签: arrays algorithm hash

是否有任何哈希函数为具有相同元素的向量生成相同的存储桶,具有相同的相对位置但移位 k 次?

例如:

hash([1,9,8,7]) -> b1
hash([9,8,7,1]) -> b1

hash([1,8,9,7]) -> b2
hash([1,9,8,5]) -> b3

v1 = [1,9,8,7] v2 = [9,8,7,1]两个向量都应该获得相同的哈希 v2 v1 左移k = 3次。

但是 v3 = [1,8,9,7]不保持相同的相对顺序, v4 = [1,9,8,5]不同的值,所以他们都没有得到哈希b1。

我最初的方法是计算每个向量的最大值,并将其位置视为参考(偏移= 0)。拥有它我只需要移动每个向量,以便最大值始终位于第一个位置。这种方式移位的矢量看起来是一样的。但是,向量可以有重复的元素,因此最大值具有不同的位置。

6 个答案:

答案 0 :(得分:4)

  1. 找到按字典顺序排列的最小阵列旋转。

    本机方法是检查O(n 2 )中的所有旋转,但可以使用Booth算法,Shiloach的快速经典算法或Duval的Lyndon分解算法在线性时间内完成。

    有关详情,请参阅this

  2. 计算旋转数组的哈希值。

    这可以通过各种方式完成。例如,Java可以这样做:

    hash = s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
    
  3. 具有不同元素的数组将散列到相同的值并不是不可能的(这对于散列是不可避免的),但是同一数组的所有旋转都将具有相同的散列。

答案 1 :(得分:1)

如果我们将b1与自身连接起来,那么我们得到:

[1,9,8,7,1,9,8,7]

此数组包含原始数组的所有循环排列。

如果我们为每个长度为4的子数组计算一个哈希并加入并组合它们,那么您将拥有一个唯一的哈希值。哈希函数计算可能需要进行一些优化,具体取决于数组的大小。

编辑:每个子阵列,除了最后一个,等于第一个!

答案 2 :(得分:1)

如果你不太关心偶尔的哈希冲突,你可以简单地将所有元素的总和作为哈希(但要注意浮点问题),因为这对于向量的任何旋转都是不变的。或者,您可以xor或总结各个元素的所有哈希值。您还可以根据后续元素的差异来计算某些内容(同时将最后一个元素包装到第一个元素)。添加一些对旋转不变的属性,并且两个“不相等”数组产生相同散列的可能性非常低。也许像是

n = length(x)
rot_invariant_hash = hash(n) + sum(hash(x[i])) + sum(hash(x[mod(i+1, n)] - x[i]))

您可以替换任何其他可交换(?)操作的所有总和,例如XOR。还要确保应用于差异的哈希函数不是标识函数,否则这些部分都将加起来为零。所有这些都需要O(n)计算时间。

只是好奇心:你的目标应用是什么?

答案 3 :(得分:1)

假设您始终将数字作为矢量分量,请计算:

  • 所有组件的产品
  • 相邻组件(d_ii)的所有差异(i+1) mod n的乘积, 其中1为所有非负差异添加

并将两者相乘。

第一个产品抽象出元素的顺序,这是由第二个产品模数组件旋转重新引入的。如果存在相同值的2个相邻组件,则每个差异加1会避免映射到0。

独立的第一个产品是不够的,因为它将所有组件排列映射到相同的哈希值。 独立的第二个产品是不够的,因为它将沿(1,...,1)偏移的所有向量映射到相同的值。

答案 4 :(得分:1)

不要散列数组的元素,而是散列两个相邻单元格的差异:

#include <stdio.h>

unsigned hashdiff(unsigned arr[], size_t siz);

        /* toy hash function: don't try this at home ... */
#define HASH1(v) ((v)*7654321)

unsigned hashdiff(unsigned arr[], size_t siz)
{
unsigned idx;
unsigned hash;

if (siz < 1) return 0;
if (siz < 2) return HASH1(arr[0]);

hash = HASH1( arr[0] - arr[siz-1] );

for(idx=1; idx < siz; idx++) {
        hash ^= HASH1(arr[idx] - arr[idx-1] );
        }

return hash;
}

unsigned arr1[] = {1,9,8,7};
unsigned arr2[] = {9,8,7,1 };

unsigned arr3[] = {1,8,9,7 };
unsigned arr4[] = {1,9,8,5 };

int main(void)
{
unsigned hash;

hash = hashdiff (arr1, 4); printf("%x\n", hash);
hash = hashdiff (arr2, 4); printf("%x\n", hash);
hash = hashdiff (arr3, 4); printf("%x\n", hash);
hash = hashdiff (arr4, 4); printf("%x\n", hash);

return 0;
}

结果:

./a.out
fee56452
fee56452
1100b22
fca02416

更新:如果您不希望{1,2,3,4}和{11,12,13,14}散列到相同的值,您可以像这样增加差异:

#define HASH1(v) ((v)*7654321)
#define HASH2(a,b) HASH1(3u*(a)-5u*(b))

unsigned hashdiff2(unsigned arr[], size_t siz)
{
unsigned idx;
unsigned hash;

if (siz < 1) return 0;
if (siz < 2) return HASH1(arr[0]);

hash = HASH2( arr[0] , arr[siz-1] );

for(idx=1; idx < siz; idx++) {
        hash ^= HASH2( arr[idx] , arr[idx-1] );
        }

return hash;
}

答案 5 :(得分:0)

我没有编码,但我认为它可行:

要获取哈希,您只需要捕获项目的顺序,并避免偏移。对这些项目进行排序:

a = [1,9,8,7]
s = sort(a) = [1,7,8,9]

现在捕捉它们之间的顺序:

1 => 9
7 => 1
8 => 7
9 => 8

snext = next(s, a) = [9,1,7,8]

现在结束并悄悄地说:

[1,7,8,9,9,1,7,8]

哈希吧。

要实现next()函数,只需使用向量a作为关联数组并遍历s项。

数组[9,8,7,1]会产生相同的哈希,因为它共享相同的项目,并且它们的相对顺序相等。

然而,数组[1,8,9,7]会产生不同的哈希值;它共享相同的项目,但它们的相对顺序不一样。

我希望它有所帮助。