簡潔データ構造 国立情報学研究所 定兼 邦彦 目次 0. 基礎 1. ビットベクトル 2. 木構造1 3. 木構造2 4. 接尾辞配列 5. 接尾辞木 6. 動的なデータ構造 7. 配列 2 4. 接尾辞配列 3 文字列用の標準的データ構造 • 操作 – パタンの出現回数,場所 – 共通部分文字列,極大パタン – アラインメント • 代表的データ構造 – 接尾辞木 [1,2] – 接尾辞配列 [3] • サイズ:文字列 + O(n log n) bits – ヒトのDNA配列:30億文字 (750MB) – 接尾辞木:40GB 4 接尾辞 (suffix) • 文字列 T の先頭の何文字を除いたもの (n 種類) • T の任意の部分文字列は,ある接尾辞の接頭辞 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 いるかいないかいないかいるかいるいるいるか るかいないかいないかいるかいるいるいるか かいないかいないかいるかいるいるいるか いないかいないかいるかいるいるいるか ないかいないかいるかいるいるいるか いかいないかいるかいるいるいるか かいないかいるかいるいるいるか いないかいるかいるいるいるか ないかいるかいるいるいるか いかいるかいるいるいるか かいるかいるいるいるか いるかいるいるいるか るかいるいるいるか かいるいるいるか いるいるいるか るいるいるか いるいるか るいるか いるか るか か =T 5 接尾辞配列 [3] • 接尾辞のポインタを 辞書順にソートした配列 • サイズ n log n + n log |A| bits • パタン P の検索 O(|P| log n) time SA 6 10 4 8 15 17 19 1 12 21 3 7 14 11 5 9 16 18 20 2 13 いかいないかいるかいるいるいるか いかいるかいるいるいるか いないかいないかいるかいるいるいるか いないかいるかいるいるいるか いるいるいるか いるいるか いるか いるかいないかいないかいるかいるいるいるか いるかいるいるいるか か かいないかいないかいるかいるいるいるか かいないかいるかいるいるいるか かいるいるいるか かいるかいるいるいるか ないかいないかいるかいるいるいるか ないかいるかいるいるいるか るいるいるか るいるか るか るかいないかいないかいるかいるいるいるか るかいるいるいるか 6 圧縮接尾辞配列 (CSA) [4,5] • SA の代わりに [i] = SA-1[SA[i]+1] を格納 • サイズ: O(n log |A|) bits i SA • パタン P の検索: O(|P| log n) time 0 1 7 $ 5 2 1 ababac$ 6 3 3 abac$ 7 4 5 ac$ 3 5 2 babac$ 4 6 4 bac$ 1 7 6 c$ 7 の計算法 1. 接尾辞配列 SA を作る 2. i を (T[SA[i]-1], i) の順に基数ソート 1. T中の各文字の頻度を数える i SA 0 1 7$ 2. i=1,2,...,n に対し, c=T[SA[i]-1] を求める 5 2 1 ababac$ 6 3 3 abac$ 3. で c に対応する範囲に 7 4 5 ac$ i を書く 3 5 2 babac$ 4 6 4 bac$ 1 7 6 c$ 8 なぜ圧縮できるのか • 接尾辞は辞書順に 格納される • 先頭の1文字を消し ても辞書順は同じ SA 12 14 15 16 17 18 19 20 21 0 3 4 5 9 1 2 6 7 10 11 13 6 10 4 8 15 17 19 1 12 21 3 7 14 11 5 9 16 18 20 2 13 いかいないかいるかいるいるいるか いかいるかいるいるいるか いないかいないかいるかいるいるいるか いないかいるかいるいるいるか いるいるいるか いるいるか いるか いるかいないかいないかいるかいるいるいるか いるかいるいるいるか か かいないかいないかいるかいるいるいるか かいないかいるかいるいるいるか かいるいるいるか かいるかいるいるいるか ないかいないかいるかいるいるいるか ないかいるかいるいるいるか るいるいるか るいるか るか るかいないかいないかいるかいるいるいるか るかいるいるいるか 9 CSA の性質 • i < j のとき T[SA[i]] T[SA[j]] • i < j かつ T[SA[i]] = T[SA[j]] のとき 0 [i] < [j] 証明:T[SA[i]] = T[SA[j]] のとき, この 5 6 接尾辞の辞書順は2文字目以降で 7 決まる. 3 i < j より T[SA[i]+1..n] < T[SA[j]+1..n] 4 SA[i’] = SA[i]+1, SA[j’] = SA[j]+1 とす 1 ると, i’ < j’ つまり i’ =SA-1[SA[i]+1]=[i]<[j]=j’ i SA 1 2 3 4 5 6 7 7$ 1 ababac$ 3 abac$ 5 ac$ 2 babac$ 4 bac$ 6 c$ 10 の符号化法 • ’[i] = T[SA[i]] n + [i] を格納 – [i] = ’[i] mod n – T[SA[i]] = ’[i] div n • ’[i] (i = 1,2,...,n) は単調増加列になる – n(2+log ) $: 2 a: 5, 8, 9 c: 3, 4 g: 1, 6, 7 $: 000010 a: 010101, 011000, 011001 c: 100011, 100100 g: 110001, 110110, 110111 11 ’の符号化法 • ’[i] の2進表現の上位 log n ビット – 直前の値からの差分を1進数で符号化 – 最大 2n ビット (1の数 = n,0の数 n) • ’[i] の下位 log ビットはそのまま格納 – n log bits $: 2 a: 5, 8, 9 c: 3, 4 g: 1, 6, 7 $: 000010 a: 010101, 011000, 011001 c: 100011, 100100 g: 110001, 110110, 110111 1, 000001, 01, 1, 001, 01, 0001, 01, 1 10, 01, 00, 01, 11, 00, 01, 10, 11 12 ’の復号 • 上位桁: x = select(H,i) i • 下位桁: y = L[i] • ’[i] = x + y • 時間計算量: O(1) • 領域計算量: n(2+log ) + O(n log log n/log n) $: 2 a: 5, 8, 9 c: 3, 4 g: 1, 6, 7 H: 1, 000001, 01, 1, 001, 01, 0001, 01, 1 L: 10, 01, 00, 01, 11, 00, 01, 10, 11 13 の圧縮 • [i] を T[SA[i]] で分割 S c i | T [SA[i]] c, nc S (c) • 各 S(c) を符号化: nc 2 log n bits n c • 全体で n c nc 2 log n nH 0 3 c 1 H 0 pc log pc c A nc ( pc :文字 c の出現確率) n • H0 log (等号は p1 = p2 = … のとき) 1 1 H 0 pc log log pc log pc pc c A c A 14 要素 SA[i]のアクセス方法 • i が log n の倍数のときに SA[i] を格納 • k = 0; w = log n; • while (i % w != 0) – i = [i]; k++; SA2 • return SA2[i / w] - k; 08 13 24 アクセス時間: 平均 O(log n) 時間 n=8 w=3 i SA 0 8 0 1 7$ 5 2 1 ababac$ 6 3 3 abac$ 7 4 5 ac$ 3 5 2 babac$ 4 6 4 bac$ 1 7 6 c$ 15 1 [i] SA [SA[i] 1] • B[i]=1 SA[i] が log n の倍数 • SA[i]が log n の倍数のものを SA2 に格納 10 1 B 1 T E SA 8 8 2 0 B 14 9 11 13 15 3 4 5 6 0 0 1 1 D E B D 5 2 12 16 1 6 7 8 0 0 D A 7 15 7 12 14 16 2 3 4 5 9 10 11 12 13 14 15 16 0 0 0 0 0 1 0 0 D D E B E B D C 6 9 3 10 13 4 1 11 SA2 2 3 4 1 k = 0; w = log n; while (B[i] != 0) i = [i]; k++; return SA2[rank (B,i)]w k; B : n+o(n) ビット アクセス時間: O(log n) 時間 16 の階層的表現 • レベル i では – T 中の連続する 2i 文字を1つの文字とみなす – 文字列のエントロピーは増えない B 1 1 T E B SA 8 14 0 D 5 1 1 1 E B D 2 12 16 0 0 D A 7 15 1 D 6 0 D 9 0 1 0 E B E 3 10 13 1 B 4 0 0 D C 1 11 BD EB DD AD DE BE BD C$ SA1 4 7 1 6 8 3 5 2 BDEB DDAD DEBE BDC$ 2 3 4 1 SA2 17 データ構造のサイズ レベル数が 1/ のとき • : 1/ n(3+H0) bits • SA1/ : n/log n log n = n bits • B: n + n/2 + n/4 + ... 2n bits n O H 1 合計: 0 bits SA[i] の計算: O 1 log n 時間 18 部分文字列の検索 二分探索時に実際のSAの値は必要ない T E 1 10 B 2 8 r 1 2 D 1 1 SA 8 14 A B D D C 1 2 C A B D E B D 3 4 5 6 9 11 13 15 D 7 1 2 0 5 B D D 3 C 4 4 1 0 7 15 D D A C 2 2 3 0 0 1 2 12 16 B B C D E E 4 5 D E A 8 6 D D E B E B D C 9 10 11 12 13 14 15 16 7 12 14 16 2 3 4 5 4 0 6 D D A 4 0 9 D D E 4 4 5 0 0 1 3 10 13 D D E E E B B B D D E C 5 0 4 E B D D 5 5 0 0 1 11 E E B B D E E 19 後方検索 C[$]=[1,1] C[a]=[2,4] C[b]=[5,6] C[c]=[7,7] P=P[1..p] の検索 l 1; r n for (k = p; k >=1; k--) l arg min [ j ] l jC [ P[ k ]] r arg max [ j ] r jC [ P[ k ]] O(p log n) time 0 5 6 7 3 4 1 i SA 1 2 3 4 5 6 7 7$ 1 ababac$ 3 abac$ 5 ac$ 2 babac$ 4 bac$ 6 c$ 20 l arg min [ j ] l jC [ P[ k ]] r arg max [ j ] r jC [ P[ k ]] • の値で二分探索: O(log n) time • P の検索に O(|P| log n) time 21 テキストの部分的な復元 T[9..13] = DDEBEを復元する場合 1. i=SA-1[9]=10 を求める 2. 辞書順で i 番目の接尾辞の先頭の文字を求める 1 2 3 3. i=10からをたどる C A B C 1 T E A 10 2 B B 8 1 2 SA 8 14 3 D B 4 E B 5 B B 6 D C 9 11 13 15 3 4 5 6 5 2 12 16 7 D D 4 D 5 E 8 A D 9 10 11 12 13 14 15 16 D D E B E B D C D D D D E E E E 1 6 7 8 7 15 7 12 14 16 2 3 4 5 9 10 11 12 13 14 15 16 6 9 3 10 13 4 1 11 22 圧縮接尾辞配列の機能 • • • • lookup(i): SA[i] を返す (O(log n) 時間) inverse(i): SA-1[i] を返す (O(log n) 時間) [i]: SA-1[SA[i]+1] を返す(O(1) 時間) substring(i,l): T[SA[i]..SA[i]+l-1]を返す – O(l) 時間 – (i からT[SA[i] は長さ n のベクトルのrankで求まる) 23 CSAの問題点 • サイズが n(H0(S)+O(1)) bits • nHk(S)+o(n) にしたい 24 ブロックソート圧縮法 • • • • Burrows, Wheeler 1994 [6] gzipよりも高圧縮率, PPM [7] に迫る 圧縮がPPMよりも高速 伸張はさらに高速 – データの配布に適する 25 ブロックソート圧縮法 ababac$ 接尾辞ソート BW変換 c$bbaaa MTF変換 3441411 ハフマン符号 算術符号 δ符号 011 1 1 2 0 10 3 0 11 4 00 100 5 00 101 00100 00100 1 00100 1 1 26 接尾辞配列 T = acagcagg$ 接尾辞配列 SA 1 2 3 4 5 6 7 8 9 acagcagg$ cagcagg$ agcagg$ gcagg$ cagg$ agg$ gg$ g$ $ 辞書順に ソート 9 1 3 6 2 5 8 4 7 $ acagcagg$ agcagg$ agg$ cagcagg$ cagg$ g$ gcagg$ gg$ 27 BW変換 T = acagcagg$ • BW[i] = T[SA[i]1] • ソートした各接尾辞の1つ 前の文字を並べたもの • T の並び替えになる • BWから T を復元可能 • BWは圧縮しやすい BW = g$ccaggaa SA 9 1 3 6 2 5 8 4 7 g$ $ acagcagg c agcagg$ c agg$ a cagcagg$ g cagg$ g g$ a gcagg$ a gg$ 28 逆BW変換 • T は BW から復元できる • SA も復元できる [8] g $ c c a g g a a $ acagcagg$ agcagg$ agg$ cagcagg$ cagg$ g$ gcagg$ gg$ 29 FM-index [9] BWだけでパタン検索が可能 1 2 3 4 5 6 7 8 9 g $ c c a g g a a $ a a a c c g g g P=P[1, p] を検索 c = P[p] l = C[c]+1, r = C[c+1] while (--p 0) { c = P[p] l = C[c]+rankc(BW,l1)+1 r = C[c]+rankc(BW,r) } [l,r] は P の辞書順 C $: 0 a: 1 c: 4 g: 6 30 LF mapping LF [i] SA1[ SA[i] 1] C[c] rank c ( BW , i) LF[5]=C[a]+ranka(BW,5) =1+1 =2 SA[5]=2 SA[2]=2-1 1つ前の接尾辞の辞書順を表す C$abc 0146 (c BW [i]) 1 2 3 4 5 6 7 8 9 g $ c c a g g a a $ a a a c c g g g 31 • BW をウェーブレット木で格納すれば,rankを O(log /log log n) 時間で計算できる • パタン検索が O(|P| log /log log n) 時間 • BWのサイズ: nH0(BW) + O(n log /log log n) bits • lookup/inverseの索引を d 個おきにすると – サイズ: O(n log n/d) bits – 時間: O(d log /log log n) • サイズを o(n) にするために d = log1+ n とする 32 文字列の高次圧縮 • BW変換後の文字列では,同じ文脈を持つ文字 が集まっている 文脈 = $ g $ • 各文脈ごとに H0 まで圧縮 $ acagcagg ⇒全体では Hk を達成 文脈 = a c agcagg$ c agg$ a cagcagg$ 文脈 = c g cagg$ g g$ 文脈 = g a gcagg$ a gg$ 33 まとめ: FM-index • • • • • = polylog(n) とする 索引サイズ: nHk(S) + o(n) bits パタンの検索: O(|P|) time lookup/inverse: O(log1+ n) time 長さ l の部分文字列の復元: O(l + log1+ n) time 34 CSAとFM-indexの関係 • 両者は同じものを表している 1 [i ] SA [ SA[i ] 1] LF [i ] SA1[ SA[i ] 1] [ LF [i ]] LF [[i]] i • 片方でもう一方を模倣できる $: 010000000 (2) a: 000010011 (5, 8, 9) c: 001100000 (3, 4) g: 100001100 (1, 6, 7) BW: g$ccaggaa 35 • • • • • は BW 上のselectで求まる LF, BW は 回の の後方探索で求まる t: を求める時間 tLF: LF を求める時間 ts: BW でのselectを求める時間 t t s O(log ) t LF O log n 36 参考文献 [1] P. Weiner. Linear Pattern Matching Algorithms. In Proceedings of the 14th IEEE Symposium on Switching and Automata Theory, pages 1– 11, 1973. [2] E. M. McCreight. A Space-economical Suffix Tree Construction Algorithm. Journal of the ACM, 23(12):262–272, 1976. [3] Udi Manber, Gene Myers. Suffix arrays: a new method for on-line string searches, Proc. SODA, 1990. [4] R. Grossi and J. S. Vitter. Compressed Suffix Arrays and Suffix Trees with Applications to Text Indexing and String Matching. SIAM Journal on Computing, 35(2):378–407, 2005. [5] Kunihiko Sadakane: New text indexing functionalities of the compressed suffix arrays. J. Algorithms 48(2): 294-313 (2003). [6] M. Burrows, D. Wheeler. A block sorting lossless data compression algorithm, Technical Report 124, Digital Equipment Corporation, 37 1994. [7] John G. Cleary, Ian H. Witten: A comparison of enumerative and adaptive codes. IEEE Transactions on Information Theory 30(2): 306315 (1984) [8] Kunihiko Sadakane: A Modified Burrows-Wheeler Transformation for Case-Insensitive Search with Application to Suffix Array Compression. Data Compression Conference 1999: 548 [9] P. Ferragina and G. Manzini. Indexing compressed texts. Journal of the ACM, 52(4):552–581, 2005. 38
© Copyright 2024 ExpyDoc