简单介绍

数独是当下较为流行的数学游戏之一。通常数独由9x9的格子构成,其中将9x9的格子分为9个3x3的区域,称为“宫”(通常宫与宫之间会用较粗的线来分隔)。游戏的目标则是在格子中填满1~9的数字,并且使得数字之间满足一定的约束条件。

对于标准数独,其约数条件如下:

  • 在9x9的格子下,每行,每列不得有重复的数字
  • 每个宫内不得有重复的数字

除了标准数独,还有一系列的升级版本,例如连续数独,对角线数独,窗口数独,杀手数独等等。这里暂时只讨论标准数独的回溯解法。

对于同一个数独题目,存在着有多种解,也可能只有一种解。已知信息和解的数量通常也可以来衡量一个数独题目的难易程度,通常容易的数独给出的已知信息很多,又或者可能拥有很多个解。困难的数独则是给出的已知信息少,而最终解可能只有一个。

下面是关于最简单的破解数独的算法之一 —— 回溯法。回溯法采用深度优先搜索的思想,是常用的一种算法。关于回溯法的经典例子也很多,例如找生成树,0-1背包问题,8皇后问题,走迷宫,全排列问题等等。本文要说的解数独也可以采用回溯法。

本文采用的解数独思想就是最简单的一个个格子去试探,直到试探到把格子填满,既可以检查答案,如果符合则输出。根据这个思想和回溯法的一般步骤,可以得出解数独算法的步骤,此外,这里采用递归的形式。

回溯步骤一 问题的解空间

对于一个数独,每个格子都去试探实际上是一种暴力破解的思想,对于9x9的格子,每个格子可填1~9,如果不做任何限制,问题的解空间树将会是一棵九叉树,这样的搜索空间大小是非常可怕的。

回溯步骤二 确定搜索规则

在讨论解空间后,显然应该意识到,必须确定解空间树的拓展条件来抑制解空间树的指数增长。对于数独来说,搜索规则正好就是数独填数字的规则。对于一个待填格子,选是1~9,在1~9之中,同行同列同宫都出现过的数字就不必让其子节点生成了,从该节点开始,不符合数独规则的搜索结果最终必然不是数独的解。

回溯步骤三 剪枝

搜索规则实质上就是一个剪枝函数,通过剪枝函数提前剔除不可能的解,能够有效地控制解空间树的增长,并且随着搜索的推进,解空间树的增长会越来越缓慢。想象一下当你快要将数独填满的时候,剩下的格子的选择必然是越来越少,甚至能够直接确定某格子只有一个可行的数字。

伪代码描述

backTracking(int index){if(index == 81){格子已经填满,输出结果}//x,y表示格子的坐标x <-- index % 9;y <-- index / 9;if(grid(x, y)的数字为题目的信息){backTracking(index+1);  //题目给出的数字是确定的,没必要搜索}for(i=1; i<=9 ;i++){   if(isLegal(x, y, i)){ //逐个数字试探其合法性grid(x, y) = i;backTracking(index+1);grid(x, y) = 0;}}
}

Java代码

import java.util.Scanner;public class Sudoku {private static int WIDTH = 3;private static int arr[][] = new int[WIDTH * WIDTH][WIDTH * WIDTH];private static boolean q[][] = new boolean[WIDTH * WIDTH][WIDTH * WIDTH];public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int val;for (int i = 0; i < 9; i++) {for (int j = 0; j < 9; j++) {val = scanner.nextInt();if (val != 0)generateQuestion(i, j, val);}}for (int i = 1; i < WIDTH * WIDTH; i++) {fill(0, i);}}private static void fill(int index, int val) {int mWidth = WIDTH * WIDTH;if (index == mWidth * mWidth) {if (!nullCheck()) {printM();
//                System.exit(0);}return;}int x = index % (mWidth);int y = index / (mWidth);if (q[y][x]) {fill(index + 1, val);} else {if (isLegalPos(x, y, val)) {arr[y][x] = val;if (index + 1 == mWidth * mWidth) {fill(index + 1, 1);} elsefor (int i = 1; i <= mWidth; i++) {fill(index + 1, i);}arr[y][x] = 0;}}}private static boolean nullCheck() {for (int j = 0; j < WIDTH; j++)for (int i = 0; i < WIDTH; i++) {if (arr[j][i] == 0)return true;}return false;}private static boolean isLegalPos(int x, int y, int val) {for (int i = 0; i < WIDTH * WIDTH; i++) {if (x != i && arr[y][i] == val)return false;if (y != i && arr[i][x] == val)return false;}int offsetX = (x / WIDTH) * WIDTH;int offsetY = (y / WIDTH) * WIDTH;for (int i = offsetX; i < offsetX + WIDTH; i++)for (int j = offsetY; j < offsetY + WIDTH; j++) {if (!(y == j && x == i) && arr[j][i] == val)return false;}return true;}private static void printM() {int k = WIDTH * WIDTH;for (int i = 0; i < k; i++) {for (int j = 0; j < k - 1; j++) {System.out.printf("%d ", arr[i][j]);}System.out.println(arr[i][k-1]);}}private static void generateQuestion(int y, int x, int val) {arr[y][x] = val;q[y][x] = true;}
}

样例测试(0表示待填)

输入1
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

输出1
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

输入2
0 0 3 0 1 9 0 0 0
9 0 0 8 0 0 0 0 0
0 1 0 6 2 0 0 0 9
0 8 0 0 0 0 0 4 1
5 2 6 0 0 0 9 8 7
4 9 0 0 0 0 0 6 0
6 0 0 0 8 3 0 9 0
0 0 0 0 0 2 0 0 3
0 0 0 4 6 0 8 0 0

输出2
2 6 3 5 1 9 4 7 8
9 7 5 8 3 4 1 2 6
8 1 4 6 2 7 5 3 9
3 8 7 9 5 6 2 4 1
5 2 6 3 4 1 9 8 7
4 9 1 2 7 8 3 6 5
6 5 2 1 8 3 7 9 4
1 4 8 7 9 2 6 5 3
7 3 9 4 6 5 8 1 2

回溯法简单应用--解数独相关推荐

  1. 探讨与研究——动态规划算法、回溯法、分支限界法解0-1背包问题

    一个人终归是要成长的,是要不断历练的,没有人可以安安稳稳一辈子.就算是最有地位最有钱的人也要不断追求.不断历练.不断提升自己. 人的学问少时在不断学习,青年时期不断实践.随着时间推移,到了老年终有所成 ...

  2. 回溯法实例详解(转)

    概念 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就"回溯"返回,尝试别的路径. 回溯法是一种选优搜索法,按选优条件向前 ...

  3. LintCode 802. 数独(回溯)/ LeetCode 37. 解数独

    1. 题目 编写一个程序,通过填充空单元来解决数独难题. 空单元由数字0表示. 你可以认为只有一个唯一的解决方案. LeetCode 37 题类似,把 int 改成 char,注意转换 2. 解题 行 ...

  4. 回溯法求解数独问题(最简单,通俗易懂,附C++代码)

    问题描述:数独是一种运用纸.笔进行演算的逻辑游戏.玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行.每一列.每一个九宫格内的数字均含1-9,不重复 要求:设计算法随机生成不同 ...

  5. 五大经典算法之回溯法

    一.基本概念   回溯法,又称为试探法,按选优条件向前不断搜索,以达到目标.但是当探索到某一步时,如果发现原先选择并不优或达不到目标,就会退回一步重新选择,这种达不到目的就退回再走的算法称为回溯法. ...

  6. 第39级台阶回溯算法c语言,五大经典算法之回溯法 - osc_9ipdey7e的个人空间 - OSCHINA - 中文开源技术交流社区...

    一.基本概念 回溯法,又称为试探法,按选优条件向前不断搜索,以达到目标.但是当探索到某一步时,如果发现原先选择并不优或达不到目标,就会退回一步重新选择,这种达不到目的就退回再走的算法称为回溯法. 与穷 ...

  7. 回溯法遵循深度优先吗_回溯算法(一)

    一.什么是回溯算法 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就"回溯"返回,尝试别的路径.许多复杂的,规模较大的问题 ...

  8. 回溯法基本思想_回溯算法(一)

    一.什么是回溯算法 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就"回溯"返回,尝试别的路径.许多复杂的,规模较大的问题 ...

  9. 算法分析与设计——回溯法实验报告

       算法导论  课程设计 成 绩 题    目:    回 溯 法 学院班级:        1613013         学    号:      16130130216       姓     ...

最新文章

  1. QoS、IPv6、软交换和VoIP技术受质疑
  2. Pandas 4 个小 trick,都很实用!
  3. (背)顺序容器的操作-向容器中插入元素详细总结
  4. 《剑指offer》和为s的连续正数序列
  5. windows下连接db2数据库
  6. java构建protobuf中的map,Protobuf对象作为Maps中的键
  7. Codeforces Round #715 (Div. 1) C. Complete the MST 补图 + 思维 + 最小生成树
  8. php 定时缓存,php定时清理缓存文件的简单示例
  9. Asp.Net服务器控件开发的Grid实现(三)列编辑器
  10. JAVA常见命名规范
  11. iOS 深拷贝、浅拷贝、自定义对象拷贝简介
  12. SQL知识点脑图(一张图总结SQL)
  13. 接口测试工具--SoapUI下载安装教程
  14. Web程序设计(第三版)课后习题答案
  15. 新建 FrameMaker API 时引用目录的设置
  16. 教授专栏11|张处:企业支付政策和信用风险:来自信用违约掉期(CDS)市场的证据
  17. 7所大学提供区块链、加密货币及金融科技相关线上课程
  18. html 表单提交 地址栏 显示=%cc%ed%bc%d3 ,html--表单(示例代码)
  19. 《C语言及程序设计》实践参考——对称点
  20. Linux,ubuntu 格式化移动硬盘

热门文章

  1. 解析冰蓄冷与水蓄冷及应用
  2. 从Excel批量导入数据说到ForkJoin的原理
  3. Vuforia Engine Android开发入门
  4. 信息学奥赛一本通:1199:全排列
  5. mongodb-golang 权限验证报错:SASL authentication step: Authentication failed
  6. html文章整体居中,HTML如何让文字居中?附两种方式
  7. 【数据分享】中国保险统计年鉴(1981-2020)
  8. 分布式之RabbitMQ
  9. asp.net oracle 问号,asp.net 中文部分显示问号
  10. PVC 塑料片BS 476-6 火焰传播性能测定