メモリに入りきらないデータを扱う

タイトルのように、例えばWebデータを扱ってそこに出現する単語の頻度をカウントするような場合、どうするのが一番手軽で楽なんでしょうというお話。

勿論、多分ほとんどの人は知っていて、常識なのかもしれませんが、僕は最近気づきました。
まず、対象となる単語(研究によって異なるでしょう)だけを行単位で全部書き出します。

aaa
bbb
ccc

みたいに。
この時データ量があまりに多い場合複数ファイルに書き出した方が楽かもしれません。

その後は、単一ファイルの場合、

sort -S 500M | uniq -c | sort -nr -S 500M

で終わりです。

複数ファイルの場合は、全てのファイルをソートした後に、

sort -m -S 500M *.sort(ソート済みファイル) > merge.txt
uniq -c merge.txt > count.txt
sort -nr -S 500M count.txt > result.txt

だけでした。
sort の-Sオプションはこれだけのメモリに収めてくださいということです。
-mオプションでマージしながらソートしてくれます。
ちなみにマージソートの場合は、メモリをあまり使わなくてもファイルを上手く利用してソートが可能です。

そんなこんなで最近知ったトリビア
クイックソートは実はマージソートよりも遅いです。
だから、C言語の標準ライブラリにあるqsortは内部でマージソートを利用しています。
(以前はクイックソート


ちなみに計算量はどちらもO(nlogn)になります。

いろいろ考えて、自分でマージソートを実装するのが面倒だ…と思っていたときにUNIXコマンドにあることを思い出した俺は…orz



2007/07/20 追記:
Linuxなどで上記のコマンドを行った場合、LANGやLC_ALLの言語環境に影響を受けるようです。
(詳しくは調べていません。ただこの部分ではまりました。)
EUC-JPのテキストをja_JP.UTF-8の環境で行うと時間が通常の10倍以上かかり、おかしな結果になりました。
上手くいかない場合は、LANGUAGE=C(これはメッセージなので関係ないと思うけど)やLC_ALL=C、LANG=Cとして再度試してみてください。
おそらく、


LC_COLLATE 並べかえ(ソート)の順番を設定する 
もしくは

LC_CTYPE 文字定義、大文字、小文字など。これらは toupper、tolower、islower、isdigit のような関数で用いられる。
この辺が原因。LC_ALLで上書きされます。