週末閒來無事,看到隔壁家的老王在和隔壁家的媳婦玩24點,就進屋看了看。發現老王是真不行啊,那不行,這也不行。python
就連個24點都玩不過他媳婦,給他媳婦氣的,啥都不能知足,這不能,那也不能。git
我坐下來和他媳婦玩了兩把,那都是無出其右,把把贏!github
我要走的時候,他媳婦還挽留我多玩幾把,有意思。算法
爲了能讓老王在他媳婦面前擡起頭來,我決定幫他一把……就用python寫了個算24點的玩意,老王對我感激不盡。windows
咱們先來約定下老王和他媳婦玩的24點規則:給定4個任意數字(0-9),而後經過+,-,*,/
,將這4個數字計算出24。編輯器
小時候玩的都是這個規則,長大了纔有根號,纔有各類莫名其妙的高級算法,很差玩了,由於我不會。函數
可能有人會以爲很簡單,可是真的簡單嗎?測試
好比:spa
你能一眼看出來答案嗎?好像真的能夠……操作系統
這樣想,將四個數字進行全排列,在他們之間添加運算符號。
運算符咱們須要進行排列組合,由於只有四個數字,因此只須要三個運算符,並且算法符可能會重複,好比三個都是+
。
再遍歷四個數字的全排列,對每一組數字而言,遍歷全部組合的操做符。最後將數字和操做符進行拼接運算,就能夠獲得最終結果了。
操做系統:windows10
python版本:python 3.7
代碼編輯器:pycharm 2018.2
使用模塊:math,itertools, collections.abc
一、首先咱們對全部數字進行去全排列,這裏咱們使用 itertools.permutations 來幫助咱們完成。
iertools.permutations 用法演示
from itertools import permutations
data_list = permutations([1,2,3,4],2)
for data in data_list:
print(data)
複製代碼
結果顯示
(1, 2)
(1, 3)
(1, 4)
(2, 1)
(2, 3)
(2, 4)
(3, 1)
(3, 2)
(3, 4)
(4, 1)
(4, 2)
(4, 3)
複製代碼
permutations 第一個參數是接收一個課迭代的對象,第二個參數指定每次排列時從課迭代對象中選着幾個字符進行排列。也能夠不傳入第二個參數,那麼默認就是可迭代對象的長度。而且返回一個生成器。
因此咱們須要對全部數字進行全排列,就能夠像下面這樣寫:
def get_all_data_sequence(data_iter):
return permutations(data_iter)
複製代碼
二、而後咱們須要拿到全部的操做運算符的全部組合方式。這裏咱們就會使用 itertools.product
函數了。
itertools.product 用法演示
from itertools import product
sequence1 = product('ABCD','xy')
sequence2 = product([0,1],repeat=3)
for sequence in sequence1:
print(sequence)
print('-'*30)
for sequence in sequence2:
print(sequence)
複製代碼
結果顯示
('A','x')
('A','y')
('B','x')
('B','y')
('C','x')
('C','y')
('D','x')
('D','y')
------------------------------
(0, 0, 0)
(0, 0, 1)
(0, 1, 0)
(0, 1, 1)
(1, 0, 0)
(1, 0, 1)
(1, 1, 0)
(1, 1, 1)
複製代碼
itertools.product
,返回傳入全部序列中笛卡爾積的元祖,repeat參數表示傳入序列的重複次數。返回的是一個生成器。
那麼獲取全部的操做運算符就能夠經過這個函數來獲取了
def get_all_operations_sequence():
operations = ['+','-','*','/']
return product(operations,repeat=3)
複製代碼
三、如今咱們已經拿到了全部可能組合的操做符和數字了,接下來就須要對他們進行拼接了。而後執行運算。
這一步操做咱們會用到 itertools.zip_longest()
和 itertools.chain.form_iterable()
函數。
itertools.zip_longest() 用法演示
data = zip_longest([1,2,3,4],['*','-','+'],fillvalue='')
for value in data:
print(value)
複製代碼
結果顯示
(1, '*')
(2, '-')
(3, '+')
(4, '')
複製代碼
zip_longest()
其實和 python 內置的 zip() 函數用法差很少,只是 zip_longest 是以最長的一個序列爲基準,缺失值就使用 fillvalue
參數的值進行填充
itertools.chain.form_iterable() 用法演示
data = zip_longest([1,2,3,4],['*','-','+'],fillvalue='')
data_chain = chain.from_iterable(data)
for value in data_chain:
print(value)
複製代碼
結果顯示
1
*
2
-
3
+
4
複製代碼
這裏的data是什麼樣的你們知道了吧,而後咱們將data傳入 chain.form_iterable() 中,它就能將裏面的值依次拿出來。
瞭解了這兩個函數以後,那麼咱們就能夠開始拼接數字和操做運算符了。
def calculate(self):
''' 計算值,返回對應的表達式和值 :return: '''
for data_sequence in get_all_data_sequence():
operation_sequences = get_all_operation_sequence()
for operation_sequence in operation_sequences:
value = zip_longest(data_sequence, operation_sequence,
fillvalue='')
value_chain = chain.from_iterable(value)
calculate_str = ''
# 對獲得的字符進行拼接成爲表達式 calculate_str
for _ in value_chain:
calculate_str += _
try:
result = eval(calculate_str
# 處理被除數可能爲零的狀況,而後就直接跳過此次循環
except ZeroDivisionError:
continue
if math.isclose(result, 24):
return calculate_str,result
return None,None
複製代碼
一、eval() 函數,接受一個字符串,能讓這個字符串當成 python 代碼運行,返回運行的結果。
二、math.isclose():爲何這裏須要使用 math.isclose() ,而不是直接使用==
運算符呢?這是由於最後算出來的表達式可能有精度問題,例如23.9...或者24.0...等數字,因此咱們就須要使用math.isclose()函數來幫助咱們判斷兩個數字是否相等了,這個函數就有一個精度範圍。這樣出現上面狀況的時候,咱們也能匹配獲得條件了。
咱們運行代碼,而後測試代碼是否能達到咱們的需求。
首先咱們測試1,2,3,4四個數字,
程序出來告終果 1*2*3*4
24
看來好像咱們寫的代碼是正確的
咱們再來測試一組數據8,8,3,3.
嗯?咱們並無獲得結果?這四個數字不能運算出24嗎?
8 / ( 3 - 8 / 3 )
這樣組合能夠吧,爲何沒有算出來這種結果呢?
這是由於咱們沒有考慮括號的緣由。括號是能夠改變運算優先級的。因此咱們得把括號考慮進去。
那麼想一下括號最多能夠有幾個呢?怎樣給咱們的表達式添加括號呢?
在4個數字的運算中,括號最多隻能有三個。
而且,在這裏,咱們使用一種簡單的方法添加括號,咱們把全部可能出現括號的狀況所有羅列出來,而後在將獲得的運算表達式拼接進去。
可能你們會以爲羅列出全部括號出現的狀況不現實,由於有不少狀況
其實否則,當咱們去羅列的時候,你就會發現,只有11種狀況。
FORM_STRS = [
# 數字 運算符 數字 運算符 數字 運算符 數字
# 一個括號 的狀況
'(%s %s %s) %s %s %s %s',
'(%s %s %s %s %s) %s %s',
'(%s %s %s %s %s %s %s)',
'%s %s (%s %s %s) %s %s',
'%s %s (%s %s %s %s %s)',
'%s %s %s %s (%s %s %s)',
# 兩個括號 的狀況
'(%s %s %s) %s (%s %s %s)',
'( (%s %s %s) %s %s) %s %s',
'( %s %s (%s %s %s)) %s %s',
'%s %s ((%s %s %s) %s %s)',
'%s %s (%s %s (%s %s %s))',
# 三個括號是重複的,就不用羅列出來了
]
複製代碼
而後咱們對獲得的表達式在進行遍歷拼接,而後咱們再運算表達式。
這樣咱們就能得出正確的結果了
代碼寫完了,終於能夠開始和媳婦,哦不,老王家的媳婦玩起來了
代碼已所有上傳至Github:github.com/MiracleYoun…
關注公衆號「Python專欄」,更多好玩有趣的Python等着你