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

[b]Swing数独游戏(一):终盘生成之矩阵转换法 [/b]==> [url]http://mouselearnjava.iteye.com/blog/1941483[/url]

本篇博文将对数独游戏随机产生数独终盘展开说明。

[size=medium][color=blue][b]用什么样的随机方法?[/b][/color][/size]

读过一些文章,关于随机产生数据终盘比较流行的是将拉斯维加斯算法与回溯法相结合。比如下面的文章都有提及:

[url]http://zhangroup.aporc.org/images/files/Paper_3485.pdf[/url]
[url]http://wenku.baidu.com/view/f9e3f17101f69e31433294e1.html[/url]

[img]http://dl2.iteye.com/upload/attachment/0089/2557/e887ce09-1027-319f-b4d1-cf4a7947f2f8.jpg[/img]

看了如下PPT: Java算法[url]http://wenku.baidu.com/view/0a67634de518964bcf847c80.html[/url]

[img]http://dl2.iteye.com/upload/attachment/0089/2564/4105d102-57b2-3228-8aa9-dd18e581007e.jpg[/img]

[img]http://dl2.iteye.com/upload/attachment/0089/2566/3e2b4b61-33d4-3787-8b86-da572118371d.jpg[/img]

从上面的两个PPT截图中可以看出,拉斯维加斯算法思想去解决N皇后时所花费的时间不少。
数独中的判断不比N皇后少,如果想要在1秒内或者更短的时间产生1000个数独终盘,个人觉得并不是很容易(当然了,这个希望以后去证明一下是正确的 :))。

所以,我想选择另外的途径去完成。

[size=medium][color=blue][b]我的随机方法思想[/b][/color][/size]

在给出我的随机方法思想之前,我们先来看看数独终盘的数量。

[b]终盘数量[/b]

终盘数量数独中的数字排列千变万化,那么究竟有多少种终盘的数字组合呢?
6,670,903,752,021,072,936,960(约有[b]6.67×10的21次方[/b])种组合,2005年由Bertram Felgenhauer和Frazer Jarvis计算出该数字,并将计算方法发布在他们网站上,[b]如果将等价终盘(如旋转、翻转、行行对换,数字对换等变形)不计算,则有5,472,730,538个组合[/b]。数独终盘的组合数量都如此惊人,那么数独题目数量就更加不计其数了,因为每个数独终盘又可以制作出无数道合格的数独题目。
-- 参考自[url]http://baike.baidu.com/link?url=ePXUCvpBaRKBkEA3pVfOkg3m-NBozO6a4GDS0N3E5_gK1nnJCDzd5O-YL1w7c5S3[/url]

[color=blue][b]按照这个数量,如果我们将一个[1,2,3,4,5,6,7,8,9]的数组随机化,然后将其作为一行数据添加到一个二维数组中去,该行能满足数独终盘规则的概率是很大的。[/b][/color]

[b]基于这个假设(假设的有效性会在文章后面验证),我的随机算法思想如下:[/b]

[list]
[*][b]1. 写一个方法用于获取一个由1到9九个数随机排列的一维数组。
[*]2. 循环行(下标从0到8),将这个随机产生的一维数组作为当前行的内容,如果是第一行(行标为0),那么直接作为该行的内容。如果是其它行,则验证数据是否都符合条件。
[*] 如果符合条件,则再产生一个由1到9九个数随机排列的一维数组作为下一行的内容并验证数据是否可用。
[*] 如果不符合条件,则将该行数据设置为0,调整row和col,产生一个由1到9九个数随机排列的一维数组,重新对该行验证。
[*]3. 程序中为了防止产生一维随机数组的方法调用很多次而没有产生结果,设置一个最多调用该方法次数的阈值,当达到这个阈值还没有产生结果,重新从 row =0 col =0 开始。[/b]
[/list]

实现的程序如下:

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]

[size=medium][color=blue][b]假设有效性验证及阈值设定[/b][/color][/size]

针对上述的代码,我跑10组,每组30个实例,看看这300个例子中,产生数独终盘所需要调用随机产生由1到9的一维数组的次数各是多少, 结果如下:

[img]http://dl2.iteye.com/upload/attachment/0089/2535/0c1df9c0-bad5-3fa5-8393-d4298f635528.jpg[/img]

从上面的结果图中可以看出:[[color=blue]b]300个实例中,调用次数最小的为11,接近理想最小调用次数9.

最大值为217次,平均约50次。而且大部分实例调用的次数在100以内。

从这些数据[/color]分析中,上述假设基本上是合适的,阈值最后选择了接近实验最大值217的220.[/b]

[size=medium][color=blue][b]效果和结论[/b][/color][/size]

选择产生不同的数目的数独终盘,看看执行的时间。
[img]http://dl2.iteye.com/upload/attachment/0089/2553/1e7bb208-1dd9-3469-97e3-f370f9296e14.jpg[/img]

[img]http://dl2.iteye.com/upload/attachment/0089/2555/772bbfe7-f06e-3f93-91cb-ecb4616f65d6.jpg[/img]

[b]结论:[/b]

[b]1. 在打印二维数组到控制台显示的情况下,产生10万个随机数独终盘的时间差不多1分钟左右。
2. 在打印二维数组到控制台显示的情况下,产生1000个随机数独终盘的时间差不到1秒,这个正是我们之前所期望的。
3. 产生大数目的随机终盘,将二维数组打印到控制台显示的时间占据总时间的60%左右。
如果不在控制台输出二维数组,花费的时间将少很多。 看上面的折线就可以知道。[/b]

[b]总的来说,效果还是比较不错的,比较适合一下需要产生很多的数独终盘的情况,比如:初始化1000个或者更多个数独终盘并写入文件,然后作为数独游戏的每关内容。[/b]

在下一篇博文中将介绍使用“挖洞”的思想去生成数独难题。

使用在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();     } }}

[img]http://dl2.iteye.com/upload/attachment/0089/2537/6ab07468-e52e-305e-959b-5facb63f356d.jpg[/img]

[img]http://dl2.iteye.com/upload/attachment/0089/2539/83da46e8-2e96-3a04-b66c-0e4208ce3d24.jpg[/img]

在下一篇博文中将介绍使用“挖洞”的思想去生成数独难题。

转载请注明出处:[url]http://mouselearnjava.iteye.com/blog/1941693[/url]

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

  1. mfc做数独游戏_我终于在iPhone上找到了体验最好的数独游戏

    我已经被一个 9×9 的格子困住将近 30 分钟,它既让人深陷其中还欲罢不能. 玩<数独 2>这款 app 之前我是抗拒的,因为我对数字超级无感,但抱着朋友告诉我入门很简单后试一试的心态, ...

  2. C语言简单数独游戏终盘生成

    前言 这一篇文章介绍的是移动变换法,有详细的移动变化法的图文解析,在文末有完整的可用于查看移动变换法生成数独终盘过程的代码 实现思路 移动变换法这一方法是很简单的一种方法,实现起来也比较容易,但同时它 ...

  3. 用 JS 做一个数独游戏(二)

    用 JS 做一个数独游戏(二) 在 上一篇博客 中,我们通过 Node 运行了我们的 JavaScript 代码,在控制台中打印出来生成好的数独终盘.为了让我们的数独游戏能有良好的体验,这篇博客将会为 ...

  4. 游戏编程中的数学——随机数字生成(RNG)的黑暗秘密

    大家好,你们能听到我讲话吗?这个演讲的内容是介绍RNG(随机数字生成)的一些黑暗秘密.如你在大屏幕上看到的,Squirrel已经介绍了一些RNG的基础概念.首先,我想详细讲解几点.他的演讲更偏重理论, ...

  5. 浅谈利用NLG技术来进行游戏自动化(生成随机剧情随机对话)的可行性

    目录 背景 NLG(自然语言生成) 文本到文本生成 数据到文本生成 图像到文本生成 NLG模型的对比 NLG在游戏自动化的可能应用 根据UI图片来生成描述文本 根据背景或开头来生成剧情 根据背景或开头 ...

  6. 数独游戏的解法到App的实现

    在LeetCode上偶然刷到一个解数独的题目: 编写一个程序,通过已填充的空格来解决数独问题. 一个数独的解法需遵循如下规则: 数字 1-9 在每一行只能出现一次. 数字 1-9 在每一列只能出现一次 ...

  7. 软件工程大作业——数独游戏

    软件工程大作业--数独游戏1 一.PSP表格 二.问题分析 三.系统设计 四.具体实现 五.单元测试 六.程序性能及质量分析 七.GUI 八.总结 代码地址:https://github.com/fr ...

  8. python数独游戏源代码_使用Python编写数独游戏自动出题程序

    数独是一个很好玩的游戏,可以锻炼推理能力.下面的代码可以自动生成数独游戏题目. from random import shuffle, randrange def generate(): # 初始网格 ...

  9. 使用Python编写数独游戏自动出题程序

    数独是一个很好玩的游戏,可以锻炼推理能力.下面的代码可以自动生成数独游戏题目. from random import shuffle, randrange def generate():     # ...

  10. 软件工程基础-个人项目-数独游戏

    软件工程基础-个人项目-数独游戏 ----------------------------------------------------------------------------------- ...

最新文章

  1. feign直接走熔断_【121期】面试官:什么是熔断?什么是服务降级?
  2. DBController心得之一:利用DMO对象对SQL2005数据库进行Backup和restore的操作
  3. vn.py 2.0.1 发布,全功能交易程序开发框架
  4. 【云图】一键生成连锁店品牌地图
  5. CompletableFuture异步调用
  6. 《VMware vSphere 6.5企业运维实战》已经出版
  7. RxSwift中Observable的各种创建方法
  8. 在字节跳动的实习经历分享 | 万字求职指南
  9. android stop 服务,当调用stopService方法时服务不会停止
  10. JSP概述——什么是JSP、JSP运行原理
  11. uni-app 学习: 页面高度设置100%
  12. 战神引擎修改文件的位置
  13. linux 内存映射-ioremap和mmap函数
  14. 网上买电信电话卡又被欺骗百元
  15. Canvas 画九宫格图片
  16. 测试工程师必读经典好书,自动化测试和测试开发的同学必看
  17. 上海共享办公,创新创业空间
  18. centos系统安装中文字体几种方法
  19. Python的提交表单功能
  20. 微服务精通之Eureka原理解析

热门文章

  1. 联想i5无线网无法连接服务器,联想笔记本无线网络连接不上是什么原因
  2. 在阿里矢量库下载了字体图标在项目引入无法显示时
  3. AppScan安装使用
  4. PCB Web版SI9000阻抗计算器
  5. Hitool工具烧写程序(按分区烧写)
  6. 软件观念革命:交互设计精髓_专业科班答案:一个标准的交互设计流程是怎样的?...
  7. 三星c7000 android8,三星C7000官方固件rom刷机包-C7000ZCU3CRI1 安卓8.0
  8. 如何寻找、下载期刊投稿的LaTeX模板
  9. 【区块链】量子链命令行qtum-cli全命令详解
  10. 麻省理工18年春软件构造课程阅读01“静态检查”