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