ダブル配列の豆知識
- 2. 自己紹介
経緯
大学ではダブル配列の研究をしていた
ダブル配列は 10 回以上実装した
実績
Darts-clone
Darts(Double-ARray Trie System)のクローン
トライだったりトライじゃなかったり
2013/9/1 ダブル配列の豆知識 #DSIRNLP
2
- 3. あらすじ
トライとダブル配列
トライ = 抽象データ構造
ダブル配列 = トライの実装
ダブル配列の豆知識
実装の基本
更新時間の短縮
検索速度の向上
空間効率の向上
2013/9/1
3
ダブル配列の豆知識 #DSIRNLP
- 6. ダブル配列
配列によるトライの実装
BASE = 子ノード番号へのオフセット
子ノード番号 = オフセット + ラベル
CHECK = 親ノード番号
2013/9/1 ダブル配列の豆知識 #DSIRNLP
6
0 1 2 3 4 5 6 7 …
BASE 3 …
CHECK 1 1 …
オフセット + ラベル
- 8. 内部データ構造
理論的構成
Array<BASE>, Array<CHECK>
BASE, CHECK の参照で個別にキャッシュミス
現実的構成
Node = { BASE, CHECK }
Array<Node>
Node がキャッシュラインに収まる
キャッシュミスを半減できる
2013/9/1 ダブル配列の豆知識 #DSIRNLP
8
- 9. 演算方法
排他的論理和の方が実装しやすい
BASE = 子ノード番号へのオフセット
子ノード番号 = オフセット ⊕ ラベル
排他的論理和の利点
オーバーフローとアンダーフローの不安がない
ダブル配列をブロック単位で管理できる
ラベルが 8-bit のときブロックの大きさは 256
2013/9/1 ダブル配列の豆知識 #DSIRNLP
9
- 15. 隙間探しの効率化
隙間探しのポイント
常に先頭から探索するのは危険
理由:前の方に使いにくい隙間がたまる可能性
対処 1:前回の探索位置から再開する
対処 2:探索ノード数を制限する
静的な構築では後半だけの探索で十分
理由:衝突は完全に回避できる
対処:探索範囲を後半 N ノードのみにする
2013/9/1 ダブル配列の豆知識 #DSIRNLP
15
- 19. 前半部分文字列の検索
例
入力: “ANDROID”
出力: “A”, “AN”, “AND”, “ANDROID”
終端ラベル
終端ノードかどうか
終端ラベルを持つ子ノードが存在するかどうか
使い方
経路上の各ノードが終端ノードかどうかを確認する
2013/9/1 ダブル配列の豆知識 #DSIRNLP
19
- 22. TAIL のマージ
同じ文字列なら共有可能
更新のあるタスクでは難しい
同じ文字列を探すのに時間がかかる
探すためのデータ構造を使うと余計に大きくなる
後半部分列も共有可能
たとえば, “DROID” は “ANDROID” の後半が使える
おまけ
キャッシュミスを減らせる
2013/9/1 ダブル配列の豆知識 #DSIRNLP
22
- 23. CHECK のラベル化
親ノード番号の代わりにラベルを使う
基本的にノード番号は 32-bit でラベルは 8-bit
BASE を 24-bit にすれば Node を 32-bit にできる
更新のあるタスクでは難しい
衝突したときに親ノードがわからない
動かしやすい方を選択するのは難しい
BASE が重複するとおかしくなる
親ノードが複数になってしまう
2013/9/1 ダブル配列の豆知識 #DSIRNLP
23
- 24. 相対オフセットと拡張ビット
24-bit BASE で大規模なダブル配列を実現
BASE に拡張用のビットを加える
拡張時はオフセットを 256 倍にする
オフセットを相対値にする
拡張時に粒度が粗くなる問題への対処
拡張ビットによる分岐はなくせる
(Node >> 10) << ((Node & (1U << 9)) >> 6)
Darts-clone より抜粋(拡張ビットは 1U << 9)
2013/9/1 ダブル配列の豆知識 #DSIRNLP
24
- 25. グラフ化
同じ部分木なら共有可能
CHECK をラベル化して BASE を重複させる
トライ(木構造)ではなく DAWG(グラフ)になる
グラフを構築してからダブル配列を構築する
静的な構築であれば,意外に時間がかからない
Darts と Darts-clone の比較
http://code.google.com/p/darts-clone/wiki/Evaluation
2013/9/1 ダブル配列の豆知識 #DSIRNLP
25