1. HOME
  2. ブログ
  3. IT技術
  4. 【前編】ダブルアレイで高速でコンパクトな辞書を実装する〜準備〜

【前編】ダブルアレイで高速でコンパクトな辞書を実装する〜準備〜

ダブルアレイを使って辞書を実装してみる

ダブルアレイは、トライ木を表現することができるデータ構造で、コンパクトで高速に辞書検索が行えるという特徴を持っています。

小さなサイズで、高速に辞書検索を行うことができるという特徴を持っています。

有名な形態素解析エンジンである「mecab」でも用いられています。

2本の配列を用いることでトライ木を表現

上の図のように、2本の配列を用いることでトライ木を表現します。

今回は、このダブルアレイについて解説をし、実際に辞書を実装、コードの解説も行っていきたいと思います!

ダブルアレイとトリプルアレイ

以前の記事では、トリプルアレイについて紹介をしました。

ダブルアレイは、このトリプルアレイを元に、配列を一本取り除くことで、検索速度は(ほぼ)そのままで、よりコンパクトにトライ木を格納することができます。

※ダブルアレイとトリプルアレイの関係について、詳しくはダブルアレイの元論文である『An Efficient Digital Search Algorithm by Using a Double-Array Structure(JI-Aoe.1989)』に書かれています

ダブルアレイも、トリプルアレイと元となるアイデアは同じなので、トリプルアレイが理解できていればダブルアレイの理解も簡単です。

今回は、トリプルアレイの仕組みが理解できているという前提で、ダブルアレイについて解説していきます。

トリプルアレイについて自信のない方は、以前の記事を参考にしてください。

トリプルアレイ記事

トリプルアレイの復習

ダブルアレイの元となるトリプルアレイについて、少しだけ確認をしておきます。

トリプルアレイは、以下の図のように構築されるもので、3本の配列でトライ木を表現します。

3本の配列でトライ木を表現

3本の配列には「NEXT」「CHECK」「OFFSET」という名前がつけられており、それぞれ以下のような役割を担っています。

NEXT : 次に遷移するノードを格納する
CHECK : 正しい遷移であることを確認するために遷移元(親ノード)のidを格納する
OFFSET : 各ノードのずらし幅を格納する

このうち、「NEXT」と「CHECK」は同じ大きさとなります。

「OFFSET」だけ異なり、ノード数分の大きさとなります。

ダブルアレイ構築方法

それでは、ダブルアレイの構築方法について解説していきます。

今回は以前解説した、「トリプルアレイ」を利用して構築していきます。

トリプルアレイについては、以前の記事を参照してください。

トリプルアレイを2本の配列で表現するために、OFFSETを削除することを考えます。

そのためにまず、OFFSETが遷移の際にどんな役割をしているかを見てみましょう。

OFFSETの役割

この図から、OFFSETは「ノードのずらし幅を得るため」に使われていることがわかります。

このため、もしOFFSETを用いずにNEXTから直接ずらし幅が得ることができれば、OFFSETは不要となります。

NEXTから直接ずらし幅を得る」というのは実はとても簡単で、NEXTにノードidではなくそのノードのずらし幅を直接格納しておけばよいのです。

このノードidの代わりにずらし幅を格納されたNEXTは、BASE配列とよばれます。

ここまでで、OFFSETを削除することができました。

CHECK配列について

しかし、これによりノードidの情報が失われてしまったので、ノードidによって正しい遷移であることを確認していたCHECK配列の方に問題がおきてしまいます。

これもさほど難しくなく、以下の図のようにCHECK配列にノードidの代わりにBASE配列上での遷移元のindexを保存しておけばよいです。

これは元のCHECK配列の要素を、NEXT配列のおなじ要素があるindexに置き換えることでできます。

ここまでで、ダブルアレイは完成です。

遷移が可能かの確認

実際に、遷移が可能かどうかを確認しましよう。

単語idが「0」である「a」と単語idが「2」である「c”」いう単語が順に入力された場合の遷移を考えます。

最初は、ルートノードにいます。

(1) ルートノードのずらし幅は0ですので、index = 0+(aの単語id) = 0+0 = 0

(2) CHECK[0]をみると、CHECK[0] = -1です。
-1はルートノードを表すことにしているので、正しく遷移できています。

(3) BASE[0]をみます。
BASE[0] = -1 ですので、次のずらし幅は-1ということがわかります。

ここまでで、「a」の入力の処理が終わり、現時点でindexは「0」となります。

「c」の入力の処理

続いて、「c」の入力の処理に移ります。

(4) index = -1 + (cの単語id) = -1 + 2 = 1
CHECK[1]をみると、CHECK[1] = 0です。

(5) 前のindexは0だったので、正しく遷移できています。
cの次は入力が無いのでindex = 1の位置で終了です。

以上でBASEとCHECK配列で、ダブルアレイの構築は完了です。

ダブルアレイの実装(準備)

それでは、実際に、ダブルアレイを実装し、辞書システムを作っていきましょう!

言語は、Pythonを用います。

ダブルアレイの実装にはいくつか方法がありますが、今回は「トリプルアレイが構築された状態から、ダブルアレイに変換する」という方法で実装します。

要件

要件は以下です。

トリプルアレイのときと同様です。

  1. 単語とその読み方のペアを登録する
  2. 単語を入力することで読み方を出力する
  3. 単語に使える文字はasciiのみ

単語とその読み方のペアとして、今回も前回同様以下のデータを用います。

単語読み方
aエー
abエービー
acエーシー
abcエービーシー
bcビーシー

まずは、準備段階として、構造体から作っていきます。

今回は、 DoubleArray というクラスを作成します。

なお、ダブルアレイでは以前の記事で作成した、TripleArrayクラスを用いています。

DoubleArrayクラスは、BASECHECKitemsという3つの変数を持ちます。

BASEは、トリプルアレイのオフセットと同じ長さとなるので、TripleArrayのOFFSETSと同じ長さで初期化します。

CHECK、itemsは、トリプルアレイのCHECKと同じ長さで初期化します。

__init__() 関数の最後の行で、後に説明する make_double_array() を呼び出し、トリプルアレイを元にダブルアレイを構築します。

後半へ続く

以上で、ダブルアレイの解説と準備が終わりました。

後半では、完成に向けて、実装していきたいと思います。

後半へ続きます!

ライトコードよりお知らせ

にゃんこ師匠にゃんこ師匠
システム開発のご相談やご依頼はこちら
ミツオカミツオカ
ライトコードの採用募集はこちら
にゃんこ師匠にゃんこ師匠
社長と一杯飲みながらお話してみたい方はこちら
ミツオカミツオカ
フリーランスエンジニア様の募集はこちら
にゃんこ師匠にゃんこ師匠
その他、お問い合わせはこちら
ミツオカミツオカ
   
お気軽にお問い合わせください!せっかくなので、別の記事もぜひ読んでいって下さいね!

一緒に働いてくれる仲間を募集しております!

ライトコードでは、仲間を募集しております!

当社のモットーは「好きなことを仕事にするエンジニア集団」「エンジニアによるエンジニアのための会社」。エンジニアであるあなたの「やってみたいこと」を全力で応援する会社です。

また、ライトコードは現在、急成長中!だからこそ、あなたにお任せしたいやりがいのあるお仕事は沢山あります。「コアメンバー」として活躍してくれる、あなたからのご応募をお待ちしております!

なお、ご応募の前に、「話しだけ聞いてみたい」「社内の雰囲気を知りたい」という方はこちらをご覧ください。

ライトコードでは一緒に働いていただける方を募集しております!

採用情報はこちら

書いた人はこんな人

ライトコードメディア編集部
ライトコードメディア編集部
「好きなことを仕事にするエンジニア集団」の(株)ライトコードのメディア編集部が書いている記事です。

関連記事