完美hash函數

之前的學習了Hash表的基礎知識(數據結構與算法(Hash表)), 知道了Hash表的核心就是Hash函數,今天來學習完美hash函數。

一、什麼是完美hash函數?

如果存在函數h(x)將集合U映射到集合S並且沒有碰撞, 我們就可以說h(x)是集合U到集合S的完美hash函數。

二、如何實現一個完美hash函數

實現的思路不復雜, 但是有一個前提:在構建hash表之前, 我們要先獲得所有可能的key。然用之前的方法(好像有些地方叫除留餘數法)進行hash分組, 然後用同樣的方法將落在不同槽的數據再做一次映射, 確保這次映射不再有碰撞。

 三、c++代碼實例實現完美hash函數

class Hash {
public:
    Hash(std::list<int> keys)
        :size_(keys.size())
        ,region_sizes(keys.size())
        ,offset_table(keys.size())
        ,a2_array(keys.size()){

        // 一級hash
        int sum = 10 * size_;
        int max_sum = 5 * size_;
        while (sum > max_sum) { // 獲得分佈相對均勻的hash
          a = rnd.rand();
          sum = 0;

          for (auto& v : region_sizes) {
              v = 0;
          }

          for (auto key : keys) {
              int h = key * a % MAXP % size_;
              ++region_sizes[h];
              sum += region_sizes[h] * region_sizes[h];
          }
        }

        // 二級hash
        sum *= 2;
        hash_table.resize(sum, MAXP);

        for (auto& v : region_sizes) {
            v *= 2;
        }
        // 計數個區間起始位置
        offset_table[0] = 0;
        for (int i = 1; i < size_; ++i) {
            offset_table[i] = offset_table[i-1] + region_sizes[i-1];
        }
        // 生成二級hash映射係數
        for (auto& v : a2_array) {
            v = rnd.rand();
        }

        // 檢查是否有碰撞, 如果有碰撞,重新生成映射係數,直到沒有碰撞爲止
        bool isCollision = true;
        while (isCollision) {
            isCollision = false;

            for (auto key : keys) {
                int h1 = a * key % MAXP % size_;
                int h2 = a2_array[h1] * key % MAXP % region_sizes[h1] + offset_table[h1];

                if (hash_table[h2] == MAXP
                    || hash_table[h2] == key) {
                    hash_table[h2] = key;
                }
                else {
                    isCollision = true;
                    a2_array[h1] = 0; // 係數需要重新生成
                }
            }

            if (isCollision) {
                for (int i = 0; i < size_; ++i) {
                    if (a2_array[i] == 0) {
                        a2_array[i] = rnd.rand();

                        // 清除對應區的hash表
                        for (int j = offset_table[i]; j < offset_table[i] + region_sizes[i]; ++j) {
                            hash_table[j] = MAXP;
                        }
                    }
                }

            }
        }
    }

    int hash(int key) {
        int h1 = a * key % MAXP % size_;
        return a2_array[h1] * key % MAXP % region_sizes[h1] + offset_table[h1];
    }

private:
    int a; // 原始映射係數
    int size_; // 所以key的數量
    std::vector<int> region_sizes; // 一級hash計數及保存最終hash表的各區間長度
    std::vector<int> offset_table; // 各區間的起始位置
    std::vector<int> a2_array; // 各區間的映射係數
    std::vector<int> hash_table; // 最終hash表
    HashRand rnd; // 用於生成在區間[1,46337)的隨機數
};

這個實現的內存有些浪費, 有待改進。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章