填制规则

数独的游戏规则很简单:在九个九宫格里,填入 1 到 9 的数字,让每个数字在每个行、列及九宫格里都 只出现一次就可以过关了。

解谜技巧

数独的解谜技巧,可大分为直观法及候选数法两种。

直观法的特性:

  1. 不需任何辅助工具就可应用。所以要玩报章杂志上的数独谜题时,只要有一枝笔就可以开始了。
  2. 从接到数独谜题的那一刻起就可以立即开始解题。
  3. 初学者或没有计算机辅助时的首要解题方法。
  4. 相对而言,能解出的谜题较简单。
  5. 主要的技巧:唯一解法、基础摒除法、区块摒除法、唯余解法、矩形摒除法、单元摒除法。

候选数法的特性:

  1. 需先建立候选数列表,所以要玩报章杂志上的数独谜题时,因篇幅的影响通常格子不会太大,且候选数列表 的建立十分繁琐,所以常需计算机辅助,或使用候选数法的辅助解题用纸。
  2. 需先建立候选数列表,所以从接到数独谜题的那一刻起,需经过一段相当的时间才会出现第 1 个解。
  3. 需使用高阶直观法技巧或有计算机辅助时的首要解题方法。
  4. 相对而言,能解出的谜题较复杂。

编写 Calculate 类

Calculate 类是本算法中最重要的部分,为了便于使用,很多函数和变量都声明为静态的。在类中,我们将完成数独空格的填充和计算。而且 Calculate 类实现了 Runnable 接口:

步骤:

  1. 定义实现 Runnable 接口。
  2. 覆盖 Runnable 接口中的 run 方法,将线程要运行的代码存放在 run 方法中。
  3. 通过 Thread 类建立线程对象。
  4. 将 Runnable 接口的子类对象作为实际参数传递给 Thread 类的构造函数。
  5. 调用 Thread 类的 start 方法开启线程并调用 Runnable 接口子类 run 方法。

先介绍一下 Calculate 类中所有的成员:

class Calculate implements Runnable {// boo用于判断该格是否为空public static boolean[][] boo = new boolean[9][9];//计算指定行的值public static int upRow = 0; //计算指定列值public static int upColumn = 0; //将存储九宫格中的数据public static int[][] b = new int[9][9]; //查找没有填入数值的空格public static void flyBack(boolean[][] judge,int row,int column){}//遍历所有可能的值public static void arrayAdd(ArrayList<Integer> array,TreeSet<Integer> tree){} public static ArrayList<Integer> assume(int row,int column){}//添加每格可能的选项public void run(){} //分析九宫格是否完成public void judge(){}
}

分析:

  • 二维数组 boo 用于判断该格是否为空。如果已经填入了数值,就不用再填了。
  • 二维数据 b 将存储九宫格中的数据。
  • flyBack 函数用于查找没有填入数值的空格。
  • arrayAdd 函数添加新的数值(1~9)到一行中。如果数据已经有了,跳过,没有就继续赋值。
  • assume 主要是判断在同行同列同一个小九宫格内哪些数值沒有被填充,添加备选的数值,就是候选法的思想。
  • run 函数开始运行整个程序,生成最后的结果。

2 编写 flyBack() 函数

public static void flyBack(boolean[][] judge, int row, int column) {// 生成临时变量s,具体下面会介绍int s = column * 9 + row;s--;// 取商的值,实际就是column的值int quotient = s / 9;// 取余数的值,实际是取(row-1)%9int remainder = s % 9;// 判断是否满足条件if (judge[remainder][quotient]) {flyBack(judge, remainder, quotient);} else {// 赋值给upRowupRow = remainder;// 赋值给upColumnupColumn = quotient;}
}

我们来分析一下这段代码:

quotient 是指商,remainder 是指余数,

//此处为伪代码
s=column*9+row
s--=(column*9+row)-1
quotient = s/9= ((column*9+row)-1)/9= column + (row-1)/9 // 因为row-1<9,这里的除法只保留整数部分= column
remainder=s%9= ((column*9+row)-1)%9= (row-1)%9 //column*9能被9整除

分析了之后可以看出,函数的作用是计算同列的上一行元素值。如果它为空,就赋值给 upRow 和 upColumn ,如果依然满足条件,就继续递归。

这段代码看上去很麻烦,实际上就是找还没有填入数值的空格。

3 编写 arrayAdd() 函数

arrayAdd() 填充某行的值,遍历所有可能的值。

public static void arrayAdd(ArrayList<Integer> array, TreeSet<Integer> tree) {// 遍历1~10for (int z = 1; z < 10; z++) {// flag3默认为true,判断z是否符合条件boolean flag3 = true;// it就是一个迭代器Iterator<Integer> it = tree.iterator();// tree如果没有遍历完继续遍历while (it.hasNext()) {// 将列表中的值赋给bint b = it.next().intValue();if (z == b) {flag3 = false;break;}}// 如果判断z没有出现在过tree中,就将它添加进去if (flag3) {array.add(new Integer(z));}// 初始化flag3flag3 = true;}
}

由于数独的规则,每行每列每个小九宫格 1 ~ 9 不能重复,所以填写 arrayAdd() 函数来添加 tree 中没有的元素,如果有了就跳过。

4 编写 assume() 函数

这个函数的作用是分析每个格子可能的解,并将其放到数组中,之后 run() 函数会调用它来分析可能的解。

public static ArrayList<Integer> assume(int row, int column) {// 创建数组arrayArrayList<Integer> array = new ArrayList<Integer>();TreeSet<Integer> tree = new TreeSet<Integer>();// 添加同一列其他的元素值for (int a = 0; a<9; a++) { // 如果该格不为空,就添加到tree中if (a != column && b[row][a] != 0) { tree.add(new Integer(b[row][a]));}}// 添加同行的其他元素for (int c=0; c<9; c++) {// 如果该格满足添加,就添加到tree中if (c != row && b[c][column] != 0) {tree.add(new Integer(b[c][column]));}}// 这里使用了整型除法只保留整数部分的特点,获取元素在同一个小九宫格的行for (int a = (row/3)*3; a<(row/3+1)*3; a++){// 获取元素在同一个九宫格的列for (int c = (column/3)*3; c < (column/3 + 1)*3; c++) {// 如果元素满足条件都添加到tree中if ((!(a == row && c == column)) && b[a][c] != 0) {tree.add(new Integer(b[a][c]));}}}arrayAdd(array, tree);return array;
}

为了提高算法的效率,我们将大九宫格分成 9 个小九宫格,主要是分析在同行同列同一个小九宫格内哪些数值已经被填充了,然后地调用 arrayAdd() 函数,添加该格备选的数值。

5 编写 run() 函数

run() 就是用来赋值计算的,然后调用 arrayAdd() 函数和 assume() 函数来判断。

public void run() {// 初始化变量行,列int row = 0,column = 0; // flag用来判断该格子是否填入正确boolean flag = true;for (int a = 0; a < 9; a++) {for (int c = 0; c < 9; c++) {if (b[a][c] != 0) {/* boo的作用是找出填入数据的空格,*  填入数据的空格是谜面,我们需要根据这些信息解迷题*/boo[a][c] = true;} else {// 为空的格子是需要填入数据的部分boo[a][c] = false;}}}/* arraylist是一个二维的序列,它的每一个值都是一个数组指针,*  存放了该格子可能的解,当一个解错误时,调用下一个解,* 这也就是前面介绍的数独解法。*/ArrayList<Integer>[][] utilization = new ArrayList[9][9];while (column < 9) {if (flag == true) {row = 0;}while (row < 9) {if (b[row][column] == 0) {if (flag) {ArrayList<Integer> list = assume(row, column);//utilization[row][column] = list;}// 如果没有找到可能的解,说明前面的值有错误,就回溯到之前的格子进行修改if (utilization[row][column].isEmpty()) {// 调用flyBack函数寻找合适的row和columnflyBack(boo, row, column); // 将row返回到合适的位子row = upRow;// 将column返回到合适的位子column = upColumn;// 初始化有问题的格子b[row][column] = 0;column--;flag = false;break;} else {// 将备选数组中第一个值赋给bb[row][column] = utilization[row][column].get(0);// 因为上面已经赋值了,所以就删除掉第一个数值utilization[row][column].remove(0);flag = true;//判断是否所有的格子都填入正确,然后将正确的结果输出到屏幕上judge();}} else {// 如果r为false,说明还有格子没填入数据,就继续遍历flag = true;}row++;}column++;}
}

在 run() 函数中填写空格的地方,我们的想法是将一行一行的分析,每个点都可能有几个值,我们用一个数组 utilization 来存放所有可能的值,在这个值的基础上填写下一个空格,当填写不动的时候回溯到这里,填写为 utilization 数组里的下一个值。

6 编写判断函数judge()

public void judge()
{boolean r = true;// 查找还没有填入数据的格子for (int a1 = 0; a1 < 9; a1++) {for (int b1 = 0; b1 < 9; b1++) {if (r == false) {break;}// 如果 b[a1][b1] 需要计算,就将它提取出来if (b[a1][b1] == 0) {r = false;}}}// 如果r为true,则所有的格子都填入了数据,说明九宫格就完成了,此时输出结果到屏幕上if (r) { for (int a1 = 0; a1 < 9; a1++) {for (int b1 = 0; b1 < 9; b1++) {Myframe.filed[a1][b1].setText(b[a1][b1] + "");}}}
}

编写 MyFrame 类

由于我们的结构是九个 TextField 外加两个 button 控件,计算,关闭。基于这些需求,所以编写的内容如下:

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeSet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;// 继承界面类
class Myframe extends JFrame {public static Object obj = new Object();// 创建九宫格界面public final static JTextField[][] filed = new JTextField[9][9];public Myframe() {// 初始化界面,让所有的格子都等于空for (int a = 0; a < 9; a++) {for (int b = 0; b < 9; b++) {filed[a][b] = new JTextField();filed[a][b].setText("");}}// 编写布局,把textfield添加到布局中JPanel jpan = new JPanel();jpan.setLayout(new GridLayout(9, 9));for (int a = 8; a > -1; a--) {for (int b = 0; b < 9; b++) {jpan.add(filed[b][a]);}}// 界面布局为居中add(jpan, BorderLayout.CENTER);JPanel jpb = new JPanel();// 设置两个按钮,计算和退出JButton button1 = new JButton("calc");JButton button2 = new JButton("close");// 将按钮添加到界面上jpb.add(button1);jpb.add(button2);// 给按钮添加监听器,就是添加事件响应函数button1.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent event) {synchronized (obj) {for (int a = 0; a < 9; a++) {for (int b3 = 0; b3 < 9; b3++) {int pp = 0;// 获取九宫格中的已填入数据的值,这些就是谜面if (!(filed[a][b3].getText().trim().equals(""))) {pp = Integer.parseInt(filed[a][b3].getText().trim());Calculate.b[a][b3] = pp;}}}}synchronized (obj) {// 开启线程计算九宫格的答案new Thread(new Calculate()).start();}}});// button2很简单,调用api关闭程序button2.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent event) {System.exit(0);}});// 设置界面的布局add(jpb, BorderLayout.SOUTH);}
}

我们在 button 上添加了监听器,分别用于获取界面信息,生成计算结果和关闭程序。

三、编写主函数

public class Sudoku{public static void main(String[] args) {Myframe myf=new Myframe();myf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置主界面的名称myf.setTitle("sudoku");//设置界面的大小myf.setSize(500,500);//设置主程序可见myf.setVisible(true);}
}

完整的代码如下:

http://labfile.oss.aliyuncs.com/courses/704/Sudoku.java

代码编写完毕后,开始编译和运行,

javac Sudoku.java  //这里会报一个错,没有关系,直接运行
java Sudoku

编译效果图:

运行效果图:

3.1 解决实际谜题

将题目导入我们的程序,很快就可以得到结果了,为自己的成功鼓掌吧!

java编写数独计算器相关推荐

  1. java编写某计算器控制台程序_用java程序编写一个计算器

    点击查看用java程序编写一个计算器具体信息 答:给你一个参考,希望不要被百度吞了当晚餐 import java.awt.BorderLayout; import java.awt.GridLayou ...

  2. java编写某计算器控制台程序_计算器 - 进阶的憨狗 - 博客园

    源起 最近在看程杰著作的<大话设计模式>,全书以小菜和大鸟对话的形势,由浅入深的讲解程序的设计思想,影射出一个个设计模式.我之前虽然也使用过一些设计模式,但没有系统的学习.整理.总结,现从 ...

  3. java编写简易计算器_java实现简易计算器功能

    本文为大家分享了java实现简易计算器功能,具体内容如下 题目: 编写一个模拟计算器的程序.在面板中添加一个文本框(显示按键及运算结果). 10个数字按钮(0~9).4个运算按钮(加.减.乘.除).一 ...

  4. 计算器的程序代码java_求用JAVA编写的计算器程序代码

    展开全部 import java.awt.*;//引入包java.awt中所有的类 import java.awt.event.*;//引入包java.awt.event中所有的类. public c ...

  5. Java正则表达式实现计算器_用java编写win7计算器

    展开全部 参考代码:62616964757a686964616fe78988e69d8331333337393635import java.awt.*; import java.awt.event.* ...

  6. 用java编写一个计算器_用java程序编写一个计算器

    展开全部 给你一个参考,希望不62616964757a686964616fe58685e5aeb931333330343261要被百度吞了当晚餐 import java.awt.BorderLayou ...

  7. java编写安卓计算器_安卓实现简单计算器

    实现一个计算器 ,有加减乘除功能,小数点和清除操作. 这是学校安卓老师布置的作业,计算器说实话实现起来挺多坑的,之前在算法比赛中见过这种题,用来熟悉安卓的布局的确是挺好的一个小案例,不过需要挺多逻辑处 ...

  8. java编写GUI计算器

    简介: 此计算器实现基本运算加减乘除 还能显示系统时间 求导 求根号 运用java面向对象,GUI知识 import javax.swing.*; import java.awt.*; import ...

  9. Java编写简易计算器

    目录 1.功能: 2.结果: 3.代码: 1.功能: (1)"C":将之前输入的数字,计算结果等功能全部归零: (2)"CE":清除键,清除当前输入的数据或符号 ...

  10. 用java编写计算器_用JAVA编写一个计算器

    展开全部 import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.aw ...

最新文章

  1. Python自然语言处理实战
  2. xib与nib的区别
  3. python语言包含的错误,Python语言程序中包含的错误,一般分为三种,以下____________不是其中的一种...
  4. 《 短文本数据理解》——2.5小结
  5. input type=file change事件只触发一次
  6. Nacos(十一)之NacosSync 介绍
  7. Redux初学者指南
  8. 胶囊网络显神威:Google AI和Hinton团队检测到针对图像分类器的对抗攻击
  9. JAVA Swing GUI设计 WindowBuilder Pro Container使用大全3——JScrollPane使用
  10. sip协议的功能及其应用
  11. 从化工技术员到微软最有价值专家(MVP)--我的IT从业路
  12. 7.nestjs文件上传
  13. php极光短信接口接入
  14. DW标签使用与javascript文档基础介绍
  15. 「小车看百度,大车看深兰」,自动驾驶公交驶向千亿蓝海市场
  16. extern C C 调用c++
  17. Python教程:输入一系列整数输出最大值
  18. 使用MAC中碰到的各种问题
  19. SpringBoot项目启动失败: is not eligible for getting processed by all BeanPostProcessors (for example: not)
  20. Tracert 抓包测试

热门文章

  1. QT进行Word文档读写
  2. linux设置display参数,Linux DISPLAY 变量设置
  3. Python分支+简单循环
  4. 自动驾驶软件开发人才现状_一文读懂自动驾驶研究现状
  5. 小程序发布新版本后,部分用户手机白屏
  6. p图软件pⅰc_pic修图软件下载-pic修图 安卓版v16.4.52-PC6安卓网
  7. 来自H3C的降维打击:H3C BX54鲸路由评测体验
  8. Java获取时间,将当前时间减一天、一月、一年,并加以格式化
  9. 使用gcov和lcov测试代码覆盖率
  10. Spring @Value 用法