`
MouseLearnJava
  • 浏览: 467126 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Swing数独游戏(二):终盘生成之随机法

阅读更多
在上一篇博文介绍了用矩阵转换法来产生数独终盘。关于矩阵转换法产生终盘矩阵请参考如下的文章。

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
  • 大小: 146.6 KB
  • 大小: 52 KB
  • 大小: 41.4 KB
  • 大小: 59 KB
  • 大小: 38.1 KB
  • 大小: 43.8 KB
  • 大小: 70.3 KB
  • 大小: 70.9 KB
2
0
分享到:
评论
1 楼 yaling521 2013-12-04  
你好,你这个生成的数独能不能保证是唯一解啊?

相关推荐

    数独游戏终盘生成算法1

    数独游戏终盘生成算法是计算机科学中一种有趣的算法应用,主要目的是生成具有唯一解决方案的数独谜题。本文将探讨四种不同的数独终盘生成方法,并以代码示例展示其中的一种。 首先,数独是一种逻辑游戏,由9x9的...

    数独游戏---自写算法。随即生成数独

    2. 实现数独生成器:随机生成一个完整的数独解决方案,然后通过删除部分数字生成一个残局。 3. 检查逻辑:编写函数检查当前填入的数字是否合法,即所在行、列和小宫格内没有重复。 4. 回溯算法:当用户填入数字后,...

    sd.rar_c++数独_c++生成数独_数独_生成数独_随机生成数独

    随机生成数独通常会在填充初始数字时引入随机性,确保生成的数独谜题具有不同难度级别。 "过关斩将"可能指的是游戏的关卡设计,即程序中预设了一系列难度不同的数独谜题供玩家挑战。这些谜题可能是预先生成并存储的...

    java课程设计作业-基于java+swing构建的数独小游戏(源码+资源文件)

    java课程设计作业——基于java+swing构建的数独小游戏(源码+资源文件) 编程语言:java 界面绘制:swing IDE:MyEclipse,IDEA java课程设计作业——基于java+swing构建的数独小游戏(源码+资源文件) 编程语言...

    基于挖洞思想的数独游戏生成算法

    综上所述,基于“挖洞思想”的数独游戏生成算法是一种创新的方法,它从一个完整的数独谜题出发,通过特定的规则删除一些数字,从而得到一个有唯一解的数独谜题。这项算法的提出者为薛源海、蒋彪彬和李永卓,他们来自...

    数独游戏,随机生成只有唯一解的数独表

    用c#写了一个能够随机生成唯一解的数独游戏,很多其他人的都不是随机生成,而且也不能保证生成的数独表的解是唯一的,所以我自己写了一个可以随机生成唯一解的数独游戏,上传上来和大家一起学习学习,看看算法还有...

    数独游戏答案生成器 真牛逼

    “数独游戏答案生成器”是一款专为数独爱好者设计的工具,它的功能强大,能够帮助玩家快速找到数独游戏的答案。这款生成器的出现,无疑为那些在数独题目面前感到困惑或者想要验证自己解答正确性的玩家提供了极大的...

    100个数独(Python语言生成)

    总的来说,“100个数独(Python语言生成)”项目展示了Python在创建数独游戏方面的灵活性和实用性。通过学习这个项目,不仅可以掌握数独生成的算法,还能了解Python在数据处理、算法实现和图形化界面设计上的应用。...

    用C语言生成一个随机数独.txt

    利用C语言生成一张可以玩的数独,但只是原始数据

    数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏.rar

    数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏数独游戏...

    简单数独游戏,能够自动生成4*4的数独

    2. **初始化数独**:自动生成数独盘面通常需要随机填充部分数字,同时保证合法。这涉及到随机数生成和回溯算法。C++的`&lt;cstdlib&gt;`库提供`rand()`函数生成随机数,而回溯算法用于在填入数字后检查是否符合规则,若不...

    数独游戏技巧:1~9九宫格数独口诀和解题技巧心得分享.doc

    数独,这一源于18世纪末的瑞士的数字推理游戏,在经过美国的普及和日本的推广后,成为了一种风靡全球的益智活动。在这一活动中,玩家需要在一个由81个格子组成的9x9的九宫格中,运用逻辑推理能力,根据既有的数字...

    Windows数独游戏:Java FX实现

    几何数独游戏,微博[@屠龙的胭脂](https://weibo.com/1852299857?refer_flag=1001030103_)所介绍[几何数独游戏视频介绍](https://video.weibo.com/show?fid=1034:4737961351381034)同款。源码在:[蛇蛇数独游戏源...

    Python数独游戏源代码

    数独游戏的开发可能涉及到生成随机数独板面、解决算法的实现,以及友好的用户交互界面设计。 7. **数独逻辑**: 数独是一种逻辑解谜游戏,其核心在于每一行、每一列以及每一个宫格内的数字都不能重复。在Python中...

    java数独生成算法及基于此算法的android数独游戏APK

    在本项目中,我们将探讨Java实现的数独生成算法以及基于此算法的Android数独游戏APK。 首先,让我们深入理解Java数独生成算法。这种算法通常分为两个主要部分:生成一个合法的数独配置和解决数独谜题。生成算法可以...

    数独自动生成题库

    总的来说,"数独自动生成题库"这个项目不仅涵盖了数独游戏的基础实现,还深入到了算法设计和难度控制,以及用户反馈的优化。通过这样的实践,不仅能提升编程技能,也能加深对数独游戏逻辑的理解。

    【原创】数独游戏,C#源码,可随机出题,自动解题,代码简易

    在这个项目中,随机出题可能采用了这样的方法:首先生成一个完整的数独解,然后通过随机消除一些已知数字,形成不完整的数独盘面。自动解题则可能采用深度优先搜索(DFS)配合候选数字列表的方式,当搜索到无解时...

    数独随机矩阵生成

    数独是一种广受欢迎的逻辑推理游戏,它基于一个9x9的网格,被分为9个小的3x3的宫格。每个宫格、行和列都必须填入1到9的数字,且每个数字在各自的宫格、行和列中只能出现一次。"数独随机矩阵生成"是指创建符合数独...

    数独的难度衡量、生成及微粒群算法(张艳宗)

    **数独生成**:为了生成具有特定难度级别的数独谜题,研究者开发了一种基于数独终盘的生成方法。这种方法首先生成一个完整的数独终盘,然后通过删除某些已知数字来形成谜题。在删除过程中,算法确保剩余的谜题只有一...

    九宫格数独题目生成器.rar

    九宫格数独题目生成器就是根据这个规则,通过算法随机生成符合要求的数独盘面。 在C#编程语言中,实现数独生成器的关键在于设计合理的算法。一般而言,这涉及到以下几个步骤: 1. **基础设置**:创建9x9的二维数组...

Global site tag (gtag.js) - Google Analytics