#!/usr/local/bin/perl # number4.pl # # [問題] 切符に印刷された4桁の数字を、四則演算を使って10にする遊び。 #     四則演算の組合せで 10 になるすべてのパターンを求める。 # [解] Written by ODASAN, 2003/05/20,24 # 4つの数字を A, B, C, D とする。 # カッコを考慮した演算の形は 5 通りある。 # いずれも演算子は3個なので、順に op1, op2, op3 とする。 # (1) ((A op1 B) op2 C) op3 D # (2) (A op1 B) op2 (C op3 D) # (3) (A op1 (B op2 C)) op3 D # (4) A op1 ((B op2 C) op3 D) # (5) A op1 (B op2 (C op3 D)) # 演算子 op1, op2, op3 は +, -, *, / のいずれかだから、 # 演算子の組合せは 4*4*4=64 通り。 # + と * は交換法則が成り立つし、 # カッコを外しても同じ結果になることもあるが、 # 判定が面倒なので総あたりすると、 # ひとつの数字の組合せ(4桁の数)について # 5*64=320 通りの計算を試せばよい。 # 切符に印刷された数字は 0000〜9999 の # 10,000通り可能性がある。 # 320×10000=3,200,000回計算するのである。 # 小数計算誤差による 9.999... を10とみなす。 # 結果は result.txt というファイルに書出す。 # 成功した4つの値を value.csv というファイルに書出す。 # 実行時間も計ってみる。 $seconds = time(); # 計算時間を計算 $equal = 10; # この値になる演算式を求めるのである $allow = 0.001; # 小数計算で想定される誤差の許容範囲(てきとー) $under = $equal-$allow; # 等しいとみなす下限 $over = $equal+$allow; # 等しいとみなす上限 $counter = 0; # いくつあるか? @exp = ('(($a op1 $b) op2 $c) op3 $d', # 式のパターンは5通り '($a op1 $b) op2 ($c op3 $d)', '($a op1 ($b op2 $c)) op3 $d', '$a op1 (($b op2 $c) op3 $d)', '$a op1 ($b op2 ($c op3 $d))'); @ope = ('+', '-', '*', '/'); # 演算子は4通り open(PRES,">result.txt"); open(PVAL,">value.csv"); for ($a=0;$a<10;$a++) { # 数字 A for ($b=0;$b<10;$b++) { # 数字 B for ($c=0;$c<10;$c++) { # 数字 C for ($d=0;$d<10;$d++) { # 数字 D for ($p=0;$p<5;$p++) { # 式のパターン for ($i=0;$i<4;$i++) { # op1 for ($j=0;$j<4;$j++) { # op2 for ($k=0;$k<4;$k++) { # op3 $exp2=$exp[$p]; $exp2 =~s/op1/$ope[$i]/; # 式の書換え $exp2 =~s/op2/$ope[$j]/; $exp2 =~s/op3/$ope[$k]/; $exp2 =~s/ //g; $value = eval($exp2); # ここがミソ if (($value <= $over) and ($value >= $under)) { $exp2 =~s/\$a/$a/; # 結果表示用 $exp2 =~s/\$b/$b/; $exp2 =~s/\$c/$c/; $exp2 =~s/\$d/$d/; $counter++; print (PRES "$exp2=$equal\n"); print (PVAL "$a,$b,$c,$d\n"); } } } } } } } } } print ("$counter 個ありました。\n"); $seconds = time() - $seconds; print "計算時間は $seconds 秒でした。\n"; close(PRES); close(PVAL); exit();