• 常见的哈希函数


    原理参考《算法导论》

    一、除法散列函数

    根据描述实现算法,先取对应种子

    int hash_mod_seed(int n, int iCheck) {//存放n个关键字,一次不成功的查找要检查iCheck个关键字,默认一个字符是8位
        int iStart= n / iCheck, prime = (iStart == 1) ? 2 : iStart;
        assert(iCheck >= 0 && iCheck <= 8);
        //odd起始要跳过已经判断了的奇数
        for (int j = 0, odd = (iStart % 2 == 0) ? iStart / 2 : (iStart - 1) / 2 + 1;
            j < 8 - iCheck; odd++) {
            //生成一个素数
            bool fPrime = true;
            for (int k = 2; k <= sqrt(prime); k++)
                if (prime % k == 0) {
                    fPrime = false;
                    break;
                }
            if (fPrime) //记录素数
                j++;
            prime = odd * 2 + 1;//待判断的奇数
        }
        return prime - 2;
    }

    关键算法实现

    int hash_mod(int key, int seed) {
        return key % seed;
    }

    二、乘法散列函数

    根据算法描述实现(应用时,slot大小是2^p,此函数无论如何映射,不会超出slot大小)

    int hash_mul(int key, int w, int p) {//w指关键字int是32位,p是映射后截取的最高有效位
        __int64 s = (sqrt(5) - 1) / 2 * pow(2, w);
        __int64 r0 = s*key % (__int64)pow(2, w);
        return r0 / pow(2, w - p);//高p位有效位
    }

     三、全域散列

    原理是:假设数组有n个元素,通过除法散列算法中的hash_mod_seed函数取得一个素数种子R。利用系统的随机函数并采用取模的方法生成一个长度位R的数组A,在进行全域哈希时,通过关键字key生成一个类似数组A的数组B。最后,散列的主要算法核心是求A[0]*B[0]+....+A[R-1]*B[R-1]的模(被除数是R*R)

    全域散列种子生成

    int *hash_seed(int *pKey, int R) {//pKey为NULL时表示初始种子,并作为hash_full函数中的v参数传入
        int *v = new int[R], key;
        memset(v, 0, R * sizeof(R));
        if (pKey == NULL) {
            srand(time(NULL));
            for (int i = 0; i < R; i++)
                v[i] = rand() % R;
        }
        else {
            key = *pKey;
            for (int i = 0; i < R && key; i++) {
                v[i] = key % R;
                key = key / R;
            }
        }
        return v;
    }

    对应的种子释放

    void hash_seed_free(int *v) {
        delete[] v;
    }

    关键函数,全域散列算法实现(应用时,slot大小是R*R)

    int hash_full(int key, int R, int *v) {//R是hash_mod_seed(n, 3),M是hash_mod_seed^2
        int slot = 0, M = R*R;
        int *numV = hash_seed(&key, R);
        for (int i = 0; i < R; i++)
            slot += numV[i] * v[i];
        hash_seed_free(numV);
        return slot % M;
    }

     为了方便大家测试代码,我还是补一个全域散列的打印函数

    void hash_full_print(int *A, int n) {
        int R = hash_mod_seed(n, 3);
        int *v = hash_seed(NULL, R), *hash = new int[R*R];
        memset(hash, 0, sizeof(int)*R*R);
        printf("hash seed=%d, slot size=%d
    ", R, R*R);
        for (int i = 0; i < n; i++) {
            int m = hash_full(A[i], R, v);
            while (hash[m]) //若数据存在,则循环后移一位,注意:当散列全满了,这里是一个死循环,实际应用中,请自行添加判断
                m = (m + 1) % (R*R);
            hash[m] = A[i];
            printf("[%d] is %d
    ", m, hash[m]);
        }
        hash_seed_free(v);
        delete[] hash;
    }

     结果补一张(很明显至少有一个碰撞,但是通过后移一位,解决了这个问题,总体来说效果很不错。最后完全散列很容易在此基础上修改,就不发代码了。各位创新下!!!思路就是在每个对应hash值处,生成一个R*R链表,看需求实现吧!!!):

    第二次运行就不存在碰撞了,上一个测试图吧。

    所有代码均经过测试,结果正确

  • 相关阅读:
    一手遮天 Android
    一手遮天 Android
    一手遮天 Android
    一手遮天 Android
    springcloud~feign POST form-url-encoded data
    java~和.net相通的DES-ECB加解密技术
    java的byte和C#的byte的不同之处
    spi~动态监控目录的jar实现热加载
    jboss( WildFly)上运行 springboot程序
    nc
  • 原文地址:https://www.cnblogs.com/dalgleish/p/8914417.html
Copyright © 2020-2023  润新知