python基礎教程:用Python秒算24點實現及原理詳解

這篇文章主要介紹了Python秒算24點,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
什麼是24點
我們先來約定下老王和他媳婦玩的24點規則:給定4個任意數字(0-9),然後通過+,-,*,/,將這4個數字計算出24。

小時候玩的都是這個規則,長大了纔有根號,纔有各種莫名其妙的高級算法,不好玩了,因爲我不會。

可能有人會覺得很簡單,但是真的簡單嗎?

比如:

8,3,3,3
7,3,3,3
你能一眼看出來答案嗎?好像真的可以……

大致思路
這樣想,將四個數字進行全排列,在他們之間添加運算符號。

運算符我們需要進行排列組合,因爲只有四個數字,所以只需要三個運算符,而且算法符可能會重複,比如三個都是+。

再遍歷四個數字的全排列,對每一組數字而言,遍歷所有組合的操作符。最後將數字和操作符進行拼接運算,就可以得到最終結果了。

演示環境

操作系統:windows10

python版本:python 3.7

代碼編輯器:pycharm 2018.2

使用模塊:math,itertools, collections.abc

具體代碼
1、首先我們對所有數字進行去全排列,這裏我們使用 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)

2、然後我們需要拿到所有的操作運算符的所有組合方式。這裏我們就會使用 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)

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

代碼分析

1、eval() 函數,接受一個字符串,能讓這個字符串當成 python 代碼運行,返回運行的結果。

2、math.isclose():爲什麼這裏需要使用 math.isclose() ,而不是直接使用==運算符呢?這是因爲最後算出來的表達式可能有精度問題,例如23.9…或者24.0…等數字,所以我們就需要使用math.isclose()函數來幫助我們判斷兩個數字是否相等了,這個函數就有一個精度範圍。這樣出現上面情況的時候,我們也能匹配得到條件了。

我們運行代碼,然後測試代碼是否能達到我們的需求。

首先我們測試1,2,3,4四個數字,

程序出來了結果 123*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))',
 # 三個括號是重複的,就不用羅列出來了
]

然後我們對得到的表達式在進行遍歷拼接,然後我們再運算表達式。

這樣我們就能得出正確的結果了

代碼寫完了,終於可以開始和媳婦,哦不,老王家的媳婦玩起來了
最後給大家推薦一個資源很全的python學習聚集地,[點擊進入],這裏有我收集以前學習心得,學習筆記,還有一線企業的工作經驗,且給大定on零基礎到項目實戰的資料,大家也可以在下方,留言,把不懂的提出來,大家一起學習進步

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章