利用程序随机构造N个已解答的数独棋盘
高级软件工程第二次作业:利用程序随机构造N个已解答的数独棋盘,代码如下:
package SudokuGame;
/**
* 解决这个问题使用的是回溯+剪枝的算法
* 基本思想:不断地将每个格子可填入的数字减少,如果当前格子没有数字可填入,就回溯到上一格
* 实现算法:首先初始化数独,将数独的第一行以随机数字填入,从第二行开始的每个格子都从1开始找可以填入的最小的数字,
在没有数字可填时,就开始回溯。 此算法在回溯的同时将不可能的结果排除,不会陷入死循环
*/ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner; public class sudokuV2 { public static void main(String[] args) {
sudokuV2 sudoku = new sudokuV2();
int[][] square = new int[9][9]; //存放数独的二维数组 String pathName = "sudotiku.txt";
File file = new File(pathName);
if(file.exists()){
file.delete(); //此句话在每次执行程序时先删除已经存在的文件sudotiku.txt
}
//开始生成数独
sudoku.go(square); } /**
* 将square中生成的数独写到文件sudotiku.txt中
* @param square
*/
public void writerToFile(int [][] square) {
String pathName = "sudotiku.txt";
File file = new File(pathName); try {
if(!file.exists()) {//若文件不存在就创建一个新文件
file.createNewFile();
}
FileWriter fWriter = new FileWriter(file, true); //此处参数如果直接是整形,则会被当成ASCII码,最后输出时转换成ASCII码对应的字符
//所以此处将int型转换成String输出
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
fWriter.write(String.valueOf(square[i][j]) + "\t");
}
fWriter.write("\r\n");
}
fWriter.write("\r\n");
fWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 接收用户输入的数字,若不符合要求则重新输入
* @return 返回用户输入的数字
*/
public int getNumber() {
int n; //用来接收用户想要生成的数独解的个数
do{
System.out.print("请输入要生成的数独的个数:(1~1000000之间)");
Scanner input = new Scanner(System.in);
n = input.nextInt();
if(n <= 0 || n >1000000) {
System.out.println("输入错误,请重新输入!");
}
}while(n <= 0 || n >1000000);
return n;
} /**
* 初始化九宫格,全部元素置0
* 并将数独的第一行填入随机数字
*/
public void init(int[][] square) {
/** 将大九宫格数字全部置0 */
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
square[i][j] = 0;
}
}
/** 将第一行填入随机数字 */
for(int i = 0,j =0; j < 9; j++) {
square[i][j] = findValueInFirst(square, i, j);
}
} /**
* 找到数独第一行可以填入的数字,并返回该数字
* @param square
* @param row
* @param col
* @return value 返回找到的合适的数字
*/
public int findValueInFirst(int[][] square, int row, int col) {
int value = 0; do{
value = (int)(Math.random() * 100) % 9 + 1 ;
if(rowCheck(square, row, col, value) == true) {
//如果value的值与第一行已有的值不重复,则返回value,将其存入square数组中
return value;
}
}while(true);
} /**
* 生成数独,从第二行开始,每个格子的可填入数字都从1开始试探,不符合则增加1
* @param square
*/
public void go(int[][] square) {
int n;
n = getNumber(); //用来接收用户想要生成的数独的个数
for(;n >= 1;n--) { //控制生成数独的个数
init(square);
/* 开始生成数独**/
int number ;
for (int i = 1; i < 9; i++) {
for (int j = 0; j < 9;) {
number = 0;
number = findValue(square, i, j);
if(number > 0) {
square[i][j] = number;
j++;
}else {
//没有找到可填入的值,开始回溯
if(j == 0) {//如果当前格子在第一列,则回溯到上一行的最后一格,此时需要将当前格子的数字置为0
square[i][j] = 0;
i--;
j = 8;
}else { //如果当前格子不在第一列,则回溯到同行的前一格
square[i][j] = 0;
j--;
}
}
}
}
writerToFile(square);
show(square);
System.out.println();
}
} /**
* 寻找可以填入格子的数字
* @param square
* @param row
* @param col
* @return
*/
public int findValue(int[][] square, int row, int col) { int temp = square[row][col];
if(temp == 0) {
//如果square当前的格子值为0,则说明在一个新的格子里,从1开始寻找可以填入的值
temp = 1;
}else {
//如果square当前的格子的不为0,则说明进入了回溯程序,将temp+1
temp++;
} boolean isChange = false; //记录temp是否有改变,用来判断循环是否继续 do {
/* tempValue用来记录进入循环时temp的值,以方便查看temp的值是否有变化 **/
int tempValue = temp;
if(rowCheck(square, row, col, temp) == false) {
//行内检查不唯一则temp在原来基础上+1
temp++;
isChange = true;
}
if(colCheck(square, row, col, temp) == false) {
//列内检查不唯一则temp在原来基础上+1
temp++;
isChange = true;
}
if(smallCheck(square, row, col, temp) == false) {
//小九宫格内检查不唯一则temp在上一步基础上+1
temp++;
isChange = true;
}
if(temp >= 10) {
//此时1~9均不能填入格子,需要进入回溯
return -1;
}
if(temp == tempValue) {
//全部检查完成后,若没有改变,则表示找到可以填入的值,返回temp
return temp;
}
}while(isChange == true); //isChange变成true,说明temp改变了,此时没有找到可以填入格子的值
return -1;
} /**
* 输出数独
* @param square
*/
public void show(int[][] square) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
System.out.print(square[i][j] + "\t");
}
System.out.println();
}
} /**
* 行检查:判断将要填入的数字是否与该行的元素有重复
* @param square 二维数组组成的大九宫格
* @param row 当前格子的行坐标
* @param col 当前格子的列坐标
* @param value 当前准备填入格子的数字
* @return 若value与该行已有的元素重复则返回false,不重复返回true
*/
public boolean rowCheck(int[][] square, int row, int col, int value) {
for(int j = 0; j < col ;j++ ) {//判断新填入的元素是否与该行的元素有重复
if(value == square[row][j]) {
return false;
}
}
return true;
} /**
* 列检查:判断将要填入的数字是否与该列的元素有重复
* @param square 二维数组组成的大九宫格
* @param row 当前格子的行坐标
* @param col 当前格子的列坐标
* @param value 当前准备填入格子的数字
* @return 若value与该列已有的元素重复则返回false,不重复返回true
*/
public boolean colCheck(int[][] square, int row,int col,int value) {
for(int i = 0; i < row ;i++ ) {//判断新填入的元素是否与该列的元素有重复
if( value == square[i][col] ) {
return false;
}
}
return true;
} /**
* 小九宫格检查:判断新填入的元素是否与其所在的小九宫格的元素有重复
* 想法:根据该格子所在的行和列判断所属的小九宫格的位置,然后分成九个小块分别判断
分别有九组 (0,0),(0,1),(0,2)
(1,0),(1,1),(1,2)
(2,0),(2,1),(2,2)
* @param square 二维数组组成的大九宫格
* @param row 当前格子的行坐标
* @param col 当前格子的列坐标
* @param value 当前准备填入格子的数字
* @return 若value与该小九宫格已有的元素重复则返回false,不重复返回true
*/
public boolean smallCheck(int[][] square, int row,int col,int value) { int x = row / 3; //(x,y)的组合表示小九宫格的位置
int y = col / 3; for(int i = 3 * x; i < 3 * x + 3 ;i++ ) {
for (int j = 3 * y; j < 3 * y + 3; j++) {
if(i == row && j == col) {
continue;
}
if(value == square[i][j]) {
return false;
}
}
}
return true;
}
}
以上为程序运行的全部代码。生成数独后将其写入文件“sudotiku.txt”中,也会在屏幕上显示。以下是运行结果:
请输入要生成的数独的个数:(1~1000000之间)3
9 3 5 7 1 8 2 4 6
1 2 4 3 5 6 7 8 9
6 7 8 2 4 9 1 3 5
2 1 3 4 6 5 8 9 7
4 5 6 8 9 7 3 1 2
7 8 9 1 2 3 5 6 4
3 4 1 6 7 2 9 5 8
5 6 2 9 8 1 4 7 3
8 9 7 5 3 4 6 2 1
4 5 1 9 7 8 2 3 6
2 3 6 1 4 5 7 8 9
7 8 9 2 3 6 1 4 5
1 2 3 4 5 7 6 9 8
5 4 7 6 8 9 3 1 2
6 9 8 3 1 2 4 5 7
3 6 2 5 9 1 8 7 4
8 1 5 7 2 4 9 6 3
9 7 4 8 6 3 5 2 1
4 8 1 5 2 7 6 9 3
2 3 5 1 6 9 4 7 8
6 7 9 3 4 8 1 2 5
1 2 3 4 5 6 7 8 9
5 4 7 8 9 1 2 3 6
8 9 6 2 7 3 5 1 4
3 1 2 6 8 4 9 5 7
7 5 4 9 3 2 8 6 1
9 6 8 7 1 5 3 4 2
程序运行的正确性以及性能分析:程序可以正确地运行出结果,计算1000个数独完全解大概用时926ms,理论上来说此程序可以得出9!个不同结果。另外,代码已经上传到coding,请老师指点。
学习过程及遇到的问题:
1、在做这次作业时,最开始我想到的方案是:用一个二维数组,每次填入格子中的值都是1~9中的随机数value,如果该数不符合数独要求,则value循环+1再填入,1~9都不能填入则回溯到上一格。此方法遇到了一个死循环,因为每次回溯到上一格后,该格子总是可以填入一个值,再开始下一格,因此导致死循环。
解决方法:在网上看到了目前这个算法,此算法每次都会排除不合适的数字(包括当前格不能填入的数字和填入此数字后导致后面的格子不能正确填入的数字),这样可以控制不陷入死循环。(PS:学习到别人的算法后,有试着再在每个格子填入随机数的基础上改进,但无可避免的落入死循环,这点不知道老师能否给出指点。)
2、本次作业从自己开始编程、遇到死循环再到寻找解决方案完成作业大概用了三到四天时间,通过本次作业锻炼了自己的编程能力,同时也看到了自己在算法设计上的不足,学习了别人的算法后才发现原来可以用这样的方式解决问题,还是受益匪浅。
关于课外任务:
问题:在你一生中身体最健康,精力最旺盛的时候,能在大学学习和研究,是一生中少有的机会。请说明一下,你已经具备的专业知识、技能、能力有哪些?离成为一个合格的 IT专业毕业生,在专业知识、技能、能力上还差距哪些?请看这个技能调查表, 从表中抽取 5 - 7 项你认为对你特别重要的技能, 记下你目前的水平, 和你想在课程结束后达到的水平 (必须至少列出 5 项)。链接: http://www.cnblogs.com/xinz/p/3852177.html
目前来说,我觉得我的专业知识与技能水平是比较差的,有些专业基础课之前没有学过,这些要利用课外的时间尽快补上。以下几项技能我觉得是比较重要的,希望课程结束之后可以在以下几个方面有较大的提升。
利用程序随机构造N个已解答的数独棋盘的更多相关文章
- 利用VBA+OO4O构造CTAIS开放式通用平台
利用VBA+OO4O构造CTAIS开放式通用平台 2010-06-08 14:59:28 | 来源:税务信息化论文集 | 作者:于非 易飞 摘 要:文立足于CTAIS系统体系,探讨如何通过OO4O技 ...
- 利用GBDT模型构造新特征具体方法
利用GBDT模型构造新特征具体方法 数据挖掘入门与实战 公众号: datadw 实际问题中,可直接用于机器学**模型的特征往往并不多.能否从"混乱"的原始log中挖掘到有用的 ...
- Java利用数组随机抽取幸运观众
编写程序,事先将所有观众姓名输入数组,然后获得数组元素的总数量,最后在数组元素中随机抽取元素的下标,根据抽取的下标获得幸运观众的姓名. 思路如下: 定义输入框的按键事件,使用KeyEvent类的get ...
- 小程序入口构造工具&二维码测试工具
小程序入口构造工具&二维码测试工具 本文将介绍我们小程序中隐藏的两个工具页面.原理虽不复杂,收益却实实在在,或许也能给诸君带来启发. 入口构造工具 痛点 PM&运营 投放链接 PM&a ...
- CodeForces-329C(div1):Graph Reconstruction(随机&构造)
I have an undirected graph consisting of n nodes, numbered 1 through n. Each node has at most two in ...
- 随机数问题--已知有个Random7()的函数,返回1到7随机自然数,让利用这个Random7()构造Random10()随机1~10.
Math.random()随机生成(0,1)之间的float数,Random7随机生成[1,7]之间的整数,利用Random7构造Random10的步骤: 1.生成数a:a是两次Random7的结果, ...
- 高级软件工程第二次作业:随机生成N个不重复的已解答完毕的数独棋盘
#include <stdio.h> #include "SuduCheck.h" ][],int i,int j,int k) //判断是否可以将第i行.第j列的数设 ...
- TF:Tensorflow结构简单应用,随机生成100个数,利用Tensorflow训练使其逼近已知线性直线的效率和截距—Jason niu
import os os.environ[' import tensorflow as tf import numpy as np x_data = np.random.rand(100).astyp ...
- Linux下运行C++程序出现"段错误(核心已转储)"的原因
今天写程序出现了“段错误(核心已转储)"的问题,查了一下资料,加上自己的实践,总结了以下几个方面的原因. 1.内存访问出错 这类问题的典型代表就是数组越界. 2.非法内存访问 出现这类问题 ...
随机推荐
- Codeforces 770C Online Courses In BSU (DFS)
<题目链接> 题目大意:给定$n$个任务,其中有$m$个主线任务,然后$n$个任务中,每个任务都有可能有一个list,表示完成这个任务之前必须要完成的任务,然后现在让你找出,完成这$m$个 ...
- JVM(15)之 类加载器
开发十年,就只剩下这套架构体系了! >>> 今天我们将类加载机制5个阶段中的第一个阶段,加载,又叫做装载.为了便于阅读,以下都叫做装载. 装载的第一步就是要获得二进制的字节 ...
- 攻防世界--dmd-50
测试文件:https://adworld.xctf.org.cn/media/task/attachments/7ef7678559ea46cbb535c0b6835f2f4d 1.准备 获取信息 6 ...
- 20191114PHP文件操作
<meta charset="utf-8"><?php// $fn=fopen("c:\\abc.txt","w"); / ...
- Solr的学习使用之(四)建数据库(添加Core)、表(配置schema.xml)
1.数据库数据库就相当于solr里面的核.solr4.6不能使用界面提供的的Core Admin/Add Core来建立,会报错,不懂为啥:那就采用最 简单的办法:把solr下载包里面的\solr-4 ...
- maven clean后 编译报错
<plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <config ...
- BZOJ3038 上帝造题的七分钟
Time Limit: 3 Sec Memory Limit: 128 MB Description XLk觉得<上帝造题的七分钟>不太过瘾,于是有了第二部. "第一分钟,X说, ...
- ubuntu 自带截图工具快捷键盘
PrtSc – 获取整个屏幕的截图并保存到 Pictures 目录. Shift + PrtSc – 获取屏幕的某个区域截图并保存到 Pictures 目录. Alt + PrtSc –获取当前窗口的 ...
- centos 6.5 配置网络
编辑 vi /etc/sysconfig/network-scripts/ifcfg-eth0 修改内容 DEVICE="eth0" BOOTPROTO="static& ...
- MySQL入门常用命令
使用本地 MySQL,系统 Ubuntu. mysql -u root -p 输入 root 用户的密码进入MySQL: mysql>