4桁の数字で10を作る
1桁の数字4つに対して四則演算を行って10を作るという頭の体操がある。プログラムを作ってみた。
#!/usr/local/bin/ruby X = %w{ 1 1 9 9 } #対象の数字 OP = %w{ + - * / )+ )- )* )/ +( -( *( /( )+( )-( )*( )/( } # 間に挟み込む演算子、括弧の有無と組み合わせて16種 LL = %w{ - ( -( } <<"" # 左端に来る物 RR = %w{ ) } <<"" # 右端にくる物 X.map{|x| "#{x}.0"}.permutation(4) do |x| # 整数のままでは除算で切り捨てになるので実数化して順列を取る OP.permutation(3) do |op| # 演算子も順列を取る LL.each do |ll| # 左端と右端はeachでOK RR.each do |rr| expr = ll+x.zip(op).join+rr # つなぎ合わせて next if /\(\d\.0\)/ =~ expr # (1.0) のようなものは冗長なのでスキップ val = begin eval expr # とりあえず式と思って評価してみる rescue SyntaxError # 括弧の対応ができてないのは事前にチェックせずエラーになるのに任せる nil end puts "#{expr.gsub(/\.0/,'')} = #{val}" if val==10.0 # 浮動小数点誤差は気にしなくて良いみたい end end end end
Rubyで浮動小数点の値に対しての == を使ったのは多分初めてだけど、a=1.0/9.0; a*9.0==1.0 は真になるようです。
Cでやると当然等しくならないので、Rubyがこのあたりの面倒を見ていると思うが、マニュアルを見たり、ちょっとググったりしてみた限りでは情報は見つからず。安全サイドでやるためには「差の絶対値がいくら以下」のようにすべきか。
2013-02-07追記
そうだ、Rationalでやればいいのか。
#!/usr/local/bin/ruby X = %w{ 1 1 9 9 } #対象の数字 OP = %w{ + - * / )+ )- )* )/ +( -( *( /( )+( )-( )*( )/( } # 間に挟み込む演算子、括弧の有無と組み合わせて16種 LL = %w{ - ( -( } <<"" # 左端に来る物 RR = %w{ ) } <<"" # 右端にくる物 class Fixnum def /(other) # 整数除算を有理数除算に再定義 self.to_r / other.to_r end end X.permutation(4) do |x| # 順列を取る OP.permutation(3) do |op| # 演算子も順列を取る LL.each do |ll| # 左端と右端はeachでOK RR.each do |rr| expr = ll+x.zip(op).join+rr # つなぎ合わせて next if /\(\d\)/ =~ expr # (1) のようなものは冗長なのでスキップ val = begin eval expr # とりあえず式と思って評価してみる rescue SyntaxError, ZeroDivisionError nil # 括弧の対応ができてないのは事前にチェックせずエラーになるのに任せる end puts "#{expr} = #{val}" if val==10 end end end end
2013-02-08追記
メモ:数字が4つだからこれで良いが、5つ以上になったら括弧の多重を考えないといけない。
((a+b)*c+d)*e のようなケース。数字が4つだと多重括弧があっても外して一重以下にできる。
2014-08-02追記
だめだ。演算子はpermutationじゃなくてrepeated_permutationを使わないと。
#!/usr/local/bin/ruby X = %w{ 1 1 9 9 } #対象の数字 OP = %w{ + - * / )+ )- )* )/ +( -( *( /( )+( )-( )*( )/( } # 間に挟み込む演算子、括弧の有無と組み合わせて16種 LL = %w{ - ( -( } <<"" # 左端に来る物 RR = %w{ ) } <<"" # 右端にくる物 class Fixnum def /(other) # 整数除算を有理数除算に再定義 self.to_r / other.to_r end end X.permutation(4) do |x| # 順列を取る OP.repeated_permutation(3) do |op| # 演算子は重複を許す順列を取る LL.each do |ll| # 左端と右端はeachでOK RR.each do |rr| expr = ll+x.zip(op).join+rr # つなぎ合わせて next if /\(\d\)/ =~ expr # (1) のようなものは冗長なのでスキップ val = begin eval expr # とりあえず式と思って評価してみる rescue SyntaxError, ZeroDivisionError nil # 括弧の対応ができてないのは事前にチェックせずエラーになるのに任せる end puts "#{expr} = #{val}" if val==10 end end end end