2012年6月26日火曜日

プロジェクトオイラー Problem22

「プロジェクト オイラー」の問題をpythonでやってみます。

出典:
Project Euler(日本語翻訳サイト)
参考:
サイエンスもの置き場 プロジェクトオイラーを遊び倒すガイド(初級編)

Problem 22
5000個以上の名前が書かれている46Kのテキストファイルnames.txt を用いる.
まずアルファベット順にソートせよ.
のち, 各名前についてアルファベットに値を割り振り, リスト中の出現順の数と掛け合わせることで, 名前のスコアを計算する.

たとえば, リストがアルファベット順にソートされているとすると,
COLINはリストの938番目にある.
またCOLINは3 + 15 + 12 + 9 + 14 = 53という値を持つ.
よってCOLINは938 × 53 = 49714というスコアを持つ.

ファイル中の全名前のスコアの合計を求めよ.


私の解答例は以下です。
-----

def f(sIn):
	#alphabet dictionary
	D = {}
	for i, s in enumerate(xrange(ord("A"), ord("Z")+1)):
		D[chr(s)] = i+1
	
	#read
	L0 = []
	s = open(sIn).read()
	for t in s.split(","):
		L0.append(t[1:-1])
	
	#sort
	L1 = sorted(L0)
	
	#score
	L2 = []
	for i, s in enumerate(L1):
		u = [D[t] for t in s]
		L2.append((i+1)*sum(u))
	
	return sum(L2)
	
import os, sys
sIn = os.path.join(os.path.dirname(sys.argv[0]), "names.txt")
print f(sIn)
-----

1.アルファベット辞書作成
・スコア計算で使用するアルファベットの記号と序数の対応辞書を作成します。
・for i, s in enumerate(xrange(ord("A"), ord("Z")+1)):
 アルファベットを全部手打ちするのは大変なので、ord関数で8ビット文字のバイト値(数値)にします。 
例)ord("A")=65
 xrange関数でAからZのバイト値(数値)の範囲で順にfor文で値を取れるようにします。
 Zまで含めるために終了条件はord("Z")+1になります。
 enumerate関数により、変数iに0から始まる序数を設定し、変数sに各アルファベットのバイト値を設定します。
・D[chr(s)] = i+1
 chr関数はord関数の逆関数です。先ほどのバイト値を8ビット文字に戻します。
 辞書Dには文字A~Zをキーにして、スコア値1~26を持つように設定します。
 序数iは0から始まるので、Aと1を対応づけるためにi+1をセットします。
 辞書Dは以下の通り。D={"A":1, "B":2, ・・・, "Z":26}


2.読み込み
・提示されたテキストファイルを読み込みデータ要素ごとにリストL0に貯めます。
 提示されたテキストファイルはカンマ区切り、ダブルクォーテーション挟み、改行なしのデータファイルです。
・s = open(sIn).read()
 ファイルを開いて、全部一気に読み込みます。
・for t in s.split(","):
 まず区切り文字のカンマで区切って、要素に分解します。
・L0.append(t[1:-1])
 t[1:-1]で、各要素の左端と右端の文字を外します。これで挟み文字のダブルクォーテーションがとれます。


3.ソート
・L1 = sorted(L0)
 sorted関数で並べ替えます。第2引数以降でいろいろな並び替えができるのですが、第1引数だけ指定して単純に昇順に並べます。


4.スコア計算
・アルファベットに値を割り振り, リスト中の出現順の数と掛け合わせスコア値とします。

・for i, s in enumerate(L1):
 enumerate関数でリストL1の要素を1つずつ取り出すと同時に、対応する0からの連番を付与します。
 iには0からの連番、sには要素が1つずつ設定されます。
・u = [D[t] for t in s]
 先ほど取り出した要素sは文字列なので、さらにfor文で1文字ずつに分解してループ変数tに取り出します。
 そしてtをキーにしてアルファベット辞書Dの値を参照して、"A"なら1のように変換した状態でリストにため、1要素分全体でリストuとします。
 例)s=COLINのとき、u=[3, 15, 12, 9, 14]
・L2.append((i+1)*sum(u))
 スコア値は、リストuの合計値とデータ要素序数の積なので、その通りにリストL2に貯めていきます。
 このときデータ要素序数の方は、0から始まっているので、+1して、1から始まる数に調整しておきます。
・return sum(L2)
 求めるスコア合計は、上記のリストL2の合計なので、sum関数でリストの全要素の合計値を計算し返します。


5.関数fの外で
・提示されたテキストファイルのフルパスを、関数fに渡します。
・sIn = os.path.join(os.path.dirname(sys.argv[0]), "names.txt")
・sys.arvgは、今実行中のシステムが持つ引数値アーギュメントバリューです。
 sys.argv[0]は、このソースが書かれたpythonソースファイルのフルパスです。
・os.path.dirnameで親フォルダ名を取り出し、
 os.path.joinでその親フォルダとnames.txtをパスとして結合しています。
・上記で、当ソースファイルと同じフォルダにあるnames.txtのフルパスが取得できました。これを関数fに渡しています。

解答はこのすぐ下の行です。文字の色を白にしてます。選択状態にすると見えます。
871198282


(追記)
・20120715
 ソースコード部分にSyntaxHighlighterを導入。

0 件のコメント:

コメントを投稿