原理參考《算法導論》
一、除法散列函數
根據描述實現算法,先取對應種子
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鏈表,看需求實現吧!!!):
第二次運行就不存在碰撞了,上一個測試圖吧。
所有代碼均經過測試,結果正確