是否有任何哈希函数为具有相同元素的向量生成相同的存储桶,具有相同的相对位置但移位 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)。拥有它我只需要移动每个向量,以便最大值始终位于第一个位置。这种方式移位的矢量看起来是一样的。但是,向量可以有重复的元素,因此最大值具有不同的位置。
答案 0 :(得分:4)
找到按字典顺序排列的最小阵列旋转。
本机方法是检查O(n 2 )中的所有旋转,但可以使用Booth算法,Shiloach的快速经典算法或Duval的Lyndon分解算法在线性时间内完成。
有关详情,请参阅this。
计算旋转数组的哈希值。
这可以通过各种方式完成。例如,Java可以这样做:
hash = s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
具有不同元素的数组将散列到相同的值并不是不可能的(这对于散列是不可避免的),但是同一数组的所有旋转都将具有相同的散列。
答案 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_i
,i
)的所有差异(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]会产生不同的哈希值;它共享相同的项目,但它们的相对顺序不一样。
我希望它有所帮助。