哈希函数对元素顺序不敏感

Hash functions not sensitive to element order

提问人:hopeimnotstupid 提问时间:1/19/2023 更新时间:1/19/2023 访问量:139

问:

我正在处理具有非重复元素的整数序列,出于某些原因,我试图通过构建哈希集来删除重复项。

int * a = {123, 145, 210, 77};
int * b = {145, 77, 123, 210}; // should be removed
int * c = {123, 37, 16};
int * d = {123, 145, 72, 91};

是否有对 和 返回相同结果的顺序不敏感的哈希函数?ab

我已经提出了一些解决方案,但它们表现不佳:

sorting - 序列是不可变的,排序将占用额外的空间和 O(NlogN) 时间。

xor - 序列中的元素范围从 0 到数百,可能会浪费许多哈希值。

还有其他方法吗?

C 序列 哈希集

评论

3赞 PhilMasteG 1/19/2023
可能是一个幼稚的问题:将它们相乘怎么样?
1赞 Lundin 1/19/2023
数据项的最大值是多少?
1赞 user3386109 1/19/2023
要在 XOR 哈希中获取更多位,请将每个元素乘以一个大常量,然后将乘积异或到哈希中。对于 32 位哈希,常用的常量是 。乘以该常量会导致其中一个小整数和一个 32 位整数之间出现 1 对 1 的映射。0xDEECE66D
1赞 PhilMasteG 1/19/2023
@Tenobaal 当然,这是 XY 问题的一个实例。但OP引用了XOR作为解决方案。因此,我总结说,对于提出的特定问题,乘法同样可以,即“是否还有其他不敏感的哈希函数?
1赞 Tenobaal 1/19/2023
@PhilMasteG这是一个有效的哈希函数,但仅仅找到重复的数组是不够的。我只是指出了这一点。对于哈希,我要么将所有数字相加,要么根据它们的索引和异或结果旋转单词。

答:

0赞 Tenobaal 1/19/2023 #1

哈希并不是您应该做的唯一事情。由于信息丢失,完全不同的数组可能会返回相同的哈希值。为了仅删除重复项,您还需要检查等效性。哈希集也可以做到这一点,但根据哈希放置元素,因此您可以更轻松地找到它们。

下面是一个示例实现:

#include <stdlib.h>
#include <stdbool.h>

struct hashset {
    int count;
    int capacity;
    struct hashset_element {
        int hash;
        int arrlen;
        int *arrval;
    } *elements;
};

void init_hashset(struct hashset *set) {
    set->count = 0;
    set->capacity = 0;
    set->elements = NULL;
}

int hash(int *arr, int len) {
    // you can replace this hash function
    // this is a pretty simple one
    int out = 0;
    for (int i = 0; i < len; i++) {
        out += arr[i];
    }
    return out;
}

void arrequals(int *arr1, int len1, int *arr2, int len2) {
    if (len1 != len2)
        return false;
    arr1srt = sort(arr1, len1);
    arr2srt = sort(arr2, len2);
    for (int i = 0; i < len1; i++) {
        if (arr1srt[i] != arr2srt[i])
            free(arr1srt);
            free(arr2srt);
            return false;
    }
    free(arr1srt);
    free(arr2srt);
    return true;
}

bool hashset_contains(struct hashset *set, int *arr, int len) {
    int rawhash = hash(arr, len);
    int hash = rawhash % set->capacity;
    for (int i = hash; i < set->capacity; i++) {
        if (set->elements[i]->arrval == NULL)
            return false;
        if (arrequals(set->elements[i]->arrval,
        set->elements[i]->arrlen, arr, len)
            return true;
    }
    for (int i = 0; i < hash; i++) {
        if (set->elements[i]->arrval == NULL)
            return false;
        if (set->elements[i]->hash == rawhash &&
        arrequals(set->elements[i]->arrval,
        set->elements[i]->arrlen, arr, len)
            return true;
    }
    return false;
}

void hashset_realloc(struct hashset *set) {
    struct hashset_element* oldarr = set->elements;
    int old_capacity = set->capacity;
    set->elements = malloc(sizeof(struct hash_element) * set->capacity + 1024);
    set->capacity += 1024;
    for (int i = 0; i < set->capacity; i++) {
        if (oldarr[i]->arrval != NULL)
            hashset_add_element(set, oldarr[i]->arrval, oldarr[i]->arrlen);
    }
}

void hashset_add_element(struct hashset *set, int *arr, int len) {
    if (!hashset_contains(set, arr, len)) {
        if (set->count >= set->capacity / 2) {
            realloc_hashset(set);
        }
        int rawhash = hash_element(arr, len);
        int hash = rawhash % set->capacity;
        for (int i = hash; i < set->capacity; i++) {
             if (set->elements[i]->arrval == NULL) {
                 set->elements[i]->hash = rawhash;
                 set->elements[i]->arrval = arr;
                 set->elements[i]->arrlen = len;
                 set->count++;
                 return;
             }
        }
        for (int i = 0; i < hash; i++) {
             if (set->elements[i]->arrval == NULL) {
                 set->elements[i]->hash = rawhash;
                 set->elements[i]->arrval = arr;
                 set->elements[i]->arrlen = len;
                 set->count++;
                 return;
             }
        }
    }
}

void destroy_hashset(struct hashset *set) {
    if (set->elements != NULL)
        free(set->elements);
}

int hashset_to_array(struct hashset *set, int **arrout, int *lenout, int maxlen) {
    int w = 0;
    for (int i = 0; i < capacity; i++) {
        if (w >= maxlen)
            break;
        arrout[w] = set->elements[i]->arrval;
        lenout[w] = set->elements[i]->arrlen;
        w++;
    }
    return set->count;
}

我没有测试此代码,但是尝试一下,如果我的代码中有错误,请随时纠正我。您必须自己实现排序功能。我不知道数组有多大,你正在使用,所以我无法为你选择一个理想的算法。顺序不敏感的比较只能在 中进行。 如果整数具有最大大小,则可能小到足以使用计数表。O(n*log(n))O(n)

在理想情况下,此哈希集的运行时为 。最坏情况的运行时是 ,这不太可能发生。哈希算法的运行时为 ,这并不理想,但对于小型数组来说还可以。O(1)O(n)O(n)