標籤:
1 24點簡介
拿一副牌,抽去大小王后(初練也可以把J/Q/K也拿去),剩下1~10這40張牌(以下用1代替A)。任意抽取4張牌(稱為牌組),用加、減、乘、除 (可加括弧)把牌面上的數算成24。每張牌必須且只能用一次。如抽出的牌是3、8、8、9,那麼算式為(9-8)×8×3=24。
解決本問題兩種常見思路:
1. 運算式加括弧。可以選擇:不加括弧,1個括弧,2個括弧。
2. 逆波蘭運算式。
2 逆波蘭運算式
逆波蘭運算式又叫做尾碼運算式。在通常的運算式中,二元運算子總是置於與之相關的兩個運算對象之間,這種標記法也稱為中綴表示。波蘭邏輯學家J.Lukasiewicz於1929年提出了另一種表示運算式的方法,按此方法,每一運算子都置於其運算對象之後,故稱為尾碼表示。
2.1 解釋
逆波蘭記法中,操作符置於運算元的後面。例如表達“三加四”時,寫作“3 4 +”,而不是“3 + 4”。如果有多個操作符,操作符置於第二個運算元的後面,所以常規中綴記法的“3 - 4 + 5”在逆波蘭記法中寫作“3 4 - 5 +”:先3減去4,再加上5。使用逆波蘭記法的一個好處是不需要使用括弧。例如中綴記法中“3 - 4 * 5”與“(3 - 4)*5”不相同,但尾碼記法中前者寫做“3 4 5 * -”,無歧義地表示“3 (4 5 *) −”;後者寫做“3 4 - 5 *”。
逆波蘭運算式的解譯器一般是基於堆棧的。解釋過程一般是:運算元入棧;遇到操作符時,運算元出棧,求值,將結果入棧;當一遍後,棧頂就是運算式的值。因此逆波蘭運算式的求值使用堆棧結構很容易實現,和能很快求值。
正常的運算式 逆波蘭運算式 a+b ---> a b + a+(b-c) ---> a b c - + a+(b-c)*d ---> a b c - d * + a+d*(b-c) ---> a d b c - * +
2.2 例子
中綴運算式“5 + ((1 + 2) * 4) − 3”寫作
-
5 1 2 + 4 * + 3 −
下表給出了該逆波蘭運算式從左至右求值的過程,堆棧欄給出了中間值,用於跟蹤演算法。
計算完成時,棧內只有一個運算元,這就是運算式的結果:14
2.3 Python實現
1 # --rpn.py-- 2 from itertools import permutations # 全排列 3 from fractions import Fraction # 分數運算 4 from operator omport add, sub, mul, div # 函數+ - × / 5 6 card = map(str, [5, 5, 5, 1]) # 輸入4張牌存為字元 7 dict_op = {‘+‘: add, ‘-‘:sub, ‘*‘:mul, ‘/‘:div} # 將運算子轉為調用函數 8 op = [‘+‘, ‘-‘, ‘*‘, ‘/‘] * 3 # 四則運算,4張牌需要3次運算 9 op1 = list(permutations(op, 3)) # op中任取三個做全排列,結果構成列表10 op2 = map(lambda x: list(x), op1) # 列表元素從元組轉為列表11 op3 = map(lambda x: sorted(x), op2) # 排序12 ops = set(map(lambda x: tuple(x), op3)) # 列錶轉為集合,去重13 14 # 逆波蘭運算式實現15 def rpn(lst):16 stack = [] # 初始化棧17 for i in lst: # 遍曆傳入運算式列表18 if i.isdigit(): # 若為數字,則直接進棧19 stack.append(Fraction(i.strip()))20 else: 21 if i in dict_op: # 若為運算子,則取出兩元素作運算22 try:23 a = stack.pop()24 b = stack.pop() 25 stack.append(dict_op(b, a))26 except: break27 if len(stack) == 1: # 若最後棧內剩餘一個元素,接將結果輸出28 return stack[0]29 30 # 計算2431 for operator in ops: # 遍曆每一種運算子組合32 space = []33 space = card + list(operator) # 組合卡牌和運算子34 s = set(permutations(space)) # 排列卡牌運算子所有可能35 for j in s:36 if rpn(list(j)) == 24:37 print j38 39 print ‘Game over‘
24點計算程式 [Python]