新型面试题-数独解法

数独sudoku

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
类似的
https://www.cnblogs.com/ansver/p/9034095.html
python穷举法解数独
总体思路 :
数独九行九列,一个list装一行,也就需要一个嵌套两层的list
初始会有很多数字,我可不想一个一个赋值
那就要想办法偷懒啦
然后再是穷举,如何科学的穷举

可破解中等难度数独(耗时约50~200ms),但对于超难数独不能短时破解,算法有待改善。

默认赠送一个数独.游戏模式选择“1”时,可修改此数独进行游戏(数独空处请填0)。

工具

anaconda-Jupyter Notebook

mac终端运行 /anaconda3/bin/jupyter_mac.command

jupyter notebook 这个不行

一、jupyter notebook里ipynb文件转为py文件
  法一:
  在xxx.ipynb所在目录下,打开终端,并输入命令:
          jupyter nbconvert --to script xxx.ipynb 
        其中xxx.ipynb是要转换文件的名字,转换后在该目录下出现xxx.py文件。

  法二:
  启动Jupyter notebook
  在网页下找打ipynb文件,然后
  选择file--download as--python file

二、jupyter notebook 加载py文件(即转为ipynb文件)
        In [ ]:%run xxx.py
          加载了 xxx.py文件,相当于导包。
        In [ ]:%load xxx.py
          把 xxx.py的代码显示出来。
          
  这句代表是王八github上       
# Node.js JavaScript runtime :sparkles::turtle::rocket::sparkles:

数独.py

https://github.com/fibncci/fbCode/blob/master/suduku.ipynb

default_data = [[0,0,8,0,4,0,0,0,0],[0,0,0,0,0,9,0,8,2],[0,0,0,5,0,6,7,9,1],[5,4,0,0,0,0,0,0,0],[7,0,0,0,0,0,0,0,8],[0,0,0,0,0,0,0,7,3],[3,1,6,2,0,4,0,0,0],[8,5,0,1,0,0,0,0,0],[0,0,0,0,6,0,1,0,0]]


def get_data():  #初始化,输入数独数据data
    data = []
    for n in range(9):
        data.append(list(map(int, input('Enter {}th line:'.format(n+1)).split())))
    print('Your data:')
    print_sudoku(data)
    return data

def interface():  #提高用户体验
    print('***Method 1: Modify the default sudoku***')
    print('***Method 2: Through input method***\n')
    while True:
        choice = input('Please select the data input method:1 or 2\n')
        if choice == '1':
            data = default_data
            break
        elif choice == '2':
            while True:
                data = get_data()
                check = input('Confirm? Y or N\n')
                if check == 'Y' or check == 'y':
                    break
            break
        else:
            print('Error, chooce again~')
    return data

def print_sudoku(data): #打印最终数独破解结果
    for i in range(9):
        for j in range(9):
            print('{:^3}'.format(data[i][j]),end='')
        print('')

def build_data_list(data): #初始化,未每个空位建立备选数字列表
    data_list = []
    for y in range(9):
        for x in range(9):
            if data[y][x] == 0:
                data_list.append([(x, y), [1, 2, 3, 4, 5, 6, 7, 8, 9]])
    return data_list

def judge(data, x, y, num): #关键函数一,判断数字是否重复,是否允许填入
    if data[y].count(num) > 0: #行判断
        #print('error1')
        return False

    for col in range(9): #列判断
        if data[col][x] == num:
            #print('error2')
            return False

    for a in range(3): #九宫格判断 求整数,让计算机认识
        for b in range(3):
            if data[a+3*(y//3)][b+3*(x//3)] == num:
                #print('error3')
                return False
    return True

def data_list_filter(data, data_list, start):
    for blank_index in range(start, len(data_list)):
        data_list[blank_index][1] = []
        for num in range(1,10):
            if judge(data, data_list[blank_index][0][0], data_list[blank_index][0][1], num):
                data_list[blank_index][1].append(num)
    return data_list

def fill_num(data, data_list, start):  
    #关键函数二,对有多个备选数字的位置循环猜数字。
    #类似深度优先遍历算法,一旦某位置的数字judge为True,
    #则允许开始下一位置的猜测;若某位置为False,则忽略。
    if start < len(data_list):
        one = data_list[start]
        for num in one[1]:
            if judge(data, one[0][0], one[0][1], num):
                data[one[0][1]][one[0][0]] = num
                tem_data = fill_num(data, data_list, start+1)
                if tem_data != None:
                    return tem_data
        data[one[0][1]][one[0][0]] = 0  
        #有可能再往后猜了好几步后才发现前面错误,此时需要将过程中的所有赋值操作清零。
    else:
        return data

def main(): #主函数
    try:
        data = interface()
        data_list = data_list_filter(data, build_data_list(data), 0)
        newdata = fill_num(data, data_list, 0)
        print('Answer:')
        print_sudoku(newdata)
    except:
        print('Error occurred! please check your data~')

main()


例1

# default_data = [[0,0,8,0,4,0,0,0,0],
#                 [0,0,0,0,0,9,0,8,2],
#                 [0,0,0,5,0,6,7,9,1],
#                 [5,4,0,0,0,0,0,0,0],
#                 [7,0,0,0,0,0,0,0,8],
#                 [0,0,0,0,0,0,0,7,3],
#                 [3,1,6,2,0,4,0,0,0],
#                 [8,5,0,1,0,0,0,0,0],
#                 [0,0,0,0,6,0,1,0,0]]


答案:
***Method 1: Modify the default sudoku***
***Method 2: Through input method***

Please select the data input method:1 or 2
1
Answer:
 1  9  8  7  4  2  3  6  5 
 6  7  5  3  1  9  4  8  2 
 4  3  2  5  8  6  7  9  1 
 5  4  3  6  7  8  2  1  9 
 7  6  1  9  2  3  5  4  8 
 2  8  9  4  5  1  6  7  3 
 3  1  6  2  9  4  8  5  7 
 8  5  4  1  3  7  9  2  6 
 9  2  7  8  6  5  1  3  4

例二:


# default_data = [[1 ,0 ,6 ,2 ,0 ,0 ,0 ,0 ,0 ],  
#                 [0 ,0 ,0 ,4 ,0 ,0 ,8 ,2 ,0 ],
#                 [2 ,0 ,0 ,0 ,0 ,5 ,0 ,0 ,0 ],
#                 [0 ,8 ,0 ,0 ,4 ,0 ,0 ,0 ,7 ],
#                 [0 ,0 ,0 ,6 ,0 ,3 ,0 ,0 ,0 ],
#                 [5 ,0 ,0 ,0 ,1 ,0 ,0 ,4 ,0 ],
#                 [0 ,0 ,0 ,9 ,0 ,0 ,0 ,0 ,0 ],
#                 [0 ,3 ,9 ,0 ,0 ,4 ,0 ,0 ,0 ],
#                 [0 ,0 ,0 ,0 ,0 ,2 ,9 ,0 ,5 ]]

Answer:
 1  4  6  2  7  8  5  9  3 
 7  5  3  4  9  6  8  2  1 
 2  9  8  1  3  5  4  7  6 
 3  8  1  5  4  9  2  6  7 
 9  7  4  6  2  3  1  5  8 
 5  6  2  8  1  7  3  4  9 
 6  2  5  9  8  1  7  3  4 
 8  3  9  7  5  4  6  1  2 
 4  1  7  3  6  2  9  8  5 

例三

default_data = [[0,0,9,0,4,7,0,0,0],[0,0,0,0,0,0,1,0,6],[0,8,0,0,2,0,0,0,0],[8,0,1,0,0,3,0,0,0],[0,7,3,0,0,0,0,0,0],[0,0,0,0,0,0,0,5,4],[0,0,0,2,0,0,0,0,1],[3,0,0,0,0,9,0,7,0],[0,9,0,8,0,6,0,4,0],[],[]]


Answer:
 2  1  9  6  4  7  5  8  3 
 7  3  4  9  8  5  1  2  6 
 6  8  5  3  2  1  4  9  7 
 8  5  1  4  9  3  7  6  2 
 4  7  3  5  6  2  8  1  9 
 9  2  6  7  1  8  3  5  4 
 5  6  8  2  7  4  9  3  1 
 3  4  2  1  5  9  6  7  8 
 1  9  7  8  3  6  2  4  5 

例四

***Method 1: Modify the default sudoku***
***Method 2: Through input method***

Please select the data input method:1 or 2
2
Enter 1th line: 1 0 6 2 0 0 0 0 0
Enter 2th line: 0 0 0 4 0 0 8 2 0
Enter 3th line: 2 0 0 0 0 5 0 0 0
Enter 4th line: 0 8 0 0 4 0 0 0 7
Enter 5th line: 0 0 0 6 0 3 0 0 0
Enter 6th line: 5 0 0 0 1 0 0 4 0
Enter 7th line: 0 0 0 9 0 0 0 0 0
Enter 8th line: 0 3 9 0 0 4 0 0 0
Enter 9th line: 0 0 0 0 0 2 9 0 5

Your data:
 1  0  6  2  0  0  0  0  0 
 0  0  0  4  0  0  8  2  0 
 2  0  0  0  0  5  0  0  0 
 0  8  0  0  4  0  0  0  7 
 0  0  0  6  0  3  0  0  0 
 5  0  0  0  1  0  0  4  0 
 0  0  0  9  0  0  0  0  0 
 0  3  9  0  0  4  0  0  0 
 0  0  0  0  0  2  9  0  5 
Confirm? Y or N

例五

选择2 ,空格随便打 。

***Method 1: Modify the default sudoku***
***Method 2: Through input method***

Please select the data input method:1 or 2
2
Enter 1th line:5 3 0 0 7 0  0 0 0
Enter 2th line:6 0 0 1 9 5 0 0 0
Enter 3th line:0 9 8 0 0 0 0 6 0
Enter 4th line:8 0 0 0 6 0 0 0 3
Enter 5th line:4 0 0 8 0 3 0 0 1
Enter 6th line:7 0 0 0 2 0 0 0 6
Enter 7th line:0 6 0 0 0 0 2 8 0
Enter 8th line:0 0 0 4 1 9 0 0 5
Enter 9th line:0 0 0 0 8 0 0 7 9
Your data:
 5  3  0  0  7  0  0  0  0 
 6  0  0  1  9  5  0  0  0 
 0  9  8  0  0  0  0  6  0 
 8  0  0  0  6  0  0  0  3 
 4  0  0  8  0  3  0  0  1 
 7  0  0  0  2  0  0  0  6 
 0  6  0  0  0  0  2  8  0 
 0  0  0  4  1  9  0  0  5 
 0  0  0  0  8  0  0  7  9 
Confirm? Y or N
Y
Answer:
 5  3  4  6  7  8  9  1  2 
 6  7  2  1  9  5  3  4  8 
 1  9  8  3  4  2  5  6  7 
 8  5  9  7  6  1  4  2  3 
 4  2  6  8  5  3  7  9  1 
 7  1  3  9  2  4  8  5  6 
 9  6  1  5  3  7  2  8  4 
 2  8  7  4  1  9  6  3  5 
 3  4  5  2  8  6  1  7  9 

例6

***Method 1: Modify the default sudoku***
***Method 2: Through input method***

Please select the data input method1 or 2
1
Answer:
 1  2  3  4  5  6  7  8  9 
 4  6  5  7  8  9  3  1  2 
 9  7  8  3  1  2  4  5  6 
 5  3  2  8  4  7  9  6  1 
 7  9  1  6  3  5  2  4  8 
 6  8  4  9  2  1  5  7  3 
 3  1  6  2  7  4  8  9  5 
 8  5  7  1  9  3  6  2  4 
 2  4  9  5  6  8  1  3  7 


default_data = [[0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,8],
                [0,0,0,0,0,0,0,7,3],
                [3,1,6,2,0,4,0,0,0],
                [8,5,0,1,0,0,0,0,0],
                [0,0,0,0,6,0,1,0,0]]

10s



default_data = [[0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,8],
                [0,0,0,0,0,0,0,7,3],
                [3,1,6,2,0,4,0,0,0],
                [8,5,0,1,0,0,0,0,0],
                [0,0,0,0,6,0,1,0,0]]
Answer:
 1  2  3  4  5  6  7  8  9 
 4  6  5  7  8  9  2  1  3 
 7  8  9  3  1  2  4  5  6 
 2  3  1  5  4  7  6  9  8 
 5  9  7  6  2  8  3  4  1 
 6  4  8  9  3  1  5  2  7 
 3  1  6  2  9  4  8  7  5 
 8  5  2  1  7  3  9  6  4 
 9  7  4  8  6  5  1  3  2 


default_data = [[0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [8,5,0,1,0,0,0,0,0],
                [0,0,0,0,6,0,1,0,0]]


Answer:
 1  2  3  4  5  6  7  8  9 
 4  6  5  7  8  9  2  1  3 
 7  8  9  3  1  2  4  5  6 
 2  3  1  5  4  7  6  9  8 
 5  9  7  6  2  8  3  4  1 
 6  4  8  9  3  1  5  2  7 
 3  1  6  2  9  4  8  7  5 
 8  5  2  1  7  3  9  6  4 
 9  7  4  8  6  5  1  3  2 


default_data = [[0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,6,0,1,0,0]]
Answer:
 1  2  3  4  5  6  7  8  9 
 4  5  6  7  8  9  2  1  3 
 7  8  9  1  2  3  4  5  6 
 2  1  4  3  7  5  6  9  8 
 3  6  7  2  9  8  5  4  1 
 5  9  8  6  1  4  3  2  7 
 6  3  1  8  4  2  9  7  5 
 9  7  2  5  3  1  8  6  4 
 8  4  5  9  6  7  1  3  2 


default_data = [[0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,0,0,0],
                [0,0,0,0,0,0,1,0,0]]

Answer:
 1  2  3  4  5  6  7  8  9 
 4  5  6  7  8  9  2  1  3 
 7  8  9  1  2  3  4  5  6 
 2  1  4  3  6  5  8  9  7 
 3  6  7  8  9  1  5  2  4 
 5  9  8  2  4  7  3  6  1 
 6  3  1  5  7  2  9  4  8 
 8  7  2  9  1  4  6  3  5 
 9  4  5  6  3  8  1  7  2 



--------------------------------------------------

Your data:
 6  0  9  7  5  1  0  0  0 
 0  0  0  0  0  3  0  0  4 
 0  0  0  0  0  0  0  0  0 
 2  0  0  0  0  0  0  8  0 
 0  0  8  0  6  7  0  0  0 
 0  0  0  0  0  9  1  5  6 
 1  5  0  2  0  0  4  0  0 
 0  0  0  0  0  0  0  0  5 
 7  0  0  0  0  0  0  6  0 
Confirm? Y or N
Y
Answer:
 6  4  9  7  5  1  2  3  8 
 5  2  1  9  8  3  6  7  4 
 3  8  7  6  4  2  5  1  9 
 2  6  5  1  3  4  9  8  7 
 9  1  8  5  6  7  3  4  2 
 4  7  3  8  2  9  1  5  6 
 1  5  6  2  7  8  4  9  3 
 8  9  4  3  1  6  7  2  5 
 7  3  2  4  9  5  8  6  1 

数独界面

运行之后电脑自动重启

简单学习了tkinter库,做了GUI. 注意哦,我还没有加入错误判断语句,以及对于超难的数独解题速度极慢,

总之碰到这两种情况很可能会卡死、无响应,默默关掉重来就好…

数独gui.py

import tkinter as tk

def isValidSudoku(board):   #判断Sudoku数据是否有效
    for y in range(9):
        for x in range(9):
            if board[y][x] != 0:
                if board[y].count(board[y][x]) > 1:
                    return False

            for col in range(9):
                if board[y][x] != 0 and col != y:
                    if board[col][x] == board[y][x]:
                        return False

            for i in range(3):
                for j in range(3):
                    if board[y][x] != 0 and (i+3*(y//3), j+3*(x//3)) != (y, x):
                        if board[i+3*(y//3)][j+3*(x//3)] == board[y][x]:
                            return False
    return True

def get_sudoku():
    window = tk.Tk()
    window.title('Solve a Sudoku')
    window.geometry('600x450')

    width = 3
    height = 1
    labels = []
    entrys = []
    sudoku = [[0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0]]

    for i in range(81):
        entrys.append(tk.Entry(window, width=width))   #初始化entrys
        labels.append(tk.Label(window, width=3, height=1, bg='yellow'))   #初始化labels

    l1 = tk.Label(window, text='How to solve a SUDOKU?', bg='yellow', font=('Arial',15))   #设计标题
    l1.place(x='170', y='15')
    b1 = tk.Button(window, text='Just click here', bg='red', font=('Arial',12), command=lambda: get_data())   #设计按钮
    b1.place(x='230', y='320')

    for e in entrys:
        e.place(x=str(entrys.index(e)%9*28+entrys.index(e)//3%3*6+20),
                y=str(entrys.index(e)//9*24+entrys.index(e)//27*6+70))   #GUI中用来将数独矩阵分割成9个九宫格

    def get_data(): #获取数独矩阵的值
        for e in entrys:
            sudoku[entrys.index(e)//9][entrys.index(e)%9] = int(e.get()) if e.get() != '' else 0
        if isValidSudoku(sudoku):   #判断输入的Sudoku是否有效
            data = sudoku   #将输入的数独代入算法中计算
            data_list = data_list_filter(data, build_data_list(data), 0)   #针对Sudoku中的每一个空格子,都算出其可能的备选数字,存入data_list中;每当空格被确认唯一值时,剩余data_list都需要再被刷新
            newdata = fill_num(data, data_list, 0)   #计算得到完整数独newdata
            print_sudoku(newdata)   #程序输出数独newdata
            for l in labels:
                labels[labels.index(l)]['text']= newdata[labels.index(l)//9][labels.index(l)%9]   #将完整数独的值代入label中
                l.place(x=str(labels.index(l)%9*28+labels.index(l)//3%3*6+300),
                        y=str(labels.index(l)//9*24+labels.index(l)//27*6+70))   #用labels将数独输出到GUI界面
        else:
            pass

    window.mainloop()

def get_data():  #初始化,用来手动输入数独数据data
    data = []
    for n in range(9):
        data.append(list(map(int, input('Enter {}th line:'.format(n+1)).split())))
    print('Your data:')
    print_sudoku(data)
    return data

def print_sudoku(data): #打印最终数独破解结果
    for i in range(9):
        for j in range(9):
            print('{:^3}'.format(data[i][j]),end='')
        print('')
    print('')

def build_data_list(data): #初始化,未每个空位建立备选数字列表data_list
    data_list = []
    for y in range(9):
        for x in range(9):
            if data[y][x] == 0:
                data_list.append([(x, y), [1, 2, 3, 4, 5, 6, 7, 8, 9]])
    return data_list

def judge(data, x, y, num): #关键函数一,判断数字是否重复,是否允许填入
    if data[y].count(num) > 0: #行判断
        #print('error1')
        return False

    for col in range(9): #列判断
        if data[col][x] == num:
            #print('error2')
            return False

    for a in range(3): #九宫格判断
        for b in range(3):
            if data[a+3*(y//3)][b+3*(x//3)] == num:
                #print('error3')
                return False
    return True

def data_list_filter(data, data_list, start):    #用来再次刷新备选数字
    for blank_index in range(start, len(data_list)):
        data_list[blank_index][1] = []
        for num in range(1,10):
            if judge(data, data_list[blank_index][0][0], data_list[blank_index][0][1], num):
                data_list[blank_index][1].append(num)
    return data_list

def fill_num(data, data_list, start):  #关键函数二,对具有多个备选数字的位置依次尝试。类似深度优先遍历算法,一旦某位置的数字judge为True,则允许开始下一位置的猜测;若某位置为False,则忽略。
    if start < len(data_list):
        one = data_list[start]
        for num in one[1]:
            if judge(data, one[0][0], one[0][1], num):
                data[one[0][1]][one[0][0]] = num
                tem_data = fill_num(data, data_list, start+1)
                if tem_data != None:
                    return tem_data
        data[one[0][1]][one[0][0]] = 0  #有可能再往后猜了好几步后才发现前面错误,此时需要将过程中的所有赋值操作清零。
    else:
        return data

def main(): #主函数
    try:
        get_sudoku()
    except:
        print('Error occurred! please check your data~')

main()

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦