求解数独难题, Sudoku问题(回溯)
Introduction :

标准的数独游戏是在一个 9 X 9 的棋盘上填写 1 – 9 这 9 个数字,规则是这样的:
- 棋盘分成上图所示的 9 个区域(不同颜色做背景标出,每个区域是 3 X 3 的子棋盘),在每个子棋盘中填充 1 – 9 且不允许重复 ,下面简称块重复
- 每一行不许有重复值 ,下面简称行重复
- 每一列不许有重复值 ,下面简称列重复
如上红色框出的子区域中的亮黄色格子只能填 8。
Goals :
- 随机生成数独问题,如果做成一个数独小游戏的话可以按游戏难易程度生成,比如有简单、中等、困难三个程度
- 求解数独问题,根据生成的数独问题或者用户输入的数独问题求解出所有答案或者求解出不超过MAX个答案,因为某个问题可能含有上十万个解,用MAX约束找到MAX个答案后就不再找其它的答案
Idea :
有一种求解数独问题的方案是“候选数字法”,就是在待填充的格子中填写不会造成行重复、列重复、块重复的数字,有的时候存在多个这样的数字,那么我们可以随机选取一个,如果待填充的格子中填写任何一个数字都会造成某种重复的发生,则说明这个问题没有解,也就是这不是一个数独问题。如上图中红色框出区域的下面一个区域的红色格子中可以填写的数字为 4、8、9,那么它的候选数字就是4、8、9,你可以随机选一个填入。当所有的格子填充完后数独问题就解决了。
根据上述的这种候选数字法,我们可以用它来生成数独问题以及求解数独问题。
关于生成数独问题:
循环遍历 9 X 9 的数独格子,在遍历到的当前格子的候选数字中随机选取一个数字填入,如果当前格子没有了候选数字则清空所有已经填好的格子,重新再来。这是一个最简单的也是最容易理解的概率方法了。伪码如下:
1: int[][] sudoku = new int[9][9]; //数独棋盘2: while(true) {3: label:4: clearSudoku(); //清空所有已填数字, 每个格子置 05: for(int row = 0; row < 9; row++) {6: for(int col = 0; col < 9; col++) {7: //getCandidates(row, col) 获取当前格子的候选数字8: //randomCandidate() 随机选取一个候选数字9: if(getCandidates(row, col) != NULL) //如果有候选数字10: sudoku[row][col] = randomCandidate(getCandidates(row, col));11: else12: goto label; //跳转到label那里清空格子重新来过13: }14: }15: }
关于求解数独问题:
同上, 不过这次不是采用随机算法,因为随机算法没法保证把所有的解都求出。利用回溯法把所有可能的情况都遍历,那么就可以求出所有的解。我们考虑下面的一个实例:
初始时,sudoku[0][0]格子的候选数字为2, 3,5, 6,如图标出,sudoku[4][0]的为2,4,5,其它如图。那么我们在填充sudoku[0][0]的时候不能按照随机来了,因为我们需要把所有的情况遍历的话,需要按一定顺序来,也就是从2开始来,因为一旦某个格子填充了一个候选数字后,其它格子的候选数字会发生变化,假设sudoku[0][0]填充了2,那么与sudoku[0][0]在同一行、同一列、同一块内的格子的候选数字就不能再有2了,sudoku[4][0]、sudoku[6][0]等都应该将原来含有的2给删掉。
我们遍历着填写候选数字,当遇到某个格子的候选数字不存在的时候,我们应该回溯了,我们回到上一格,填写另一个候选数字。比如上图,我们按照这样的顺序来:
- sudoku[0][0]填写2,sudoku[4][0]候选为(4,5),sudoku[6][0]候选为(3, 5),sudoku[7][0]候选为(4, 5, 6),sudoku[8][0]候选为(5, 6);
- sudoku[4][0]填写4,sudoku[6][0]候选为(3, 5),sudoku[7][0]候选为(5, 6),sudoku[8][0]候选为(5, 6);
- sudoku[6][0]填写5,sudoku[7][0]候选为(6),sudoku[8][0]候选为(6);
- 那么sudoku[7][0]填了6之后sudoku[8][0]就没有候选数字可填了
至于如何遍历所有的情况,我们可以这样来:
把候选数字都按大小从小到大排序,用一个栈记录已经选取过的候选数字在这个序列中的序号,每一次选取候选数字就入栈,每一次回溯就出栈。另外需要注意的是每一次改变格子数字的操作都需要更新候选数字。伪码如下(注意,这只是表达思想,代码在如果按执行顺序来是有误的):
1: Stack s;2: int idx;3: while(true) {4: if(candidates[row][col] != NULL) {5: //当前格子填写第idx个候选数字, idx = 0,1,2,3...6: sudoku[row][col] = candidates[row][col](idx);7: //如果所有的格子都填了,那么就把这个解保存起来,然后回溯8: if(allBlanked()) {9: solutions.add();10: backtrace(row, col);11: }12: //idx入栈以记录已经填过的候选数字13: s.push(idx);14: //更新候选数字15: updateCandidates();16: //填写下一行17: next(row, col);18: }19: else {//如果没有了候选数字就回到上一格填过的位置20: backtrace(row, col);21: //回溯到原点就结束22: if(s.isEmpty()) {23: end;24: }25: //获取上一格填写的数字的序号,这次应该填写下一个候选数字了,即序号加126: idx = s.pop() + 1;27: }28: }
转自:http://ggicci.blog.163.com/blog/static/21036409620127741430397/
求解数独难题, Sudoku问题(回溯)的更多相关文章
- [LeetCode] Sudoku Solver 求解数独
Write a program to solve a Sudoku puzzle by filling the empty cells. Empty cells are indicated by th ...
- Leetcode之回溯法专题-37. 解数独(Sudoku Solver)
Leetcode之回溯法专题-37. 解数独(Sudoku Solver) 编写一个程序,通过已填充的空格来解决数独问题. 一个数独的解法需遵循如下规则: 数字 1-9 在每一行只能出现一次.数字 1 ...
- [LeetCode] 37. Sudoku Solver 求解数独
Write a program to solve a Sudoku puzzle by filling the empty cells. A sudoku solution must satisfy ...
- LeetCode 37 Sudoku Solver(求解数独)
题目链接: https://leetcode.com/problems/sudoku-solver/?tab=Description Problem : 解决数独问题,给出一个二维数组,将这个数独 ...
- 算法实践——舞蹈链(Dancing Links)算法求解数独
在“跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题”一文中介绍了舞蹈链(Dancing Links)算法求解精确覆盖问题. 本文介绍该算法的实际运用,利用舞蹈链(Dancin ...
- 转载 - 算法实践——舞蹈链(Dancing Links)算法求解数独
出处:http://www.cnblogs.com/grenet/p/3163550.html 在“跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题”一文中介绍了舞蹈链(Dan ...
- 关于用舞蹈链DLX算法求解数独的解析
欢迎访问——该文出处-博客园-zhouzhendong 去博客园看该文章--传送门 描述 在做DLX算法题中,经常会做到数独类型的题目,那么,如何求解数独类型的题目?其实,学了数独的构建方法,那么DL ...
- [USACO11NOV]二进制数独Binary Sudoku
传送门 这道题是很好的一道IDA*练习题. 首先我们先确定搜索的框架,我们要求的是用最少的修改次数使得所有的行,列,宫之内都有偶数个1,最直观的想法显然是先预处理出有奇数个1的行,列,宫,之后枚举每一 ...
- 搜索 --- 数独求解 POJ 2676 Sudoku
Sudoku Problem's Link: http://poj.org/problem?id=2676 Mean: 略 analyse: 记录所有空位置,判断当前空位置是否可以填某个数,然后直 ...
随机推荐
- Android视频直播:流媒体服务器搭建
一.前言 实时视频直播是这两年非常火的技术形态,已经渗透到教育.在线互娱等各种业务场景中.但要搭建一套实时视频直播系统,并非易事,下面针对直播系统的的搭建做一下说明: 1.1 通常的直播系统的技术模型 ...
- NancyFX 第十二章 通道截拦
所有的好的Web框架都有一套好的通道截拦的机制,Nancy在我看来是处理最好的.那什么是请求通道那?下面的图可能说的比较清楚些: 正如名称中描述的,一个典型的Web请求在到达最终响应前会穿过一定数量的 ...
- NancyFX 第九章 Responses(响应对象)
和内容协商最最为紧密的当属Nancy的Response对象. 在本书的第一张你应该就已经看到过Response对象,之前是使用它的AsFile 方法返回一个简单文件. using Nancy; nam ...
- 小程序首页onLoad为异步,调用app.js中的全局参数的解决方案。
一,先说一下遇到的问题: 在首页,为了携带app.js中一些参数去做请求动作,但是由于异步原因,发现请求时候,参数信息还未获取到但请求已经发出去. 若等app.js的全局参数返回来,再携带着它去做请求 ...
- mysql 数据库基本命令语句
mysql mariadb 客户端连接 mysql -uroot -p; 客户端退出exit 或 \q 显示所有数据库show databases;show schemas; 创建数据库create ...
- 用jquery实现日期控件
用jquery实现的日期控件,代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta ch ...
- delphi GDI 图片压缩代码 据说是位图缩放保持原图视觉效果最好的算法
delphi 图片压缩代码 据说是位图缩放保持原图视觉效果最好的算法 若有更好的,请大神留言我也学习下,感谢! uses WinAPI.GDIPAPI, WinAPI.GDIPOBJ; var Bi ...
- python全栈开发-Day6 字符编码
python全栈开发-Day6 字符编码 一 .了解字符编码的知识储备 一 .计算机基础知识 二 .文本编辑器存取文件的原理(nodepad++,pycharm,word) #1.打开编辑器就打开了启 ...
- paho.mqtt.c打印日志
mqtt中自身就带有日志系统Log.h和Log.c,这些日志文件是在客户端调用MQTTClient_create函数是初始化的,MQTTClient_create源码如下: int MQTTClien ...
- SpringBoot集成redis的key,value序列化的相关问题
使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...
