せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】【アルゴリズム】10パズルを解く。ファイナルアンサー。回答全部入り。

振り返り。テンパズルとは。

1から9までの、1桁の数字がかかれたカードが4枚ある。
この数字をそれぞれ1回ずつ使い、10になるように計算する。

あれです。
駅の切符とか、ナンバープレートとかで暇つぶしにやるあれ。

方法

1.計算順序を考えるのは大変なので、逆ポーランド記法でパターンを列挙する
 ⇒この記事でやりました
2.逆ポーランド記法を計算するメソッドを作る
 ⇒この記事でやりました
3.4つの数の組み合わせを列挙するメソッドを作る
 ⇒この記事でやりました


ここまできたらあとは計算するだけなので、先が見えています。

ポイント

0で割り算するとinfinityとなり、to_iメソッドを呼び出すとエラーになります。
0で割り算するときは答えが0になると定義しました。

コード

class Calc
  def self.make_expression(str)
    @@data = str.split("")
    @@operatars = ["+","-","*","/"]
    _make_rpn([], [], [0,0,0,0], 0, 0)
  end
  def self._make_rpn(result, candidate, is_uesd, num_count, ope_count)
    return result << candidate.join('') if num_count+ope_count == 7
    return if  num_count+ope_count > 7
    if num_count - ope_count >=2
      @@operatars.each do |ope|
        tmp = candidate.dup
        tmp << ope
        self._make_rpn(result, tmp, is_uesd, num_count, ope_count + 1)
      end
    end
    if num_count < 4
      is_uesd.each_with_index do |isused, idx|
        if isused == 0
          tmp = is_uesd.dup
          tmp2 = candidate.dup
          tmp[idx] = 1
          tmp2 << @@data[idx]
          _make_rpn(result, tmp2, tmp, num_count + 1, ope_count)
        end
      end
    end
    result
  end
  def self.exe(str)
    num_stack = []
    datas = str.split("")
    datas.each do |data|
      if data =~ /\d/
        num_stack << data 
      else
        a = num_stack.pop
        b = num_stack.pop
        tmp = _calc(b, a, data) 
        if tmp
          num_stack << tmp 
        else
          return "0"
        end
      end
    end
      raise "argument is not correct" if num_stack.length != 1
      num_stack[0]
  end
  def self._calc(num1, num2, operator)
    raise "argument is not correct" if num1.nil? || num2.nil?
    a = num1.to_f ; b = num2.to_f
    return a + b if operator == "+"
    return a - b if operator == "-"
    return a * b if operator == "*"
    if operator == "/"
      return a / b if b !=0
      return false
    end
  end
end

candidates =  Calc.make_expression("1234")
answers = []
candidates.each do |cand|
  answers << cand if Calc.exe(cand).to_i == 10
end
p "there are #{answers.length} answers"
p answers


■実行結果

"there are 48 answers"
["99*9+9/", "99*9+9/", "999*+9/", "999*+9/", "99*9+9/", "99*9+9


この答えの数から、難易度の高い組み合わせを見つけられそうですね。
「最も難しいテンパズル」。
面白そうだけどそこまで興味がないので見送ります。
と思ったけど、ついでなので全回答を作りました。

https://gist.github.com/sekaiya/6569856/



これで切符をニヤニヤみるような彼氏が出来ても、
このgistにアクセスすれば、

あ、9999?。答えは48個あるみたいだね。

ってすぐに会話が終わっちゃう。

っていうかそんな辛気臭い彼氏要らんわ。


やった!
飲んでくる!