SCons入門(1)
SConsとは?
Python製のMake代替ツール。
中でPythonコードを書けるので、複雑なMake処理を簡単に書ける。
C言語の依存関係などを自動に調べる機能などがあるらしいが、使った事はないので詳しい事はわからない。
自分の場合はプログラムのビルドではなく、ゲームデータのビルドに使用した。
(プログラムのビルドならVisualCやEclipseなどの専用ツールに任せた方が楽だったりする)
C言語などのビルドでは依存関係を調べる関係上、大規模開発では速度が出ないという意見もある。
しかし、自分が使っている限りでは、ゲームデータなどのコンバートであればかなりの数のデータを処をしているが、実用十分な速度が出ていると思う。
■SCons公式
http://www.scons.org/
使い方
ビルドしたいプロジェクトディレクトリに「SConstruct」というファイルを作成する。
SConstructにビルドコマンドを記述していくが、もちろんPythonコードをそのまま書ける。
以下、簡単なSConstructの例。
#!/usr/bin/env python # -*- coding: utf-8 -*- import os print "hello scons: %s" % os.getcwd()
コマンドライン上で「scons」と打つと、ビルドが始まる。
> scons scons: Reading SConscript files ... hello scons /home/xxxx scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. scons: done building targets.
-cコマンドを付けると、クリーンアップされる。
> scons -c
独自のコマンドを実行する「Command」
自前で作ったコンバートプログラムを実行するには「Command」を使用する。
以下、自前のコンバートプログラム「my_convert.py」で、ルートディレクトリ配下のtxtファイルをコンバートする例。
#!/usr/bin/env python # -*- coding: utf-8 -*- import os import glob #環境設定 システムパスの設定など env = Environment(ENV = os.environ) for input_name in glob.glob("*.txt"): output_name = os.path.splitext(input_name)[0]+".out" env.Command( output_name, input_name, "python my_convert.py $SOURCE $TARGET" )
「Command」は、出力したファイル名を返す。
env = Environment(ENV = os.environ) print env.Command("a.out", "a.in", "python my_convert.py $SOURCE $TARGET") # -> "a.out"
第2引数の出力ファイルをNoneにすると、出力ファイルがないタスクを作ることができる。
ファイルの更新チェックは行われずに、毎回必ず実行されるタスクとなる。
env = Environment(ENV = os.environ) #出力ファイルがない場合、毎回実行されるタスクとなる env.Command(None, "a.in", "python my_convert.py $SOURCE")
また、第3引数のactionにはPython関数も指定する事が可能。
def my_action(target, source, env): print target[0], len(target) print source[0], len(source) return None env.Command("a.out", "a.in", my_action)
独自のビルダーを定義する「Builder」もあるが、「Command」で十分な場合が多い。
独自のビルダーを実行する「Builder」
独自のビルダーを登録する事も可能、こちらは拡張子などを自動で変換してくれる。
#!/usr/bin/env python # -*- coding: utf-8 -*- import os import glob #独自ビルダー bld = Builder( action = "python my_convert.py $SOURCE $TARGET", suffix = '.out', src_suffix = '.txt', ) #独自ビルダーを登録 env = Environment(ENV = os.environ, BUILDERS = {'MyBuilder' : bld}) for input_name in glob.glob("*.txt"): env.MyBuilder(input_name)
引数の解析
sconsに引数を設定する事ができる。
以下、引数「project_dir」を渡すコマンドライン。
> scons project_dir=/home/prj
「arg=value」の形で書かないと、ターゲットオブジェクトの認識されてしまうので注意。
SConstructでは、「ARGUMENTS」という辞書に引数が格納される。
#引数 project_pathを取得 project_path = ARGUMENTS.get("project_path", "./") # -> project_path = /home/prj
スキルはあるが飯は食えないかもしれない
仕事が一段落したので、久しぶりの更新です。
あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定
面白そうなのでPythonで解いてみました、実装時間は1時間くらいです。
Pythonすごいなー、C++やJavaでやれって言われたら3時間では終わらんわ・・・
#!/usr/bin/env python #-*- coding:utf-8 -*- import sys import itertools def main(input): # 牌情報を"牌が何個あるか"という数値配列形式に変換 # 例) 1223344888999 => [1, 2, 2, 2, 0, 0, 0, 3, 3] t = sorted([int(x) for x in input]) num_hi = [t.count(x) for x in range(1,10)] # 順子・刻子・アタマのすべての要素を列挙します pieces = getPieces(num_hi) #すべての要素から4要素の組み合わせでサーチします ans = set() for p in itertools.combinations(pieces, 4): # 牌情報から4要素を抜き取る t_num_hi = num_hi[:] subHi(t_num_hi, p[0]) subHi(t_num_hi, p[1]) subHi(t_num_hi, p[2]) subHi(t_num_hi, p[3]) # 残り枚数2枚以上、または値がマイナスだったら無効 if sum(t_num_hi) > 2 or min(t_num_hi) < 0: continue # 残った配牌から最後の要素を取得 last = getLastPices(t_num_hi) if last: # 有効な要素が取得できたら、その組み合わせは有効 # 文字列変換&ソートして集合へ追加する t = sorted(["".join([str(x) for x in y]) for y in p]) t = "".join(["("+x+")" for x in t]) t += "["+"".join([str(x) for x in last])+"]" ans.add(t) #結果表示 for a in ans: print a def getPieces(num_hi): # 順子・刻子・アタマのすべての要素を列挙します ret = [] #刻子 ret += [[i+1]*3 for (i,x) in enumerate(num_hi) if x>=3] #アタマ ret += [[i+1]*2 for (i,x) in enumerate(num_hi) if x>=2] #順子 for i in range(10-3): m = min(num_hi[i:i+3]) if m > 0: ret += [[i+1,i+2,i+3]]*m return ret def getLastPices(num_hi): # 残り1〜2牌から有効な要素を取得 sum_num = sum(num_hi) if sum_num == 1: # 残り1枚の場合は裸単騎 return [num_hi.index(1)+1] elif sum_num == 2: if 2 in num_hi: # 残り2枚、かつ同種類の場合はシャンポン待ち return [num_hi.index(2)+1, num_hi.index(2)+1] else: for i in range(10-3): if sum(num_hi[i:i+3]) == 2: # 残り2枚、かつ牌が数字が連続または1枚飛ばしなら # 両面 or ペンチャン or カンチャン待ち return [idx+1 for idx,x in enumerate(num_hi) if x==1] return None def subHi(num_hi, elm): # 牌情報から要素を抜き取る for x in elm: num_hi[x-1] -= 1 if __name__ == "__main__": main(sys.argv[1])
出力結果は以下の通り。
>mahjong.py 1223344888999 (234)(234)(888)(999)[1] (123)(44)(888)(999)[23] (123)(234)(888)(999)[4] >mahjong.py 1112345678999 (111)(234)(678)(999)[5] (11)(123)(456)(999)[78] (123)(456)(789)(99)[11] (11)(123)(456)(789)[99] (111)(345)(678)(999)[2] (11)(123)(678)(999)[45] (111)(456)(789)(99)[23] (111)(234)(567)(999)[8] (111)(234)(567)(99)[89] (111)(234)(789)(99)[56] (11)(345)(678)(999)[12]
コードは"シンプル&わかりやすさ"を重視しました。
「組み合わせ」はitertoolsを使っています、反則・・・?
きっと出題者は再帰を使って欲しいんだろうな、使いませんけど。
ほどよい難易度で問題自体はすごく楽しかったけど、これでプログラマの実力は測れるかな・・・?
麻雀ルールを知らないと準備にかなり時間かかる気がします。
でも、実際にスキルあっても飯が食えないよなー、ゲーム業界は大手(任天堂除く)でも給料安いし。
本当に人材を探したいならゲーム業界を探すといいかもしれませんよ。
安くてスキルが高い人はゴロゴロいるよー(泣)
P.S
要件定義に同牌は4枚までと明記されていない事に気づきました。
てことは「1111111111111」も待ちアリにしないとダメ・・・?
同牌を4枚以上に対応したい場合は、getPieces()を以下のように書き換えればOK
def getPieces(num_hi): # 順子・刻子・アタマのすべての要素を列挙します ret = [] #刻子 #ret += [[i+1]*3 for (i,x) in enumerate(num_hi) if x>=3] #アタマ #ret += [[i+1]*2 for (i,x) in enumerate(num_hi) if x>=2] # ↓のように書き換えます #刻子&アタマ for (i,x) in enumerate(num_hi): for y in range(x//3): ret += [[i+1]*3] for y in range(x//2): ret += [[i+1]*2] #順子 for i in range(10-3): m = min(num_hi[i:i+3]) if m > 0: ret += [[i+1,i+2,i+3]]*m return ret
出力結果は以下の通り。
>majong.py 1111111111111 (111)(111)(111)(111)[1] (11)(111)(111)(111)[11]
うーん、考えすぎかな・・・?
CEDECへ行ってきました
仕事が忙しくてブログが放置ぎみになってしまいました。
やっとマスターが終わったかと思えば、CEDECですね。
今回から横浜なんですねぇ。
S県民にとっては会場が遠い・・・・
ベイズの定理(入門編)
前回のエントリーで予告した通り、今回は「ベイズの定理」です。
事前に断っておきますが、ベイズの定理は簡単です。
内容は小学生レベルの算数ですから、解らなくても自信を持って何回か読みなおせば絶対にわかります。
(わからなかったらコメントに質問してね)
ベイズの定理ってなんぞ?
ベイズの定理とはトーマス・ベイズ(1702-1761)というイギリスの牧師によって発見されました。
今やベイズの定理はあらゆる所に使われいます、スパムメールを振り分けたり、犯罪捜査に使われたり、マーケティングに使われたり、人工知能に使われたり、沈没しちゃった潜水艦を見つけたり、株の売買に使われたり、結婚相手を見つけちゃったり・・・
ベイズ万能すぎるだろ!!
「ベイズの定理」を理解はしなくても、言葉だけでも覚えていれば何かと便利です。
何かしらの問題に直面した際に、
「ふむ・・・このxにベイズの定理を使えば・・・」
とつぶやくだけで、それっぽく見えるのでオススメです。
(「バルキスの定理」くらい万能ですので、使用は自己責任でお願いします)
ベイズの前に「条件付き確率」
ベイズの定理を理解するには、みんなが大嫌いな「条件付き確率」を理解しなくてはなりません。
「条件付き確率」とは、
ある事象Bが起こる条件下で、別の事象Aが起こる確率のこと。
記号ではP(A|B)と表す。
という事です。
OK,OK、君の言いたい事はよく解ってる、よくわからないんだろ?
例えば「あなたは男だとして、イケメンである確率は?」という問題があった場合、先ほどの記号で表すと「P(イケメン|男)」となります。
P(A|B)で、Bが前提の上でAがで成り立つ確率となります。
ここで1つ有名な問題を出しましょう。
隣の家に2人の子供がいる事が解っています、
ある日、隣の子供のうち1人が女の子である事が解りました。*1
隣家のお母さんに「女のお子さんはいますか?」と質問した所「はい」と答えました。
このとき、もう1人の子供も女の子である確率はいくつでしょうか?
※なお、男女比は1:1とする。
答えを記号で表すと「P(2人とも女の子|1人が女の子)」となります。
考え方としては、子供2人の組み合わせは「姉妹」、「姉弟」、「兄妹」、「兄弟」の4通りです。
「1人が女の子」という前提条件があるため、「兄弟」が除外され「姉妹」、「姉弟」、「兄妹」の3通りとなります。
その3通りの中で「姉妹」が当たる確率を求めればいいのですから、答えは「1/3」です。
「1/2」だと思った方、猛省してください。
いよいよベイズの定理
「ベイズの定理」とは、まさしく今まで説明してきた「条件付き確率」を求めるための計算式なのです。
まずは「ベイズの定理」を見てましょう。
P(A|B) = P(A∧B) / P(B)
「P(B)」は事前確率と呼ばれ、単純にBが起こる確率です。
(ちなみに「P(A|B)」は事後確率と呼ばれます)
「P(A∧B)」は同時確率と呼ばれ、AとBの両方の事象が成り立つ確率です、∧は論理記号で論理積を表し、「(A∧B)」は「AかつB (A and B)」という意味となります。
「P(A∧B)」が難しい!と思った方、ほとんどの場合は「P(A∧B)」=「P(A)」と思ってOKです。
ごく稀に例外となる問題もありますが、そういう問題は事前情報が欠けていたりなど、どこか不自然な問題が殆どです。
だから解らない人は無理せずに、こう覚えておいてください、その内慣れてきますから :-)
ベイズの定理に関してはここで面白い問題を見つけました。20年前の早稲田大学の入試問題だそうです。
5回に1回の割合で帽子を忘れるくせのあるK君が、正月に A、B、C 3軒を順に年始回りをして家に帰ったとき、帽子を忘れてきたことに気がついた。2軒目の家 B に忘れて
きた確率を求めよ。
正直、どの家で忘れたかよりも、K君が心配ですが・・・
普通に考えれば、Bの家で忘れた確率は「4/5×1/5=4/25」(A家で忘れない確率×B家で忘れる確率)ですよね。
しかし、上記の確率では「3軒回って帽子を忘れない確率」も含まれています、「帽子を忘れている」という前提条件がついている事に注意です。
条件付き確率の記号で表すと「P(B家で忘れた確率|帽子を忘れた確率)」となり、ベイズの定理を使うと以下の式になります。
P(帽子を忘れた確率) =
(1/5)+(4/5×1/5)+(4/5×4/5×1/5) =
61/125
P(帽子を忘れた確率∧B家で忘れた確率) =
P(B家で忘れた確率) =
(4/5×1/5) = 4/25
P(B家で忘れた確率|帽子を忘れた確率) =
P(帽子を忘れた確率∧B家で忘れた確率) / P(帽子を忘れた確率) =
(4/25)/(61/125) = 20/61
答えは「20/61」となります、簡単でしょ?
前エントリーの問2をベイズの定理で解いてみよう
前エントリーの問2をベイズの定理で解いてみましょう。
多くの方が「扉を変える2/9、扉を変えない2/9、無効になる5/9」と答えていたのが印象的でした。
確かにこれでも間違いではありませんが、条件付き確率を理解しているとは言えません。
問2では「地震で無効にならなかった」という前提条件が付き、「P(扉を変えて当たる確率|地震で無効にならない確率)」を求めるので、以下の様になります。
P(扉を変えて当たる確率) = (4/9)×(1/2)
P(地震で無効にならない確率) = 4/9
P(扉を変えて当たる確率|地震で無効にならない確率) =
P(扉を変えて当たる確率) / P(地震で無効にならない確率) =
( (4/9)×(1/2) )/(4/9) = 1/2
となり、答えは「1/2」となります。
まぁ、「変えるか、変えないか」の2択問題なのに「2/9と2/9」という答えはおかしいですよね
私が髪型で「7:3分け」にしようか「真ん中分け」にしようか悩んでいるところへ、
「2:2分けがいいよ^^」
と言ってるようなもんです。
残りの6はどこに?っていうか、ハ、ハゲちゃうわ!!
「残りの6は、近い将来なくなる可能性が高いので除いておきました^^」
く・・・くやしぃ!!
長くなったので続きます
自分でもベイズの定理で曖昧な部分があり、備忘録も兼ねて説明を書きました。
本当はベイズの定理でもっと面白い事が出来るんですが、それはまた次回に。
P.S
確率問題「トランプと3つの箱」 - kojetteの研磨日記にベイズの定理の問題が出ています、ベイズの定理の有効性を示す問題として非常によく出来ています。
時間があったら解いてみると面白いと思います。
(まだ解答が未発表です、私も解答しているけど間違ってたら非常にカッコ悪い・・・)
*1:この言い方だと、問題が確立しないので変更しました、id:kojetteさん、ありがとうございます
モンティホール問題の補講
前回のエントリーで解答をくれた方たち、ありがとうございます。
正解した方たち、おめでとうございます。
前知識なしで2問とも正解した方は、非常に頭のいい方だと思います。
残念ながら不正解だった方たち、落ち込まないでください。
私も最初にこの問題を聞いた時は、壮大に引っかかりました。
出題者は職場の女の子で、知人から聞いた問題との事でした。
一応、理数系のプログラマとしての自信もそれなりにあった私は、
「はぁ〜(深いため息)
あのねぇ、誰がそんな間違った知識を広めているかは知らないけどさ、
数学で飯を食っている僕に言わせれば、それは間違いだよ。
君もそんな嘘に騙されちゃダメだよ、よくある確率のトリックさ!」
と、女の子を優しく諫めました。
で、気になったので、あとでインターネットで調べたら・・・
うわぁぁぁぁぁぁぁぁぁぁぁぁ!!
(トラウマ発動中)
言い訳をするつもりではありませんが、モンティホール問題は一流の数学者でさえ引っかかる問題で、アメリカでも大騒動になった問題です、詳しくはid:tazumaさんのエントリーモンティ・ホール ジレンマ その3をご覧ください。
業務連絡
このエントリーを見た同僚からクレームが入りました。
「きみ、このブログの問2の問題文は、俺に出した問題文より随分とマイルドじゃないか。
俺に出題した時は、もっと難しい問題文じゃなかった?」
はい、すみません。
あのときは徹底的にあなたを貶める事が目的だったので、ちょっと難しい問題文にしました。
「これじゃ、俺がキレやすい若者みたいじゃないか」とクレームがきておりますので、彼の名誉の為に、彼へ出した問題文を掲載します。
問題:
3つの扉(A,B,C)があり、その内のどれか1つの扉に当たりが入っています。司会者はどこの扉が当たりかを知っており、あなたが選択した扉以外のハズレの扉をランダムで1つだけ開いてくれます。
あなたはAの扉を選択しました。
そこで司会者がCの扉を開けてハズレであることを示しました。
この時、あなたはBの扉に変更すると、当たる確率が2/3に上がる事は前の問題から明らかです。
先ほどと全く同じ条件下で、あなたはAの扉を選択しました。
その直後に地震が起きて、偶然Cの扉が開いてしまいハズレである事が解ってしまいました。
しかし、司会者はもともとCの扉を開こうと決めていたので、ゲームを続行しました。
この時、あなたはBの扉へ変更するべきでしょうか?
ふむ、確かに前の問題との同一性が強調されて難しくなってますね。
っていうか、この問題って検算してないけど合ってるのかな・・・?
P.S
検算しました、答えは「変えても、変えなくても一緒」です。
【回答編】はてな民に確率の問題を出してみよう
この記事は「はてな民に確率の問題を出してみよう」の回答編です。
まずは、そちらをご覧ください。
はてな民に確率の問題を出してみよう
こんにちは、今回は確率の話です。
以前、職場で余興として問題を出したのですが、ほぼ全員がこの問題を知りませんでした。
理系が多く集まる職場なので、意外にみんな知らないんだなぁと思ったのですが、今度はリテラシーの高い(と勝手に思っている)はてな民に問題を出したら、どうなるんだろうと純粋な好奇心が沸いてきました。
なお有名な問題ですので、答えを知っている方はあまりヒントを出さない方向で・・・
問1
ティムはテレビのクイズ番組に出演し見事優勝をはたしました、優勝賞品の自動車をゲットするチャンスを得たのです。
司会者は言いました。
「ここにA、B、Cの3つのドアがあります。
1つのドアの後ろには自動車、それ以外の2つのドアの後ろにはヤギがいます。
ティムは1つのドアを選び、そのドアの中に自動車が入っていれば賞品をゲットできます。
もし、ヤギが入っていた場合はハズレです。
さぁティム、どのドアを選びますか?」
ティムは自動車がどうしても欲しかったので、本気で悩みました。
ティム「Aのドア!」
それを聞いた司会者はニヤリと笑いながら言いました。
「ここでティムにスペシャルヒントをあげましょう。
ティムが選ばなかったBとCのドアですが・・・
実はCのドアにはヤギが入っています!」
司会者はどのドアが当たりかを知っていたので、ティムの選んだドア以外で、ハズレのドアを1つ開けてヤギを見せました。
さらに司会者は言います。
「さぁ、ティム。
特別に選んだドアを変更する権利をあげましょう。
本当にAのドアでいいんですか?それとも変更しますか?」
観客は大盛り上がり!
なお、ゲームのルールは以下のとおり。
- 3つのドアに自動車、ヤギ、ヤギがランダムに入っている。
- ティムはドアを1つ選ぶ。
- ティムの選んだドア以外のドアのうち1つを必ず開ける。
- 司会者は自動車のあるドアを知っていて、必ずヤギの入っているドアを開ける。
ティムは最初の「A」のドアを貫くべきか、それともドアを変更すべきなのか・・・
どちらが自動車をゲットできる確率が高いでしょうか?
以下の中からお選びください。
A.最初の直感を信じるぞ!選択を変えない。
B.やっぱり選びなおそう!選択を変える。
C.惑わされるな!どちらも同じ確率だ!
問2
さらに別バージョンの問題もあります。
こちらも合わせてどうぞ。
問1のゲームルールで、司会者がハズレのドアを宣言しようとした瞬間、地震がおきました。
偶然にハズレのドアが開いてしまい、中のヤギが見えてしまいました。
しかし、司会者はもともとハズレのドアを開けようとしていたので、気にせずに進行を続けました。
なお、ゲームのルールは以下のとおりです。
- 地震によってランダムで1つのドアが必ず開く。
- 自動車のドア、またはティムが選んだドアが開いてしまった場合、ゲームは無効。
ティムは選択を変えた方がいいのでしょうか?
こちらも、以下の中から回答を選んでください。
A.最初の直感を信じるぞ!選択を変えない。
B.やっぱり選びなおそう!選択を変える。
C.惑わされるな!どちらも同じ確率だ!
有名な問題なんで、知っている人は知ってるだろうなぁ・・・
でも、どんな結果になるのかなぁ、オラなんかワクワクしてきたぞ!
って、これで誰も答えなかったらどうしよう・・・そしたら
「我はメシア、明日この世界を粛清する。」ってエントリー立てます。
解答編:【回答編】はてな民に確率の問題を出してみよう - Pashango’s Blog
追記:
2009/8/2, 2009/8/4
問題文に誤解を与える箇所があったので、文章を変更しました。
もし、考えてしまった人がいたら、ごめんなさい。
anhedoniaさんの指摘により、誤解を招く司会者の一言を変更しました。問題の本質は変わりません。ありがとうございました。