摘要:而此處針對進一步的搜索,有兩個問題需要考慮如何選取搜索起點方格確定哪種搜索策略深度優先搜索,廣度優先搜索關于第一個問題,無論選擇哪個方格起始搜索,對于能否解決問題來說并不存在差異。
Github倉庫地址
學習是為了尋找解決問題的答案,若脫離了問題只為知曉而進行的打call,那么隨時間流逝所沉淀下來的,估計就只有“重在參與”的虛幻存在感了,自學的人就更應善于發現可供解決的問題。為了入門AI,定個小目標,解決數獨問題。
一、問題描述一個9*9的方格中,部分方格已預先填入數字,目的是按照如下規則將空白方格填上1-9中的一個:
每個方格中填且僅填一個數字,數字取值范圍1-9
以每行九個方格為單元來看,1-9每個數字都要出現,且僅出現一次
以每列九個方格為單元來看,1-9每個數字都要出現,且僅出現一次
以3*3九個方格為單元來看,1-9每個數字都要出現,且僅出現一次
描述問題是解決問題的第一步(將問題轉化為程序所能理解的數據模型,才能做進一步有效地思考)1.1 問題記錄方式
從左到右從上到下,以一個字符串的方式記下所有方格中的內容,有數字記數字,空白記作點(.),如:..3.2.6..9..3.5..1..18.64....81.29..7.......8..67.82....26.95..8..2.3..9..5.1.3..
以字典的方式記錄,將每行標記為ABCDEFGHI,每列標記為123456789,字典的key值為標記的單元格描述,如:A1,G4等;字典的value值為方格中的記錄:有數字記數字,空白記作點(.)
{ "A1": "." "A2": ".", "A3": "3", "A4": ".", "A5": "2", ... "I9": "." }
字符串方式,記錄簡潔占用空間小,但處理起來比較麻煩;字典方式,方便查找處理,但記錄空間較大。
所以,我們以字符串方式記錄存儲,以字典方式進行運算求解。那么在運算求解前需要對記錄方式的轉換。
1.2 字典方式記錄 1.2.1 所有key值數組rows = "ABCDEFGHI" cols = "123456789" def cross(a, b): return [s+t for s in a for t in b] boxes = cross(rows, cols)1.2.2 規則單元
row_units = [cross(r, cols) for r in rows] column_units = [cross(rows, c) for c in cols] square_units = [cross(rs, cs) for rs in ("ABC","DEF","GHI") for cs in ("123","456","789")] unitlist = row_units + column_units + square_units1.2.3 指定單元格所屬規則單元
units = dict((s, [u for u in unitlist if s in u]) for s in boxes) peers = dict((s, set(sum(units[s],[]))-set([s])) for s in boxes)1.3 記錄方式轉換
def grid_values(grid): return dict(zip(boxes, grid))
zip() 將可迭代的對象作為參數,把對象中對應的元素打包成一個個元組,然后返回由這些元組組成的列表
二、策略1:過濾淘汰首先明確一個概念:
規則單元:一個方格所屬的水平行、垂直列以及3*3方陣的所有方格
規則同胞:一個方格的規則單元中除了自己的其他方格
如果沒有任何限制,每個方格可填入的數字可以是123456789中的任何一個,而根據數獨游戲規則,預先填入數字的方格會限制,該方格的規則單元中,其他待填數方格的數字取值范圍。所以顯而易見的解決策略,便是根據限制規則,縮小取值范圍。
開始進行過濾淘汰之前,我們需要的初始數獨方格字典中,代表空方格的(.)用可取值的數字范圍替換,初始范圍為123456789
def grid_values(grid): valueLst = [] digits = "123456789" for item in grid: if item == ".": valueLst.append(digits) elif item in digits: valueLst.append(item) return dict(zip(boxes, valueLst))
如此獲得的初始數獨方格字典為:
{ "A1": "123456789", "A2": "123456789", "A3": "3", "A4": "123456789" "A5": "2", ... "I9": "123456789" }
過濾淘汰策略:找到已確定的數獨方格,再依次遍歷這些方格的規則同胞方格,從待確定方格的取值范圍中,把已確定的數字去掉,以縮小取值范圍。
def eliminate(values): solvedBoxes = [box for box in values.keys() if len(values[box]) == 1] for box in solvedBoxes: value = values[box] for peer in peers[box]: values[peer] = values[peer].replace(value, "") return values
過濾淘汰策略,是在規則單元上進行取值范圍縮小的。這只覆蓋了數獨游戲規則的一部分,而數獨規則還包括:
每個最小規則單元中九個方格中的數字123456789僅出現一次。特別說明一下,最小規則單元:
單行的九個方格,單列的九個方格,或3*3的九個方格,也可以說一個規則單元包含了三個最小規則單元。
根據最小規則單元,進一步縮小規則同胞中待填數的取值范圍,便引出了第二條規則:唯一可選策略
如果最小規則單元中,只有一個方格出現了某個數字,那么這個方格就該填這個數字
def only_choice(values): for unit in unitlist: for digit in "123456789": places = [box for box in unit if digit in values[box]] if len(places) == 1: values[places[0]] = digit return values
交替使用過濾淘汰策略和唯一可選策略便可將數獨問題中,所有待填數方格的取值范圍縮減至最小,但由于這兩種策略循環使用的終止條件,是不再有新確定的填數方格出現,所以這并不充分能解決所有數獨問題。
def reduce_puzzle(values): stalled = False while not stalled: solved_values_before = len([box for box in values.keys() if len(values[box]) == 1]) values = eliminate(values) values = only_choice(values) solved_values_after = len([box for box in values.keys() if len(values[box]) == 1]) stalled = solved_values_before == solved_values_after if len([box for box in values.keys() if len(values[box]) == 0]): return False return values四、策略3:約束搜索
對于方格預設數字比較多的數獨問題,或許可以直接通過上述縮減取值范圍的方法解決。但當所給預設數字方格比較少時,在完成取值范圍縮小后,必然還會有一些取值不確定的方格存在。如此問題的求解,就需要從多個可選值的方格中,分別假定其中一個進行搜索。
而此處針對進一步的搜索,有兩個問題需要考慮:
如何選取搜索起點方格?
確定哪種搜索策略:深度優先搜索,廣度優先搜索?
關于第一個問題,無論選擇哪個方格起始搜索,對于能否解決問題來說并不存在差異。而從求解過程的性能和效率來考慮,就有了差別。而在思考第二個問題之前,還需要明確一點:數獨問題的解是否唯一?顯然如果預設的方格過多且彼此矛盾,問題必然無解,而預設的方格過少,勢必也會存在多個滿足規則的解。所以為了優先求得一個確定解,我們采取深度優先搜索,而若是求可能的所有解,多線程進行廣度優先搜索,可以獲得較好的時間復雜度,但卻需要暫存許多中間信息。
def search(values): values = reduce_puzzle(values) if values is False: return False if all(len(values[s]) == 1 for s in boxes): return values n,s = min((len(values[s]), s) for s in boxes if len(values[s]) > 1) for value in values[s]: new_values = values.copy() new_values[s] = value attemp = search(new_values) if attemp: return attemp
如此數獨問題得解,但能解決速度問題的程序就能成為AI么?
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41351.html
摘要:可以針對筆者常用的數獨本文的實現都基于該,實現數獨的識別求解并把答案自動填入。專家級別的平均秒完成求解包括圖像數字提取,識別過程,完成全部操作。步驟四數獨求解,生成答案,并生成需要填充的數字序列。 1 序 ??數獨是源自18世紀瑞士的一種數學游戲。是一種運用紙、筆進行演算的邏輯游戲。玩家需要根據9×9盤面上的已知數字,推理出所有剩余空格的數字,并滿足每一行、每一列、每一個粗線宮(3*3...
摘要:首先在二維數組第一行隨機填充個數字,然后將這個數字隨機分布到整個二維數組中,然后使用求解數獨的算法對此時的數組進行求解,得到一個完整的數獨,然后按照用戶輸入的提示數量進行隨機挖洞,得到最終的數獨題目。 思路 1.生成數獨 數獨的生成總體思路是挖洞法。首先在二維數組第一行隨機填充1-9 9個數字,然后將這9個數字隨機分布到整個二維數組中,然后使用求解數獨的算法對此時的數組進行求解,得到一...
摘要:題目描述有效的數獨判斷一個的數獨是否有效。上圖是一個部分填充的有效的數獨。數獨部分空格內已填入了數字,空白格用表示。說明一個有效的數獨部分已被填充不一定是可解的。只需要根據以上規則,驗證已經填入的數字是否有效即可。 題目描述 有效的數獨判斷一個 9x9 的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。 數字 1-9 在每一行只能出現一次。數字 1-9 在每一列只能出...
摘要:上圖是一個部分填充的有效的數獨。數獨部分空格內已填入了數字,空白格用表示。但由于位于左上角的宮內有兩個存在因此這個數獨是無效的。說明一個有效的數獨部分已被填充不一定是可解的。只需要根據以上規則,驗證已經填入的數字是否有效即可。 判斷一個 9x9的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。 數字 1-9 在每一行只能出現一次。 數字 1-9 在每一列只能出現一次...
閱讀 2665·2021-11-11 16:54
閱讀 3657·2021-08-16 10:46
閱讀 3441·2019-08-30 14:18
閱讀 3034·2019-08-30 14:01
閱讀 2723·2019-08-29 14:15
閱讀 2007·2019-08-29 11:31
閱讀 3083·2019-08-29 11:05
閱讀 2583·2019-08-26 11:54