總體思路 :
數獨九行九列,一個list裝一行,也就需要一個嵌套兩層的list
初始會有很多數字,我可不想一個一個賦值
那就要想辦法偷懶啦
然后再是窮舉,如何科學的窮舉
第一部分:錄入
某在線數獨網站的截圖
要想辦法,把它方便的變成嵌套的list
我的解決辦法:
手打到Excel里面
然后另存為csv文件
然后就當做txt讀取
l = None with open('數獨.csv','r',encoding = 'utf-8') as f: l = f.readlines() print(l) ''' 運行結果 --------------------------------------- ['\ufeff3,,,1,,8,4,,\n', ',,1,,,2,,3,\n', '4,,,,,,1,6,\n', ',5,8,,,9,,,4\n', ',3,,,5,,,,9\n', ',9,,3,,,5,,\n', ',,3,9,1,,,4,\n', '2,,7,5,,,9,,\n', '9,,,,4,,,5,3\n'] '''
發現文本開頭有個莫名其妙的\ufeff,另外它的長度是1
>>> s = '\ufeff3' >>> len(s) 2
只好加一句 l[0] = l[0][1:]
然后去掉末尾的\n 再以逗號為界切割
l = None with open('數獨.csv','r',encoding = 'utf-8') as f: l = f.readlines() l[0] = l[0][1:] l = map(lambda i : i.rstrip(),l) l = map(lambda i : i.split(","),l) for i in l: print(i,'---', len(i)) ''' 運行結果 --------------------------------------- ['3', '', '', '1', '', '8', '4', '', ''] --- 9 ['', '', '1', '', '', '2', '', '3', ''] --- 9 ['4', '', '', '', '', '', '1', '6', ''] --- 9 ['', '5', '8', '', '', '9', '', '', '4'] --- 9 ['', '3', '', '', '5', '', '', '', '9'] --- 9 ['', '9', '', '3', '', '', '5', '', ''] --- 9 ['', '', '3', '9', '1', '', '', '4', ''] --- 9 ['2', '', '7', '5', '', '', '9', '', ''] --- 9 ['9', '', '', '', '4', '', '', '5', '3'] --- 9 '''
九行九列..完美
下一步全部處理成數字
鑒於int()無法將空字符串轉化為0 所以需要新定義一個new_int
def new_int(s): return int(s) if s else 0 l = None with open('數獨.csv','r',encoding = 'utf-8') as f: l = f.readlines() l[0] = l[0][1:] l = map(lambda i : i.rstrip(),l) l = map(lambda i : i.split(","),l) l = [ list(map(new_int, i)) for i in l] for i in l: print(i) ''' 運行結果 --------------------------------------- [3, 0, 0, 1, 0, 8, 4, 0, 0] [0, 0, 1, 0, 0, 2, 0, 3, 0] [4, 0, 0, 0, 0, 0, 1, 6, 0] [0, 5, 8, 0, 0, 9, 0, 0, 4] [0, 3, 0, 0, 5, 0, 0, 0, 9] [0, 9, 0, 3, 0, 0, 5, 0, 0] [0, 0, 3, 9, 1, 0, 0, 4, 0] [2, 0, 7, 5, 0, 0, 9, 0, 0] [9, 0, 0, 0, 4, 0, 0, 5, 3] '''
第二部分 窮舉
假設81個格子有50是空的,每個格子1-9 9種可能
>>> 9**50 515377520732011331036461129765621272702107522001
顯然不能傻乎乎的直接遍歷
其實一個新格子並不是1-9 9種可能
它不可能是同行,同列,同區出現過的數字
這里將會用到set的加減
x,y = 0,1 whole = {1,2,3,4,5,6,7,8,9} x_set = set(l[x]) #行 y_set = { l[i][y] for i in range(9) } #列 block_num = big_small[(x,y)] #查字典得到區號 block_set = { l[i][j] for i , j in small_big[block_num] } #根據區號查該區的9個方格,然后根據位置構建set possible = whole - x_set - y_set - block_set
下面補充下big_small和small_big兩個字典
3x3的小區共9個 分別編號上0-8
0 | 1 | 2
3 | 4 | 5
6 | 7 | 8
原來在9x9的 x行y列 對應過去 就會在x//3行y//3列
對應編號就是x//3*3 + y//3
為了方便后面的使用,建立一個字典
big_small = { (x,y): (x//3)*3+(y//3) for x in range(9) for y in range(9)} ''' --------------------------- >>> big_small[(5,5)] 4 '''
這個字典是{位置:區號}
然后反着來一下
就可以根據 區號 查包含位置的字典(這才是重點)
big_small = { (x,y): (x//3)*3+(y//3) for x in range(9) for y in range(9)} small_big = { x:[ ] for x in range(9)} for i , j in big_small.items(): small_big[ j ].append(i) for i,j in small_big.items(): print(i,'-->',j) ''' ---------------------------------------------------- 0 --> [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] 1 --> [(0, 3), (0, 4), (0, 5), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)] 2 --> [(0, 6), (0, 7), (0, 8), (1, 6), (1, 7), (1, 8), (2, 6), (2, 7), (2, 8)] 3 --> [(3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (5, 0), (5, 1), (5, 2)] 4 --> [(3, 3), (3, 4), (3, 5), (4, 3), (4, 4), (4, 5), (5, 3), (5, 4), (5, 5)] 5 --> [(3, 6), (3, 7), (3, 8), (4, 6), (4, 7), (4, 8), (5, 6), (5, 7), (5, 8)] 6 --> [(6, 0), (6, 1), (6, 2), (7, 0), (7, 1), (7, 2), (8, 0), (8, 1), (8, 2)] 7 --> [(6, 3), (6, 4), (6, 5), (7, 3), (7, 4), (7, 5), (8, 3), (8, 4), (8, 5)] 8 --> [(6, 6), (6, 7), (6, 8), (7, 6), (7, 7), (7, 8), (8, 6), (8, 7), (8, 8)] '''
下面列下總體框架(???表示還沒確定的細節)
#偽代碼 l = ??? #讀取文件獲得未完成的數獨 all_list = [l] #這個變量用於裝 待處理的數獨 key_list = [] #裝正確的解 while all_list: one = all_list.pop() #從list末尾取出一個進行處理 x, y, ed = ???(one) #這個函數找一個未填寫的格子(值為0) #x,y將接受格子的位置, #ed接受一個邏輯值,以處理格子全被填滿的特殊情況 if ed : key_list.append(one) for i in one: print(i) #輸出,保存解 continue possible = ???(x,y,one) #獲取格子可能的數字 for i in possible: new_one = copy.deepcopy(one) #深度拷貝one new_one[x][y] = i all_list.append(new_one) #修改副本,並加入待處理list
只有一個函數不夠清晰,
def output_cell(l): for i in range(9): for j in range(9): if l[ i ][ j ] : pass else: return i , j ,False else: return None, None,True
全部的代碼,
import copy def new_int(s): return int(s) if s else 0 def output_cell(l): for i in range(9): for j in range(9): if l[ i ][ j ] : pass else: return i , j ,False else: return None, None,True def possible_num(x,y,l): whole = {1,2,3,4,5,6,7,8,9} x_set = set(l[x]) y_set = { l[i][y] for i in range(9) } block_num = big_small[(x,y)] block_set = { l[i][j] for i , j in small_big[block_num] } possible = whole - x_set - y_set - block_set return possible big_small = { (x,y): (x//3)*3+(y//3) for x in range(9) for y in range(9)} small_big = { x:[ ] for x in range(9)} for i , j in big_small.items(): small_big[ j ].append(i) l = None with open('數獨.csv','r',encoding = 'utf-8') as f: l = f.readlines() l[0] = l[0][1:] l = map(lambda i : i.rstrip(),l) l = map(lambda i : i.split(","),l) l = [ list(map(new_int, i)) for i in l] all_list = [l] key_list = [] while all_list: one = all_list.pop() x, y, ed = output_cell(one) if ed : key_list.append(one) for i in one: print(i) continue possible = possible_num(x,y,one) for i in possible: new_one = copy.deepcopy(one) new_one[x][y] = i all_list.append(new_one) else: print('遍歷結束') print(len_num)
運行結果
''' --------------------------- [3, 7, 5, 1, 6, 8, 4, 9, 2] [8, 6, 1, 4, 9, 2, 7, 3, 5] [4, 2, 9, 7, 3, 5, 1, 6, 8] [1, 5, 8, 6, 2, 9, 3, 7, 4] [7, 3, 4, 8, 5, 1, 6, 2, 9] [6, 9, 2, 3, 7, 4, 5, 8, 1] [5, 8, 3, 9, 1, 6, 2, 4, 7] [2, 4, 7, 5, 8, 3, 9, 1, 6] [9, 1, 6, 2, 4, 7, 8, 5, 3] '''
#