常見的哈希函數


原理參考《算法導論》

一、除法散列函數

 

根據描述實現算法,先取對應種子

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\n", 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\n", m, hash[m]);
    }
    hash_seed_free(v);
    delete[] hash;
}

 結果補一張(很明顯至少有一個碰撞,但是通過后移一位,解決了這個問題,總體來說效果很不錯。最后完全散列很容易在此基礎上修改,就不發代碼了。各位創新下!!!思路就是在每個對應hash值處,生成一個R*R鏈表,看需求實現吧!!!):

第二次運行就不存在碰撞了,上一個測試圖吧。

所有代碼均經過測試,結果正確


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM