最難數獨的快速解法 - python

非夢nj發表於2018-07-21

轉載於: www.jianshu.com/p/1b2ee6539…

  • python3、numpy的學習
  • 遞迴演算法

世界最難數獨

image.png
芬蘭數學家因卡拉,花費3個月時間設計出了世界上迄今難度最大的數獨遊戲,而且它只有一個答案。因卡拉說只有思考能力最快、頭腦最聰明的人才能破解這個遊戲

數獨解法

有很多,這裡練習用排除+遞迴回溯法。

  1. 排除法很直觀
  • 根據已知的數字,排除同一行、同一列、同一九宮格內相同的數字
  • 同一九宮格內,如果存在某一行/列上猜測有兩個同一數字,那大數獨同一行/列也能排除
  • 新解出的數字,加入到先進先出佇列(棧) - FIFO Queue
  1. 猜測+回溯法
  • 如果已經沒有任何已知的數字,那就只能猜測了
  • 把猜測數字加入到後進先出(LastInFirstOut)佇列 - LIFO Queue
  • 遞迴寫法,畫流程圖會比較容易理解!!

image.png

根據流程圖寫程式碼,會很輕鬆,邏輯也不會亂:

    def sudo_solve_iter(self):
        # 排除法解題
        self.sudo_exclude()
        # logger.debug(f'excluded, current result:\n{self.value}')
        if self.verify_value():
            if self.get_num_count() == 81:
                # solve success
                return
            else:
                logger.info(f'current no. of fixed answers: {self.get_num_count()}')
                point = self.get_best_point()
                index = 0
                items = self.add_to_queue(point, index)
                logger.info(f'add to LIFO queue and guessing {items[index]}/{items}: '
                             f'{[x.point for x in self.record_queue.queue]}')
                self.guess_times += 1
                return self.sudo_solve_iter()
        while True:
            if self.record_queue.empty():
                # raise Exception('Sudo is wrong, no answer!')
                logger.error(f'Guessed {self.guess_times} times. Sudo is wrong, no answer!')
                exit()
            # check value ERROR, need to try next index or rollback
            record = self.record_queue.get()
            point = record.point
            index = record.point_index + 1
            items = record.value[point]
            self.value = record.value
            logger.info(f'Recall! Pop previous point, {items} @{point}')
            # 判斷索引是否超出範圍
            # if not exceed,則再回溯一次
            if index < len(items):
                items = self.add_to_queue(point, index)
                logger.info(f'guessing next index: answer={items[index]}/{items} @{point}')
                self.guess_times += 1
                return self.sudo_solve_iter()
複製程式碼

實戰

最難數獨,需要猜測109次?!確實很變態啊!不過就算i3級別的舊電腦,也只要0.3s左右。

/home/kevinqq/git/sudo-py3/venv/bin/python /home/kevinqq/git/sudo-py3/sudo-recur.py
DEBUG:__main__:current no. of fixed answers: 21
DEBUG:__main__:add to LIFO queue and guessing 3/[3, 9]: [(7, 6)]
DEBUG:__main__:current no. of fixed answers: 22
DEBUG:__main__:add to LIFO queue and guessing 5/[5, 9]: [(7, 6), (6, 6)]
DEBUG:__main__:current no. of fixed answers: 25
DEBUG:__main__:add to LIFO queue and guessing 6/[6, 8, 9]: [(7, 6), (6, 6), (5, 6)]
DEBUG:__main__:verify failed. dup in col 0
DEBUG:__main__:Recall! Pop previous point, [6, 8, 9] @(5, 6)
DEBUG:__main__:guessing next index: answer=8/[6, 8, 9] @(5, 6)
DEBUG:__main__:current no. of fixed answers: 29
DEBUG:__main__:add to LIFO queue and guessing 2/[2, 4, 6]: [(7, 6), (6, 6), (5, 6), (5, 1)]
DEBUG:__main__:verify failed. dup in col 0
...
DEBUG:__main__:guessing next index: answer=3/[2, 3, 8] @(4, 3)
DEBUG:__main__:current no. of fixed answers: 41
DEBUG:__main__:add to LIFO queue and guessing 1/[1, 4]: [(7, 6), (6, 6), (6, 1), (6, 3), (4, 3), (1, 1)]
DEBUG:__main__:verify failed. dup in row 0
DEBUG:__main__:Recall! Pop previous point, [1, 4] @(1, 1)
DEBUG:__main__:guessing next index: answer=4/[1, 4] @(1, 1)
DEBUG:__main__:current no. of fixed answers: 42
DEBUG:__main__:add to LIFO queue and guessing 1/[1, 6, 8]: [(7, 6), (6, 6), (6, 1), (6, 3), (4, 3), (1, 1), (4, 1)]
DEBUG:__main__:verify failed. dup in row 4
DEBUG:__main__:Recall! Pop previous point, [1, 6, 8] @(4, 1)
DEBUG:__main__:guessing next index: answer=6/[1, 6, 8] @(4, 1)
DEBUG:__main__:verify failed. dup in row 0
DEBUG:__main__:Recall! Pop previous point, [1, 6, 8] @(4, 1)
DEBUG:__main__:guessing next index: answer=8/[1, 6, 8] @(4, 1)
DEBUG:__main__:verify failed. dup in row 0
DEBUG:__main__:Recall! Pop previous point, [1, 6, 8] @(4, 1)
DEBUG:__main__:Recall! Pop previous point, [1, 4] @(1, 1)
DEBUG:__main__:Recall! Pop previous point, [2, 3, 8] @(4, 3)
DEBUG:__main__:guessing next index: answer=8/[2, 3, 8] @(4, 3)
DEBUG:__main__:current no. of fixed answers: 33
DEBUG:__main__:add to LIFO queue and guessing 2/[2, 3]: [(7, 6), (6, 6), (6, 1), (6, 3), (4, 3), (3, 3)]
DEBUG:__main__:current no. of fixed answers: 42
DEBUG:__main__:add to LIFO queue and guessing 3/[3, 4]: [(7, 6), (6, 6), (6, 1), (6, 3), (4, 3), (3, 3), (7, 1)]
DEBUG:__main__:current no. of fixed answers: 45
DEBUG:__main__:add to LIFO queue and guessing 1/[1, 6]: [(7, 6), (6, 6), (6, 1), (6, 3), (4, 3), (3, 3), (7, 1), (4, 1)]
DEBUG:__main__:verify failed. dup in row 0
DEBUG:__main__:Recall! Pop previous point, [1, 6] @(4, 1)
DEBUG:__main__:guessing next index: answer=6/[1, 6] @(4, 1)
INFO:__main__:Done! guessed 109 times, in 0.540sec
INFO:__main__:Puzzle:
[[8 0 0 0 0 0 0 0 0]
 [0 0 3 6 0 0 0 0 0]
 [0 7 0 0 9 0 2 0 0]
 [0 5 0 0 0 7 0 0 0]
 [0 0 0 0 4 5 7 0 0]
 [0 0 0 1 0 0 0 3 0]
 [0 0 1 0 0 0 0 6 8]
 [0 0 8 5 0 0 0 1 0]
 [0 9 0 0 0 0 4 0 0]]
INFO:__main__:Answer:
[[8 1 2 7 5 3 6 4 9]
 [9 4 3 6 8 2 1 7 5]
 [6 7 5 4 9 1 2 8 3]
 [1 5 4 2 3 7 8 9 6]
 [3 6 9 8 4 5 7 2 1]
 [2 8 7 1 6 9 5 3 4]
 [5 2 1 9 7 4 3 6 8]
 [4 3 8 5 2 6 9 1 7]
 [7 9 6 3 1 8 4 5 2]]
複製程式碼

原始碼:github.com/kevinqqnj/s…

參考:Python秒解最難數獨 - 楊仕航的部落格 http://yshblog.com/blog/74

預告1:會寫一個小網站,秒解任何你輸入的數獨題目 mysudo.herokuapp.com/

image.png

  • 預告2:新增掃一掃功能,人工智慧識別拍攝的數獨題目,Opencv抓圖 + CNN卷積網路識別。
  • 預告3:整合到微信小程式

相關文章