關鍵字查找算法

現在有一批中等數量級(十萬級)的數據,格式如下:

ID                    NameInfo
0                  北京市人民政府

1                  國家安全局

2                 上海市人民政府

3                 八達嶺長城
.......               ................

現在要對此文件建立關鍵字索引。(例如:輸入 國家,可以快速的找到  國家安全局  輸入 人民 可以找到 北京市人民政府 和 上海市人民政府);
要求建立的索引結構所佔用的空間相對最少,但是關鍵字查找速度要很快。
現在考慮使用 Trie樹,但是Trie樹太浪費空間了。
===================================================================================

沒理解錯的話,應該是類似SQL查詢中like子句的功能吧。

1、搞個詞典庫,把每個關鍵字編號;
     40萬關鍵字+編號,按4個漢字組詞算,需要(4*2 + 4) * 40W = 4.8M空間。
搜索方式可以使用簡單的二分查找(此時可以省去編號佔用的空間);但考慮這個庫實際使用壓力較大,採用hash較好。
記得哪裏說過,hash數據量應該是存儲區大小的7x%;以後再靠加大存儲區去減少碰撞,效果已經不明顯了。
採用悲觀點的值,用8M空間吧。
如果內存緊張,可以不使用hash,改成按關鍵詞排序後二分查找;這樣查詢效率稍微低一些,但可以節約4.8M空間(hash本身額外佔用的3.2M,加上可以用關鍵詞本身的數組下標當作編號而節約的每詞4字節共1.6M的空間)。

2、把NameInfo切分,然後分析切分出的關鍵詞在不在詞典庫裏;在就把它記爲關鍵字。

類似這樣:
a、NameInfo 北京市人民政府
b、分詞,分解爲 北京 市 人民 政府 或 北京市 人民 政府
c、查詞典,確認 北京 人民 政府 爲關鍵字,且編號分別爲123456 345667 456667
(根據你的舉例,每條記錄大概有3~4個關鍵字;以後都按4個關鍵字算;這些關鍵詞僅用於建索引,不必實際存儲)



3、另開一個指針數組,數組下標對應關鍵字編號(這需要40W × 4 = 1.6M字節),指針指向包含對應關鍵字的記錄ID列表。
    記錄ID的列表可以採用動態分配或內存池。因爲有10W記錄,所以ID最少需要3字節;爲以後擴充計,以下按四字節計算。
假設每條記錄都有4個關鍵字,那麼將需要4倍於記錄數的節點空間(即需要4*4*10W=1.6M字節);如果用單鏈表實現,則還需要額外的4個字節保存next指針,即一共3.2M空間。
(用數組可以比鏈表減少一半空間消耗,但插入刪除比較慢)。

現在,內存中大概是這樣:

詞典區(8M):
[關鍵字1 編號1][關鍵字2 編號2][關鍵字3 編號3]……

索引區(1.6M+3.2M=4.8M)
關鍵字編號=數組下標;

    指針指向一個鏈表,鏈表保存對應的記錄ID(或指向此記錄的指針)

    記錄區(10W × 單條記錄大小,假設你的系統還允許佔用5M內存,則單條記錄不能大於50字節;考慮一個ID要4字節,要求NameInfo不能大於23個漢字)
記錄區可以是一個簡單數組,此時ID可省略。

    所有這些一共需要17.8M空間;若詞典改成排序數組,則只需13M空間。
注意記錄區ID字段按4字節計,但實際在記錄區可以直接用數組下標代替ID,可節約10W×4=0.4M空間;此外,關鍵字編號只需3字節,前面全部是按4字節算的,也可以壓縮一下。

    總之,根據自己需要裁剪後,上面這個方案可以壓縮到僅佔用11M字節甚至更少。

以下按最優性能方案計算

插入效率:
    需要若干次O(1)的hash查詢(看分詞算法的好壞了),以把分詞結果對應到關鍵字編碼上;然後建索引需要4次數組訪問和4次鏈表插入操作(複雜度都是O(1))。

查詢效率:
    需要一次O(1)的hash查詢,以把用戶輸入的關鍵詞對應到編碼上;然後拿這個編碼當下標訪問數組,取得查詢結果鏈表;再根據鏈表記載把所有合適記錄打印出來。

    總的來說,插入、查詢效率都是O(1);很容易擴充出刪除操作(好像沒這需求吧),且刪除效率也是O(1)。

    注意這個算法是個完全內存算法,它的響應速度是微秒甚至納秒級;所以不必擔心用二分法查找代替hash後會導致太大的性能損失。


    如果將其改成完全磁盤算法(此時當然就不需要擔心空間消耗了),即使考慮干擾因素,速度仍然可以保持在十幾到幾百毫秒的級別上。
——每秒顯示25幀,人眼看起來就是連續動畫,此時每幀就要滯留1000/25=40毫秒;200毫秒的延遲也不過是5個動畫幀而已,一般用戶感覺不到任何延遲。

    當然,如果有外存的話,考慮40萬關鍵字肯定不會用全、10W記錄也不一定每一條都會訪問到;那麼最好的做法還是搞個cache,做成半內存算法:這樣一方面不需要佔用太多內存,同時還可以在大部分情況下保持微秒級響應速度。
(當然,如果用外存,還是直接用一些輕量級數據庫更方便)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章