Python秒算24點,行還是不行?

来源:https://www.cnblogs.com/moonhmily/archive/2019/07/29/11261800.html
-Advertisement-
Play Games

周末閑來無事,看到隔壁家的老王在和隔壁家的媳婦玩24點,就進屋看了看。發現老王是真不行啊,那不行,這也不行。 就連個24點都玩不過他媳婦,給他媳婦氣的,啥都不能滿足,這不能,那也不能。 我坐下來和他媳婦玩了兩把,那都是無出其右,把把贏! 我要走的時候,他媳婦還輓留我多玩幾把,有意思。 為了能... ...


周末閑來無事,看到隔壁家的老王在和隔壁家的媳婦玩24點,就進屋看了看。發現老王是真不行啊,那不行,這也不行。

就連個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四個數字,

程式出來了結果 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:https://github.com/MiracleYoung/You-are-Pythonista/tree/master/PythonExercise/App/python_24/xujin

關註公眾號「Python專欄」,更多好玩有趣的Python等著你


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、一圖認清組件關係名詞 父子關係:A與B、A與C、B與D、C與E 兄弟關係:B與C 隔代關係:A與D、A與E 非直系親屬:D與E 總結為三大類: 父子組件之間通信 兄弟組件之間通信 跨級通信 2、8種通信方式及使用總結 props / $emit $children / $parent provi ...
  • JavaScript字元串存儲一系列字元,如“John Doe”。字元串可以是雙引號或單引號內的任何文本: 字元串屬性 字元串方法 字元串HTML包裝器方法 HTML包裝器方法返回包含在相應HTML標記內的字元串。這些不是標準方法,並且可能無法在所有瀏覽器中按預期工作。 ...
  • jQuery遍歷 - 過濾最基本的過濾方法是first(),last()和eq(),它們允許您根據元素在一組元素中的位置選擇特定元素。 其他過濾方法(如filter()和not())允許您選擇與特定條件匹配或不匹配的元素。 jQuery first()方法first()方法返回指定元素的第一個元素。 ...
  • 在進入正題之前,說一些廢話,談談對於我的前一篇文章被移出博客園首頁的想法。不談我對於其他首頁文章的看法,光從我自身找找原因。下麵分析下可能的原因: 1. 篇幅太短:我覺得篇幅不能決定文章的質量,要說清楚一個問題,肯定字數越少越好 2. 代碼過多,文字太少:Talk is cheap. Show me ...
  • 對於重覆的代碼,如果是重覆的字元串,我們會想到提出一個變數。如果是重覆的代碼塊,我們會想到提取出一個方法。 但如果這重覆的代碼塊中有一處或幾處是會變化的,那麼就沒那麼容易提取出一個方法。說起來有點抽象,下麵看一個例子。 一、分頁查詢 寫過CRUD的同學肯定寫過很多分頁查詢,分頁查詢的主要步驟是先校驗 ...
  • 舉個慄子 問題描述 模擬訪問資料庫“新增用戶”和“得到用戶”,用戶類假設只有 ID和Name 兩個欄位。 簡單實現 User SqlServerUser 測試 測試結果 存在問題 如果需要連接別的資料庫,那麼這個寫法無法擴展,下麵使用 工廠方法模式 實現 工廠方法模式實現 IUser SqlServ ...
  • [toc] 前言 Composite設計模式,將物體組合成一個樹結構,讓單個對象和組合對象使用起來都一樣,組合對象負責將實際的操作分發給每一個組件。 這篇博文分析了安卓的View相關的類,它們可以說是用了Composite設計模式。其中分析View的measure,layout,draw是如何從組合 ...
  • 零基礎java開發工程師視頻教程全套,基礎+進階+項目實戰(152G) 共130天,152G 下載地址 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...