約6万語を含む英語のスペル辞書からアナグラムを探す

約6万語を含む英語のスペル辞書を使って、アナグラムとなっている単語をリストアップする。「珠玉のプログラミング」にある手法で、ソートした文字列で比較せよっていう話。それだけの話だから、すぐに試せるとか思ってやってみたけど、実際にやってみると結構あちこちでつまづいて20分ぐらいかかった。デフォルトでUTF-8を想定している外部文字エンコーディングにたいして、ISO-8859が来ると文字列マッチングのような処理でinvalidだと怒られて落ちる、とか。

入力ファイル(/usr/share/myspell/dicts/en_US.dic)は、

Abuja
abundance/SM
abundant/Y
abused/E
abuse/GVZDSRB
abuser/M
abuses/E
abusing/E
abusiveness/SM
abusive/YP
abut/LS
abutment/SM
abutted
abutter/MS
 :
 :

という感じ。何やら文法情報のようなメタ情報も入ってる。

アクセント記号のついた外来語やアポストロフィのついた単語は無視。

FILE = "/usr/share/myspell/dicts/en_US.dic"

list = {}

File.open(FILE, "r:iso-8859-1") do |f|
  while line = f.gets
    next if line.nil?
    word = line.chomp.downcase
    word.gsub!(/\/.*/,"")
    next unless word =~ /^[a-z]+$/

    sword = word.split(//).sort.join      # world --> dlorw

    if list[sword] && list[sword] & [word] == [] then
      list[sword] << word
    else
      list[sword] = [word]
    end
  end
end

max = 0 
list.each do |key,val|
  max = val.size > max ? val.size : max
  if val.size >= 2 then
    print val, "\n"
  end
end

print "words that have the max number of anagrams\n"
list.each do |key,val|
  if val.size == max then
    print val, "\n"
  end
end

結果は、

["alpert", "plater"]
["alpine", "nepali"]
["alp", "lap", "pal"]
["alps", "laps", "slap"]
["alric", "caril", "clair", "clari"]
["alston", "salton"]
["altai", "latia", "talia"]
["altair", "atrial", "lariat"]
["altitude", "latitude"]
["alton", "talon", "tonal"]
["altruism", "muralist"]
:
:

という感じ。a、e、mあたりががからむと比較的アナグラムになりやすいかしら。

["amass", "assam"]
["amber", "bream"]
["amble", "blame", "mabel", "mable", "melba"]
["ambler", "blamer", "marble", "ramble"]
["ambur", "burma", "rumba", "umbra"]
["amelie", "emelia"]
["amelina", "laminae", "malanie", "melania"]

アナグラムの最大数を調べたら8個。

["arin", "arni", "iran", "nair", "nari", "rain", "rani", "rina"]
["earth", "ertha", "harte", "hater", "heart", "herta", "retha", "rheta"]

しかし、かなり大きな英和辞典で引いても、rhetaとかerthaとか出てないし。rhetaはファーストネーム、erthaも名前で、ナチュラル系商品販売のブランド名とかだったりもするらしい。