2011-07-14 4 views
1

私は基本的にテキストファイル、から「多重グラフのデータを生成するプログラムを書いていますが、例えば、テキストファイルに書記素とその出現頻度との間のマッピング:書記素世代 - メモリの複雑さ対時間

aaaa : 0 
aaab : 0 
aaac : 0 
... 
thel : 10 
them : 250 
... 
zzzz : 0 

基本的な考え方は、マルチグラフデータに基づいて文字列を「スコア化」して、テキストファイルの言語とどれほど近いかをテストできることです。スコアリング機能は非常に高速でなければなりません。したがって、私は、n次元配列を使用してデータに直接アクセスすることを望んでいました。例えば:

data[n('t')][n('h')][n('e')][n('m')] 
N(チャー)のように文字正規化する関数である

- > 0、B - > 1、C - > 2などとにかく、ここで問題がある:26^nは非常に速く大きくなります!

  1. 104 B
  2. 3キロバイト
  3. 69キロバイト
  4. 2メガバイト
  5. 45メガバイト
  6. :私は要素ごとに4つのバイトを使用する場合、次のメモリは、nの異なる値に必要とされます1GB
  7. 30GB
  8. 778GB

したがって、n> 3のときにスタックのメモリが不足し、n> 6のときにほとんどのヒープがメモリ不足になるようです。理想的には、私はあらゆる合理的な長さのマルチグラフファイルを生成できるようにしたいと考えています。どのように私はこれを達成することができる任意のアイデア?

私は、配列の要素ごとに1バイト未満を使用する可能性について考えました。私は本当に 'a-z'とおそらくいくつかの特殊文字(スペース、句読点)を索引付けする必要があるので、おそらく5ビット(0〜31)で取り除くことができます。これは可能ですか?もし私ができるなら、私は潜在的に38%のメモリを節約するだろう。これが時間の複雑さにどのように影響すると思いますか?

1つのオプションは、配列ではなくハッシュ関数を使用することです。これは、常に0の頻度を持つ「qxzf」ではなく、実際に存在するキーにのみメモリを使用していることを意味します。メモリ要件は大幅に削減されますが、時間の複雑さ重大な影響を受ける。どう思いますか?

おそらく、私は何らかの種類のツリーデータ構造を使用できますか?グレープフエムはそのような表現に役立ちますが、やはり時間の複雑さは確かに打撃を与えます。私はそれが1ではなくデータにアクセスするための 'n'ステップをとると思います。

最後に、スコアリング機能のマルチスレッド化を検討しています。私はむしろ各スレッドのデータのコピーを割り当てないだろう。要素をロックするためにピーターソンのアルゴリズムと組み合わせて1つまたは2つを使用することは可能でしょうか?

ありがとうございます。

+1

辞書を使用してベンチマークを実行しましたか?それは遅くなるかもしれませんが、大規模なnの場合、スペースを節約すれば、可能なすべての文字列のスペースを事前に割り当てるよりも良いでしょう。 – BrandonAGr

+0

明日のベンチマークを試してみましょう。私はそれが数倍遅くなるだろうと思うだろう。各スコアリングで文字列の比較を行う必要があります。 – Chris

+2

私はブランドンです。最初に実世界の出発点を取得し、必要に応じて複合性を追加するのは簡単です。さて、私は、ハッシングが実行可能であるためには、一定時間の検索に十分近づいていると思います。 –

答えて

2

Trieは、タイムスペースのトレードオフを提供します。平文はtrieです。接頭辞 "iq"の場合)は、文字列内の次の文字(たとえば 'x')で索引付けされた子ポインタの配列を持ちますが、子ポインタ配列内に空白が無駄に残っていますが、その接頭辞に根ざす枝はありません(例えば "iqx")。他の試みは、スペースの量を減らすが、存在する子へのポインタのみを格納することによって時間の複雑さを増加させる(必ずしもそうではないが)が、子ポインタの数を対数時間で検索する必要がある。後者のタイプのいくつかの試みは、与えられた接頭辞に対するすべてのポインタを単一のノードに格納します。他のノード(例:ternary search tries)は複数のノードを使用します。

試行によるルックアップはおおよそO(n)ですが、nはかなり小さいので、実際のパフォーマンスはあなたの目的には十分に速くなる可能性があります。 nの文字を調べるには、nという用語(data[a1]...[an] == data + sum(i=1..n, ai * 256i-1))を使って多項式を評価する必要があります。多次元配列のアクセス自体はO(n)です。

仮想メモリであっても領域要件がまだ高すぎる場合は、B+ trees allowのように、ディスク上に多くの構造体を格納する必要があります。この場合、B +ツリーはハッシュテーブルの基礎となる実装を提供する。もちろん、これはかなりの打撃を招きますが、メモリ要件が一定のレベルに達すると避けられません。

私は配列の各次元に1バイト未満を使用する可能性について考えました。

潜在的な配列インデックスの数をこのように減らすことは完全に可能です。特殊なデータ構造を使用することに加えて、これを行うことができます。たとえば、これにより、トライ内のノードのファンアウトが減少し、ヌルポインタの数が減少します。

文字を配列キーにマップする機能が必要になりますが、これは時間の複雑さにわずかにしか追加されません。テーブルルックアップを使用すると、一定の時間が短くなり、スペースが少し増加します(〜256バイト)。

サンプルデータとテストする文字列を前処理して、無効な文字をフィルタアウト/マップする必要があります(大文字から小文字への変換など)。文字列。

最後に、スコアリング機能をマルチスレッド化することを検討しています。

ここでのゲインは、スコアリング関数の計算がグラフェン構造からの読み取り外でどれだけ費やされているかによって決まります。その時間外に少しの時間を費やすと、スレッドはほとんどの時間を待っていて、パフォーマンスの向上はほとんど見られません。 Amdahl's lawがここに適用されます。

コメントに基づいて、マルチスレッドスコアリング関数は読み取り専用アクセスのロックを必要としない場合があります。読み込み専用アクセスが構造自体を変更しない限り、構造を横切るすべての状態は完全にread関数内に含まれ、read関数が呼び出す関数(たとえばハッシュ関数)はスレッドセーフであり、構造全体が複数のスレッドが同時にツリーから読み取った場合、競合は発生しません。

ディスクバックアップアプローチ(B +ツリーなど)を使用する場合、最後の要件は保持されません。その場合、おそらく、スラッシングを防ぐためにディスクブロックを処理するコードの周りにロックする必要があります。

+0

私は、使用されるデータ構造が利用可能なメモリの量に依存するソリューションに傾いています。おそらく、nの値が小さい場合、n次元の配列を使用できます。中間値の場合はハッシュマップを使用でき、大きな値の場合はtrieを使用できます。 ハッシュマップまたはトライが読み込みの方が速いかどうかは少し不確実です(この場合はすべて問題あります)。私はベンチマークすべきことだと思いますか? – Chris

+1

これはベンチマークにとって素晴らしいことに思えます。 – outis

1

trieは、あなたのメソッド(n個の配列参照とn個のツリーノードトラバーサル)と同じくらい速く、1トンのスペースを節約する可能性があります。ハッシュも機能し、ルックアップで高速になるかもしれませんが、より多くのスペースが必要になります。