Arrays 偏移量无关散列函数
是否有任何哈希函数可以为具有相同元素、具有相同相对位置但移动k次的向量生成相同的存储桶 例如:Arrays 偏移量无关散列函数,arrays,algorithm,hash,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]具有不同的值
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)。这样我只需要移动每个向量,使最大值始终位于第一个位置。这样,移位向量看起来是一样的。但是,向量可以有重复的元素,因此最大值具有不同的位置。如果我们将b1与自身连接起来,则我们得到:
[1,9,8,7,1,9,8,7]
此数组包含原始数组的所有循环置换
然后,如果我们为长度为4的每个子数组计算一个散列,并将这些子数组连接并组合起来,您将拥有一个唯一的散列。哈希函数计算可能需要一些优化,具体取决于数组的大小
编辑:每个子阵列,除了最后一个,它等于第一个 如果您不太关心偶尔的散列冲突,您可以简单地将所有元素的总和作为散列(但要注意浮点问题),因为这对向量的任何旋转都是不变的。或者,您可以
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)计算时间
只是好奇:您的预期用途是什么?假设您总是将数字作为向量分量,计算:
- 所有组件的产品
- 相邻组件的所有差异(
,i
)的乘积, 其中,所有非负差异加1(i+1)mod n
独立的第二个乘积不够,因为它将沿(1,…,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;
}
更新:如果您不希望{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;
}
a = [1,9,8,7]
s = sort(a) = [1,7,8,9]
定义HASH1(v)((v)*7654321)
#定义HASH2(a,b)HASH1(3u*(a)-5u*(b))
无符号hashdiff2(无符号arr[],大小为siz)
{
无符号idx;
无符号散列;
如果(siz<1)返回0;
if(siz<2)返回HASH1(arr[0]);
hash=HASH2(arr[0],arr[siz-1]);
对于(idx=1;idxhash = s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
具有不同元素的数组将散列为相同的值并非不可能(散列不可避免),但相同数组的所有旋转将具有相同的散列。我没有对其进行编码,但我认为它可以工作: 要获取散列,只需捕获项目的顺序,并避免偏移量。按如下方式对项目进行排序:
#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;
}
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]
现在,concat s和snext:
[1,7,8,9,9,1,7,8]
把它搞糟
要实现next()函数,只需使用向量a作为关联数组并迭代s项
数组[9,8,7,1]将产生相同的散列,因为它共享相同的项,并且它们的相对顺序相等
然而,数组[1,8,9,7]产生不同的散列;它共享相同的项目,但它们的相对顺序不同
我希望这会有所帮助。不需要将向量与自身连接起来,只需从每个向量位置以循环方式迭代,这样可以在不加倍内存使用的情况下提供相同的结果。我认为您的解决方案在形式上是正确的,但如果我没有错,并且假设每个子数组散列计算都是O(n)[n=4,在本例中]您的方法至少有O(n^2)的时间复杂度。我想知道它是否可以改进,这取决于散列函数的计算:比如h(k)=a0+a1k^1+a2k^2+。。。然后,我们可以计算第二个置换的h,它与前一个置换的h相比,具有恒定的运算次数。所以h(1,9,8,7)可以被重用来计算h(9,8,7,1),我们可以重用9,8,7部分(除以k)。这将[1,2,3,4]和[11,12,13,14]散列到相同的值。碰撞是生命的事实!您可以使用另一个非公共运算符/函数来代替减法。我知道,但您可能希望避免预处理的冲突