この記事について
テンパズルの解を求めるプログラムを書いてみる。テンパズルとは、4つの数字が与えられたときにそれらに関して四則演算を適用し10を作れるか確かめるもの。今回は、タイトル通り、逆ポーランド記法を適用して求めてみる。
逆ポーランド記法とは
計算式を後方に記述していくもの。
ex.)
1 + 2 → 1 2 +
9 / (7 + 8) - 6 → 9 7 8 + / 6 -
演算の単位で数をまとめ、演算子を後ろにつけていく。
この数式はスタックを使って計算できる。
package main
import (
"fmt"
"strconv"
"strings"
)
// v を順番に見ていく
// 数が登場したら stack に push
// 演算子が登場したら
// stack から pop して取り出した値を a
// 続けて stack から pop して取り出した値を b
// b, a の計算結果をスタックに push する (b が計算の最初にくる。下記プログラム参照)
// stack に残っている数を返す
func RPolishCalc(v []string) float64 {
stack := []float64{}
for _, item := range v {
i, ok := strconv.Atoi(item)
if ok == nil {
stack = append(stack, float64(i))
} else {
poppedVal_a := stack[len(stack)-1]
stack = stack[:len(stack)-1]
poppedVal_b := stack[len(stack)-1]
stack = stack[:len(stack)-1]
switch item {
case "+":
stack = append(stack, poppedVal_b+poppedVal_a)
case "-":
stack = append(stack, poppedVal_b-poppedVal_a)
case "*":
stack = append(stack, poppedVal_b*poppedVal_a)
case "/":
stack = append(stack, poppedVal_b/poppedVal_a)
}
}
}
return stack[0]
}
func Decode(v []string) string {
result := []string{}
for _, item := range v {
_, ok := strconv.Atoi(item)
if ok == nil {
result = append(result, item)
} else {
poppedVal_a := result[len(result)-1]
result = result[:len(result)-1]
poppedVal_b := result[len(result)-1]
result = result[:len(result)-1]
switch item {
case "+":
result = append(result, fmt.Sprintf("%v + %v", poppedVal_b, poppedVal_a))
case "-":
result = append(result, fmt.Sprintf("%v - %v", poppedVal_b, poppedVal_a))
case "*":
if 1 < len(poppedVal_a) {
poppedVal_a = fmt.Sprintf("( %v )", poppedVal_a)
}
result = append(result, fmt.Sprintf("%v * %v", poppedVal_b, poppedVal_a))
case "/":
if 1 < len(poppedVal_a) {
poppedVal_a = fmt.Sprintf("( %v )", poppedVal_a)
}
result = append(result, fmt.Sprintf("%v / %v", poppedVal_b, poppedVal_a))
}
}
}
return strings.Join(result, " ")
}
func main() {
v := "6 1 2 + * 8 -"
fmt.Printf("%v = %#v\n", Decode(strings.Split(v, " ")), RPolishCalc(strings.Split(v, " ")))
}
出力
6 * ( 1 + 2 ) - 8 = 10
逆ポーランド記法に変換すると数字と記号の組み合わせ
X が数字
□ が演算子
並びのパターンは 5通り
- X X X X □ □ □ = ans
- X X X □ X □ □ = ans
- X X X □ □ X □ = ans
- X X □ X X □ □ = ans
- X X □ X □ X □ = ans
数字の並びは 4!
記号がとりうるのは四則演算記号が3箇所で 4^3
そのため、5 * 4! * 4^3 = 7680 通りが全探索に必要な計算量になる
Todo 与えられた数字4つで全探索して 10 になる式をすべて列挙する