Arrays 偏移量无关散列函数

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]具有不同的值

是否有任何哈希函数可以为具有相同元素、具有相同相对位置但移动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]由于v2v1左移的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
    (i+1)mod n
    )的乘积, 其中,所有非负差异加1
然后两者相乘

第一个产品抽象出元素的顺序,第二个产品模块组件旋转重新引入元素顺序。如果有两个相邻的组件具有相同的值,则向每个差异添加1可避免映射到0

独立的第一个产品是不够的,因为它将所有组件排列映射到相同的散列值。
独立的第二个乘积不够,因为它将沿(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;idx
  • 查找字典最小数组旋转

    原生方法是检查O(n2)中的所有旋转,但可以使用Booth算法、Shiloach的快速规范化算法或Duval的Lyndon分解算法在线性时间内完成

    更多信息,请参阅

  • 计算旋转数组的哈希值

    这可以通过多种方式实现。例如,Java将按如下方式进行:

    hash = 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]散列到相同的值。碰撞是生命的事实!您可以使用另一个非公共运算符/函数来代替减法。我知道,但您可能希望避免预处理的冲突