家裏新購了一個數獨,週四午餐後消食便拿起來玩,半小時過去了,一小時過去了,一直沒成功……超級不服氣的,這道難題想逼我是嗎?乾脆直接寫python代碼破解好了!半小時後代碼還沒寫好,我被本身蠢哭了。在寫代碼這件事上,我顯然手生得很呢。值得欣慰的是,我起碼知道生物腦沒法完成的,電腦能夠代勞,只不過這代碼依然須要生物腦來完成。python
趁着端午小長假有點時間,就來琢磨下這件事好了。算法
計算目標:找到數獨的答案。是找一些答案,仍是找出全部答案呢?後者難度高,且包含前者,我選擇後者。框架
數獨規則:在下述六角形棋盤的12個位置中分別放入數字1至12,使得圖中每條連線4個位置加和爲26。dom
個人生物腦是如未嘗試解出答案的呢?函數
從棋盤取下全部數字,先隨意選擇4個數字放入某條線使之加和爲26,而後再選擇3個數字放入與此線有交點的另一條線使之加和爲26,而後嘗試第3條線……結果是,每次嘗試到第三、4條線時開始吃力,偶爾能知足5條線但最後一條線沒法知足條件。優化
我將如何用代碼來指揮電腦演算呢?用代碼復原生物腦的思路,顯然是很是蠢的解法。大家知道我是如何知道本身蠢的嗎?由於上述思路拆解出來就是:spa
我徹底沒法快速用代碼表達出以上思路,基於此不難自我判斷上述算法是愚蠢的。code
通過一些摸索後,我把碳基問題抽象爲下述硅基問題:cdn
import math
import random
def areyouwin(point_list):
if point_list[0] + point_list[6] + point_list[11] + point_list[4] != 26 :
return False
elif point_list[0] + point_list[7] + point_list[8] + point_list[2] != 26 :
return False
elif point_list[1] + point_list[7] + point_list[6] + point_list[5] != 26 :
return False
elif point_list[1] + point_list[9] + point_list[8] + point_list[3] != 26 :
return False
elif point_list[4] + point_list[9] + point_list[10] + point_list[2] != 26 :
return False
elif point_list[3] + point_list[10] + point_list[11] + point_list[5] != 26 :
return False
else:
return True
point_list = [1,2,3,4,5,6,7,8,9,10,11,12]
k = 0
while k < 10 :#此處k表示找出10個答案,想要更多答案就改k的值便可
random.shuffle(point_list)#新知識點:隨機打亂列表
if areyouwin(point_list):
print(point_list,k+1)
k = k + 1
複製代碼
初步搜索,我沒有找到如何實現輸出列表項所有的排列組合。鑑於很是清楚本身對排列組合的代碼實現接近於0經驗,因而故意想要本身寫代碼實現。blog
計算目標的大框架已經實現,如今只缺排列組合。那我聚焦於此:
子目標:輸出天然數1至12的全部排列組合
演算方法是怎樣的呢?
這個演算方法可行嗎?代碼將如何寫?彷佛有些困難。那我我先試着簡化問題:輸出天然數1至3的全部排列組合。並寫出代碼來驗證結果是否正確。
point_list = [1,2,3]
#列表A賦值給列表B時須要用切片的方式,不然列表B改變時會引起列表A改變!
point_1_list = point_list[:]
for i in point_1_list:#實現位置1的循環
point_2_list = point_1_list[:]
point_2_list.remove(i) #位置2的取值範圍是位置1的取值範圍移除位置1的當前值
#print(point_2_list)
for j in point_2_list:
#print(j,"j的值")
point_3_list = point_2_list[:]
point_3_list.remove(j)#位置3的取值範圍是位置2的取值範圍移除位置1的當前值
k = point_3_list[0]#位置3的取值範圍只剩1個數值
print(i,j,k)#打印排列組合結果
複製代碼
在完成上述代碼的過程當中,剛開始列表賦值並無採用切片的方式,致使全部列表均發生改變。無他,仍是反映我對列表的操做不熟。
先不論代碼是否簡潔優雅,至少它在功能上實現了。那麼如今,先試着用這個思路實現天然數1至12的所有排列組合,並計算得出數獨26的解法共有多少個!
下面就是不簡潔不優雅版本的數獨全部答案的代碼:
def areyouwin(point_list):
if point_list[0] + point_list[6] + point_list[11] + point_list[4] != 26 :
return False
elif point_list[0] + point_list[7] + point_list[8] + point_list[2] != 26 :
return False
elif point_list[1] + point_list[7] + point_list[6] + point_list[5] != 26 :
return False
elif point_list[1] + point_list[9] + point_list[8] + point_list[3] != 26 :
return False
elif point_list[4] + point_list[9] + point_list[10] + point_list[2] != 26 :
return False
elif point_list[3] + point_list[10] + point_list[11] + point_list[5] != 26 :
return False
else:
return True
point_list = [1,2,3,4,5,6,7,8,9,10,11,12]
#列表A賦值給列表B時須要用切片的方式,不然列表B改變時會引起列表A改變!
point_1_list = point_list[:]
for p_1 in point_1_list:#實現位置1的循環
point_2_list = point_1_list[:]
point_2_list.remove(p_1) #位置2的取值範圍是位置1的取值範圍移除位置1的當前值
for p_2 in point_2_list:
point_3_list = point_2_list[:]
point_3_list.remove(p_2)#位置3的取值範圍是位置2的取值範圍移除位置1的當前值
for p_3 in point_3_list:
point_4_list = point_3_list[:]
point_4_list.remove(p_3)
for p_4 in point_4_list:
point_5_list = point_4_list[:]
point_5_list.remove(p_4)
for p_5 in point_5_list:
point_6_list = point_5_list[:]
point_6_list.remove(p_5)
for p_6 in point_6_list:
point_7_list = point_6_list[:]
point_7_list.remove(p_6)
for p_7 in point_7_list:
point_8_list = point_7_list[:]
point_8_list.remove(p_7)
for p_8 in point_8_list:
point_9_list = point_8_list[:]
point_9_list.remove(p_8)
for p_9 in point_9_list:
point_10_list = point_9_list[:]
point_10_list.remove(p_9)
for p_10 in point_10_list:
point_11_list = point_10_list[:]
point_11_list.remove(p_10)
for p_11 in point_11_list:
point_12_list = point_11_list[:]
point_12_list.remove(p_11)
p_12=point_12_list[0]
one_list=[p_1, p_2, p_3, p_4, p_5, p_6, p_7, p_8, p_9, p_10, p_11, p_12]
if areyouwin(one_list):
print(one_list)
複製代碼
上述代碼for循環嵌套代碼部分,不夠簡潔優雅。如何優化,是我接下來須要再琢磨的。笨辦法也是辦法,終於把數獨的答案所有演算出來了,終於能安心睡個好覺了。
這就完了嗎?固然不!這個碳基轉硅基的解決方式中,有個內核的問題是,棋盤是個對稱的六角星型,因而碳基世界的正確答案,其實只需硅基代碼演算出答案的1/6。
以正確答案[1,2,3,6,11,7,5,12,10,8,4,9]爲例,棋盤每轉動60°就產生一個新答案: [2,3,6,11,7,1,12,10,8,4,9,5] [3,6,11,7,1,2,10,8,4,9,5,12] [6,11,7,1,2,3,8,4,9,5,12,10] [11,7,1,2,3,6,4,9,5,12,10,8] [7,1,2,3,6,11,9,5,12,10,8,4]
咿??如何優化代碼,能撇掉那5/6臃腫的答案呢?——難度超綱,我仍是暫時放下吧。
代碼寫得好很差,手熟與否很關鍵呀!而手熟並不是簡單的常常寫就夠了,而是要專門分解出哪些基礎功或知識點薄弱,並重點突破之。此乃大咖們常曰的「刻意練習」。