- 浏览: 465984 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
ty1972873004:
sunwang810812 写道我运行了这个例子,怎么结果是这 ...
Java并发编程: 使用Semaphore限制资源并发访问的线程数 -
lgh1992314:
simpleDean 写道请问,Logger.setLevel ...
Java内置Logger详解 -
sunwang810812:
我运行了这个例子,怎么结果是这样的:2号车泊车6号车泊车5号车 ...
Java并发编程: 使用Semaphore限制资源并发访问的线程数 -
jp260715007:
nanjiwubing123 写道参考你的用法,用如下方式实现 ...
面试题--三个线程循环打印ABC10次的几种解决方法 -
cb_0312:
SurnameDictionary文章我没看完,现在懂了
中文排序
在上一篇博文介绍了用矩阵转换法来产生数独终盘。关于矩阵转换法产生终盘矩阵请参考如下的文章。
Swing数独游戏(一):终盘生成之矩阵转换法 ==> http://mouselearnjava.iteye.com/blog/1941483
本篇博文将对数独游戏随机产生数独终盘展开说明。
用什么样的随机方法?
读过一些文章,关于随机产生数据终盘比较流行的是将拉斯维加斯算法与回溯法相结合。比如下面的文章都有提及:
http://zhangroup.aporc.org/images/files/Paper_3485.pdf
http://wenku.baidu.com/view/f9e3f17101f69e31433294e1.html
看了如下PPT: Java算法http://wenku.baidu.com/view/0a67634de518964bcf847c80.html
从上面的两个PPT截图中可以看出,拉斯维加斯算法思想去解决N皇后时所花费的时间不少。
数独中的判断不比N皇后少,如果想要在1秒内或者更短的时间产生1000个数独终盘,个人觉得并不是很容易(当然了,这个希望以后去证明一下是正确的)。
所以,我想选择另外的途径去完成。
我的随机方法思想
在给出我的随机方法思想之前,我们先来看看数独终盘的数量。
终盘数量
终盘数量数独中的数字排列千变万化,那么究竟有多少种终盘的数字组合呢?
6,670,903,752,021,072,936,960(约有6.67×10的21次方)种组合,2005年由Bertram Felgenhauer和Frazer Jarvis计算出该数字,并将计算方法发布在他们网站上,如果将等价终盘(如旋转、翻转、行行对换,数字对换等变形)不计算,则有5,472,730,538个组合。数独终盘的组合数量都如此惊人,那么数独题目数量就更加不计其数了,因为每个数独终盘又可以制作出无数道合格的数独题目。
-- 参考自http://baike.baidu.com/link?url=ePXUCvpBaRKBkEA3pVfOkg3m-NBozO6a4GDS0N3E5_gK1nnJCDzd5O-YL1w7c5S3
按照这个数量,如果我们将一个[1,2,3,4,5,6,7,8,9]的数组随机化,然后将其作为一行数据添加到一个二维数组中去,该行能满足数独终盘规则的概率是很大的。
基于这个假设(假设的有效性会在文章后面验证),我的随机算法思想如下:
实现的程序如下:
[b]假设有效性验证及阈值设定
针对上述的代码,我跑10组,每组30个实例,看看这300个例子中,产生数独终盘所需要调用随机产生由1到9的一维数组的次数各是多少, 结果如下:
从上面的结果图中可以看出:[b]300个实例中,调用次数最小的为11,接近理想最小调用次数9.
最大值为217次,平均约50次。而且大部分实例调用的次数在100以内。
从这些数据分析中,上述假设基本上是合适的,阈值最后选择了接近实验最大值217的220.[/b]
效果和结论
选择产生不同的数目的数独终盘,看看执行的时间。
结论:
1. 在打印二维数组到控制台显示的情况下,产生10万个随机数独终盘的时间差不多1分钟左右。
2. 在打印二维数组到控制台显示的情况下,产生1000个随机数独终盘的时间差不到1秒,这个正是我们之前所期望的。
3. 产生大数目的随机终盘,将二维数组打印到控制台显示的时间占据总时间的60%左右。
如果不在控制台输出二维数组,花费的时间将少很多。 看上面的折线就可以知道。
总的来说,效果还是比较不错的,比较适合一下需要产生很多的数独终盘的情况,比如:初始化1000个或者更多个数独终盘并写入文件,然后作为数独游戏的每关内容。
在下一篇博文中将介绍使用“挖洞”的思想去生成数独难题。
使用在Console打印出随机产生二维矩阵的的测试代码,及其产生50000个和100000个二维矩阵的结果截图如下:[/b]
在下一篇博文中将介绍使用“挖洞”的思想去生成数独难题。
转载请注明出处:http://mouselearnjava.iteye.com/blog/1941693
Swing数独游戏(一):终盘生成之矩阵转换法 ==> http://mouselearnjava.iteye.com/blog/1941483
本篇博文将对数独游戏随机产生数独终盘展开说明。
用什么样的随机方法?
读过一些文章,关于随机产生数据终盘比较流行的是将拉斯维加斯算法与回溯法相结合。比如下面的文章都有提及:
http://zhangroup.aporc.org/images/files/Paper_3485.pdf
http://wenku.baidu.com/view/f9e3f17101f69e31433294e1.html
看了如下PPT: Java算法http://wenku.baidu.com/view/0a67634de518964bcf847c80.html
从上面的两个PPT截图中可以看出,拉斯维加斯算法思想去解决N皇后时所花费的时间不少。
数独中的判断不比N皇后少,如果想要在1秒内或者更短的时间产生1000个数独终盘,个人觉得并不是很容易(当然了,这个希望以后去证明一下是正确的)。
所以,我想选择另外的途径去完成。
我的随机方法思想
在给出我的随机方法思想之前,我们先来看看数独终盘的数量。
终盘数量
终盘数量数独中的数字排列千变万化,那么究竟有多少种终盘的数字组合呢?
6,670,903,752,021,072,936,960(约有6.67×10的21次方)种组合,2005年由Bertram Felgenhauer和Frazer Jarvis计算出该数字,并将计算方法发布在他们网站上,如果将等价终盘(如旋转、翻转、行行对换,数字对换等变形)不计算,则有5,472,730,538个组合。数独终盘的组合数量都如此惊人,那么数独题目数量就更加不计其数了,因为每个数独终盘又可以制作出无数道合格的数独题目。
-- 参考自http://baike.baidu.com/link?url=ePXUCvpBaRKBkEA3pVfOkg3m-NBozO6a4GDS0N3E5_gK1nnJCDzd5O-YL1w7c5S3
按照这个数量,如果我们将一个[1,2,3,4,5,6,7,8,9]的数组随机化,然后将其作为一行数据添加到一个二维数组中去,该行能满足数独终盘规则的概率是很大的。
基于这个假设(假设的有效性会在文章后面验证),我的随机算法思想如下:
- 1. 写一个方法用于获取一个由1到9九个数随机排列的一维数组。
- 2. 循环行(下标从0到8),将这个随机产生的一维数组作为当前行的内容,如果是第一行(行标为0),那么直接作为该行的内容。如果是其它行,则验证数据是否都符合条件。
- 如果符合条件,则再产生一个由1到9九个数随机排列的一维数组作为下一行的内容并验证数据是否可用。
- 如果不符合条件,则将该行数据设置为0,调整row和col,产生一个由1到9九个数随机排列的一维数组,重新对该行验证。
- 3. 程序中为了防止产生一维随机数组的方法调用很多次而没有产生结果,设置一个最多调用该方法次数的阈值,当达到这个阈值还没有产生结果,重新从 row =0 col =0 开始。
实现的程序如下:
package my.utils.algorithm.sudoku; import java.util.Random; public class SudokuPuzzleGenerator { private Random random = new Random(); /**运行此程序300次,最大值是217,最小值11,平均约等于50 * 阈值设置为220, 能满足大部分程序,二维矩阵不会置为0,重新再产生值。 */ private static final int MAX_CALL_RANDOM_ARRAY_TIMES = 220; /**记录当前buildRandomArray()方法调用的次数*/ private int currentTimes = 0; public int[][] generatePuzzleMatrix() { int[][] randomMatrix = new int[9][9]; for (int row = 0; row < 9; row++) { if (row == 0) { currentTimes = 0; randomMatrix[row] = buildRandomArray(); } else { int[] tempRandomArray = buildRandomArray(); for (int col = 0; col < 9; col++) { if (currentTimes < MAX_CALL_RANDOM_ARRAY_TIMES) { if (!isCandidateNmbFound(randomMatrix, tempRandomArray, row, col)) { /* * 将该行的数据置为0,并重新为其准备一维随机数数组 */ resetValuesInRowToZero(randomMatrix,row); row -= 1; col = 8; tempRandomArray = buildRandomArray(); } } else { /* * 将二维矩阵中的数值置为0, * row赋值为-1 col赋值为8, 下一个执行的就是row =0 col=0, * * 重头开始 */ row = -1; col = 8; resetValuesToZeros(randomMatrix); currentTimes = 0; } } } } return randomMatrix; } private void resetValuesInRowToZero(int[][] matrix, int row) { for (int j = 0; j < 9; j++) { matrix[row][j] = 0; } } private void resetValuesToZeros(int[][] matrix) { for (int row = 0; row < 9; row++) { for (int col = 0; col < 9; col++) { matrix[row][col] = 0; } } } private boolean isCandidateNmbFound(int[][] randomMatrix, int[] randomArray, int row, int col) { for (int i = 0; i < randomArray.length; i++) { /** * 试着给randomMatrix[row][col] 赋值,并判断是否合理 */ randomMatrix[row][col] = randomArray[i]; if (noConflict(randomMatrix, row, col)) { return true; } } return false; } private boolean noConflict(int[][] candidateMatrix, int row, int col) { return noConflictInRow(candidateMatrix, row, col) && noConflictInColumn(candidateMatrix, row, col) && noConflictInBlock(candidateMatrix, row, col); } private boolean noConflictInRow(int[][] candidateMatrix, int row, int col) { /** * 因为产生随机数矩阵是按照先行后列,从左到右产生的 ,该行当前列后面的所有列的值都还是0, 所以在行比较的时候, * 只要判断该行当前列与之前的列有无相同的数字即可。 * */ int currentValue = candidateMatrix[row][col]; for (int colNum = 0; colNum < col; colNum++) { if (currentValue == candidateMatrix[row][colNum]) { return false; } } return true; } private boolean noConflictInColumn(int[][] candidateMatrix, int row, int col) { /** * 与noConflictInRow(...)方法类似: * * * 因为产生随机数矩阵是按照先行后列,从左到右产生的,该列当前行后面的所有行的值都还是0, * * 所以在列比较的时候, 只要判断该列当前行与之前的行有无相同的数字即可。 * */ int currentValue = candidateMatrix[row][col]; for (int rowNum = 0; rowNum < row; rowNum++) { if (currentValue == candidateMatrix[rowNum][col]) { return false; } } return true; } private boolean noConflictInBlock(int[][] candidateMatrix, int row, int col) { /** * 为了比较3 x 3 块里面的数是否合理, 需要确定是哪一个Block,我们先要求出3 x 3的起始点。 比如: Block 1 * 的起始点是[0][0] Block 2 的起始点是[3]][0] * * ... Block 9 的起始点是[6][6] */ int baseRow = row / 3 * 3; int baseCol = col / 3 * 3; for (int rowNum = 0; rowNum < 8; rowNum++) { if (candidateMatrix[baseRow + rowNum / 3][baseCol + rowNum % 3] == 0) { continue; } for (int colNum = rowNum + 1; colNum < 9; colNum++) { if (candidateMatrix[baseRow + rowNum / 3][baseCol + rowNum % 3] == candidateMatrix[baseRow + colNum / 3][baseCol + colNum % 3]) { return false; } } } return true; } /** * 返回一个有1到9九个数随机排列的一维数组, */ private int[] buildRandomArray() { currentTimes++; int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int randomInt = 0; /* * 随机产生一个1到8的随机数,使得该下标的数值与下标为0的数值交换, * * 处理20次,能够获取一个有1到9九个数随机排列的一维数组, */ for (int i = 0; i < 20; i++) { randomInt = random.nextInt(8) + 1; int temp = array[0]; array[0] = array[randomInt]; array[randomInt] = temp; } return array; } /** * @return the currentTimes */ public int getCurrentTimes() { return currentTimes; } /** * @param currentTimes the currentTimes to set */ public void setCurrentTimes(int currentTimes) { this.currentTimes = currentTimes; } }
[b]假设有效性验证及阈值设定
针对上述的代码,我跑10组,每组30个实例,看看这300个例子中,产生数独终盘所需要调用随机产生由1到9的一维数组的次数各是多少, 结果如下:
从上面的结果图中可以看出:[b]300个实例中,调用次数最小的为11,接近理想最小调用次数9.
最大值为217次,平均约50次。而且大部分实例调用的次数在100以内。
从这些数据分析中,上述假设基本上是合适的,阈值最后选择了接近实验最大值217的220.[/b]
效果和结论
选择产生不同的数目的数独终盘,看看执行的时间。
结论:
1. 在打印二维数组到控制台显示的情况下,产生10万个随机数独终盘的时间差不多1分钟左右。
2. 在打印二维数组到控制台显示的情况下,产生1000个随机数独终盘的时间差不到1秒,这个正是我们之前所期望的。
3. 产生大数目的随机终盘,将二维数组打印到控制台显示的时间占据总时间的60%左右。
如果不在控制台输出二维数组,花费的时间将少很多。 看上面的折线就可以知道。
总的来说,效果还是比较不错的,比较适合一下需要产生很多的数独终盘的情况,比如:初始化1000个或者更多个数独终盘并写入文件,然后作为数独游戏的每关内容。
在下一篇博文中将介绍使用“挖洞”的思想去生成数独难题。
使用在Console打印出随机产生二维矩阵的的测试代码,及其产生50000个和100000个二维矩阵的结果截图如下:[/b]
public class Main { public static void main(String[] args) { SudokuPuzzleGenerator sudokuPuzzleGenerator = new SudokuPuzzleGenerator(); long start = System.currentTimeMillis(); int numOfSudokuMatrix = 10000; for (int count = 1; count <= numOfSudokuMatrix; count++) { System.out.println("第" + count + "个"); int[][] randomMatrix = sudokuPuzzleGenerator.generatePuzzleMatrix(); printMatrix(randomMatrix); } long end = System.currentTimeMillis(); System.out.println("产生"+numOfSudokuMatrix+"个数独矩阵花费时间: " + ((end - start) / 1000.0) + " 秒"); } public static void printMatrix(int[][] randomMatrix) { for (int rowNum = 0; rowNum < 9; rowNum++) { for (int colNum = 0; colNum < 9; colNum++) { System.out.print(randomMatrix[rowNum][colNum] + " "); } System.out.println(); } } }
在下一篇博文中将介绍使用“挖洞”的思想去生成数独难题。
转载请注明出处:http://mouselearnjava.iteye.com/blog/1941693
发表评论
-
工厂类中移除if/else语句
2016-07-10 19:52 900面向对象语言的一个强大的特性是多态,它可以用来在代码中移除 ... -
Java编程练手100题
2014-12-11 17:13 6728本文给出100道Java编程练手的程序。 列表如下: 面 ... -
数组复制的三种方法
2014-11-30 12:57 2212本文将给出三种实现数组复制的方法 (以复制整数数组为例)。 ... -
数组复制的三种方法
2014-11-30 12:54 0本文将给出三种实现数组复制的方法 (以复制整数数组为例)。 ... -
四种复制文件的方法
2014-11-29 13:21 1739尽管Java提供了一个类ava.io.File用于文件的操 ... -
判断一个字符串中的字符是否都只出现一次
2014-11-25 12:58 2724本篇博文将给大家带来几个判断一个字符串中的字符是否都只出现一 ... -
使用正则表达式判断一个数是否为素数
2014-11-23 13:35 2166正则表达式能够用于判断一个数是否为素数,这个以前完全没有想过 ... -
几个可以用英文单词表达的正则表达式
2014-11-21 13:12 3748本文,我们将来看一下几个可以用英文单词表达的正则表达式。这些 ... -
(广度优先搜索)打印所有可能的括号组合
2014-11-20 11:58 1953问题:给定一个正整n,作为括号的对数,输出所有括号可能 ... -
随机产生由特殊字符,大小写字母以及数字组成的字符串,且每种字符都至少出现一次
2014-11-19 14:48 3976题目:随机产生字符串,字符串中的字符只能由特殊字符 (! ... -
找出1到n缺失的一个数
2014-11-18 12:57 3173题目:Problem description: You h ... -
EnumSet的几个例子
2014-11-14 16:24 8749EnumSet 是一个与枚举类型一起使用的专用 Set 实现 ... -
给定两个有序数组和一个指定的sum值,从两个数组中各找一个数使得这两个数的和与指定的sum值相差最小
2014-11-12 11:24 3327题目:给定两个有序数组和一个指定的sum值,从两个数组 ... -
Java面试编程题练手
2014-11-04 22:49 6700面试编程 写一个程序,去除有序数组中的重复数字 编 ... -
Collections用法整理
2014-10-22 20:55 9846Collections (java.util.Collect ... -
The Code Sample 代码实例 个人博客开通
2014-09-04 18:48 1418个人博客小站开通 http://thecodesample. ... -
Collections.emptyXXX方法
2014-06-08 13:37 2145从JDK 1.5开始, Collections集合工具类中预先 ... -
这代码怎么就打印出"hello world"了呢?
2014-06-08 00:37 7396for (long l = 4946144450195624L ... -
最短时间过桥
2014-04-21 22:03 4137本文用代码实现最短时间过桥,并且打印如下两个例子的最小过桥时间 ... -
将数组分割成差值最小的子集
2014-04-20 22:34 2899本文使用位掩码实现一个功能 ==》将数组分割成差值最小的子集 ...
评论