Unicode文字列の効率的なTrie実装


12

効率的な文字列トライの実装を探しています。ほとんどの場合、次のようなコードが見つかりました。

Javaでの参照実装(ウィキペディアごと)

これらの実装は、主に2つの理由で嫌いです。

  1. 256文字のASCII文字のみをサポートします。キリル文字などをカバーする必要があります。
  2. それらは非常にメモリ効率が悪いです。

各ノードには、256個の参照の配列が含まれます。これは、Javaの64ビットマシンでは4096バイトです。これらの各ノードは、それぞれ4096バイトの参照を持つ最大256個のサブノードを持つことができます。したがって、すべてのASCII 2文字列の完全なトライには1MBを少し超えるサイズが必要です。3つの文字列?ノード内の配列にのみ256MB。等々。

もちろん、トライに1600万の3文字列すべてを含めるつもりはないので、多くのスペースが無駄になっています。これらの配列のほとんどは、挿入されたキーの実際の数をはるかに超える容量があるため、単なるヌル参照です。また、Unicodeを追加すると、配列はさらに大きくなります(charの値はJavaの256ではなく64kです)。

文字列の効率的なトライを作成する希望はありますか?これらのタイプの実装に対するいくつかの改善を検討しました。

  • 参照の配列を使用する代わりに、プリミティブ整数型の配列を使用できます。これは、サイズが実際のノードの数に近いノードへの参照の配列にインデックスを付けます。
  • 深いツリーを犠牲にしてサイズ16のノード配列を可能にする4ビットの部分に文字列を分割できます。

回答:


2

このトライを何に使用していますか?保持する予定の単語の合計数と、構成文字のスパース性はどのくらいですか?そして最も重要なのは、トライが適切なのか(単語のリストへのプレフィックスの単純なマップに対して)ですか?

短い短い単語のセットが比較的少なく、文字セットがまばらであれば、中間テーブルとポインタをインデックスで置き換えるというアイデアが機能します。そうしないと、中間テーブルのスペースが不足する危険があります。そして、非常に小さな単語のセットを見ている場合を除き、それほど多くのスペースを節約することはありません。32ビットマシンでの参照では4バイト、参照では4バイトです。64ビットJVMで実行している場合、さらに節約できます。

文字を4ビットチャンクに分割するという考えは、予想されるすべての文字が非常に限られた範囲内にない限り、おそらく大した節約にはなりません(おそらく、一般的なUnicodeコーパスではなく、大文字のUS-ASCIIに限定された単語でもOKです) )。

スパース文字セットがある場合は、a HashMap<Character,Map<...>>が最適な実装である可能性があります。はい、各エントリははるかに大きくなりますが、多くのエントリがない場合は、全体的な勝利を得るでしょう。(サイドノートとして:トライスに関するWikipediaの記事が示している-おそらくまだ-ハッシュデータ構造に基づく例を示し、その選択の空間/時間のトレードオフを完全に無視することは常に面白いと思った)

最後に、トライを完全に避けたいかもしれません。人間の言語で通常の単語のコーパス(アクティブな使用中の単語10,000、単語4〜8文字)を見る場合は、おそらくHashMap<String,List<String>キーがプレフィックス全体であるほうがはるかに良いでしょう。


-参照は32ビットでは8バイト、64ビットマシンでは16バイトです-オートコンプリート機能用です-文字列内の文字の大部分はASCII範囲にありますが、いくつかの中央ヨーロッパ文字がスローされます。 256個よりも多くの文字が切り取られるためです。HashMap <String、List <String >>は、書き込みや使用が非常に簡単ではありますが、パフォーマンスが向上したり、高速になったり、メモリ消費が少なくなったりしません。ただし、HashMap <Character、Map>のアイデアは受け入れます。128を超える文字には問題ありません(私の場合、まれです-中国語のテキストには悪いでしょう)。
RokL

4

文字列をUTF8にエンコードする場合、標準の256分岐トライを使用でき、それでもUnicodeと互換性があります。

また、可能性のある128 ASCII文字(すべてUTF8で1バイトにエンコードする)のうち70文字程度しか文字が見つからないことに注意してください(未使用の制御文字の代わりに一般的なダイグラフを含めるなど) )


UTF8はそのように表現できることを知っています。ただし、これでもメモリ消費は解決されず、メモリ消費は依然として非常に高くなります。文字を基本的な256の範囲にスワップするには、かなりの切り替え文が必要になりますが、それだけの価値があるとは思いません。UTF-8に関する限り、これは実際に私が今熟考している問題です。Java StringはUTF-16文字を使用しますが、これは簡単に取得でき、バイト単位でエンコードできます。または、UTF-8に変換して使用できます。この時点で、UTF-16からUTF-8に変換するコストが高すぎるかどうかはわかりません。
RokL

ほとんどの場合、これを使用することを想定している言語は何ですか?すべてを最適化しようとすることは不可能である(または既に行われていた)ので、一般的なケース
ラチェットフリーク

1
これは、CESU-8がUTF-8よりも望ましい非常に少数のユースケースの1つです。ここでの大きな利点は、UTF-8コードポイントから対応するCESU-8コードポイントに到達するのが簡単なことです(必要な場合) 1-2 UTF-16コードポイントをデコードして、対応するUTF-8コードポイントを取得します)。
ヨアヒムザウアー

1
@ratchetfreak Java。質問はほとんどの言語に一般化できると思いますが。Cでは、ポインタをキャストbyte*して、ビット単位のトライで任意の型をエンコードできると思います。
RokL

@UMad入力文字列の言語(英語、フランス語、ドイツ語、...)を意味しました
ラチェットフリーク
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.