用Java实现N*N的标准数独及对角线数独解题

来源:互联网 发布:ubuntu 自动挂载硬盘 编辑:程序博客网 时间:2024/05/22 07:55
<span style="font-size: x-large;"><strong>1、引言</strong></span>

前一段时间迷上了数独游戏(不知道数独的同学请自行百度,或点这里了解),就有了想编程实现解题的想法。一直拖到最近,终于抽空使用Java实现了3*3标准数独的解题,并在其基础上抽象出了N*N的标准数独及对角线数独的解题。现和众位分享相关的代码和过程。

特别说明:这里的N*N标准数独,指的是N=n*n(n为正整数),即4*4、9*9、16*16、25*25……(n=1没意义)

2、解题思路
数独的解题方法有很多种,有兴趣的同学可以自行百度或点这里了解。

我使用的是最简单的,也是比较容易实现的基础摒除法。
在每个空格上,都递归尝试1~N的每个值的可能情况,校验每个值是否符合基础摒除法,不符合则尝试下一个,直至尝试N个值结束。
引用

基础摒除法就是利用1~N的值在每一行、每一列、每一个N宫格都只能出现一次的规则进行解题的方法。基础摒除法可以分为行摒除、列摒除、N宫格摒除。


3、实现过程
3.1 几个定义
1、N宫格中,“空格”的定义,采用空字符串(”“)或0表示;

2、N宫格中,行或列的索引定义,为编码方便采用0~(N-1)来表示,如:第4行第5列的格子对应的行列索引为:row=3和col=4;

3、N宫格中,填空内容的定义,采用长度为N的一维数组表示,如:
n=2,即N=4
Java代码  收藏代码
  1. // 4*4填空内容  
  2. String[] dataArray = new String[] {  
  3.     ”1”“2”“3”“4”  
  4. });  
// 4*4填空内容String[] dataArray = new String[] {    "1", "2", "3", "4"});


n=3,即N=9

Java代码  收藏代码
  1. // 9*9填空内容  
  2. String[] dataArray = new String[] {  
  3.     ”1”“2”“3”“4”“5”“6”“7”“8”“9”  
  4. });  
// 9*9填空内容 
String[] dataArray = new String[] {
"1", "2", "3", "4", "5", "6", "7", "8", "9"
});


n=4,即N=16

Java代码  收藏代码
  1. // 16*16填空内容  
  2. String[] dataArray = new String[] {  
  3.     ”1”“2”“3”“4”“5”“6”“7”“8”“9”“A”“B”“C”“D”“E”“F”“G”  
  4. });  
// 16*16填空内容 
String[] dataArray = new String[] {
"1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G"
});



当然,N宫格的填空内容,不局限于1~N的数值,你可以使用任何非“空格”的且互不一样的字符串,如:

Java代码  收藏代码
  1. // 4*4填空内容  
  2. String[] dataArray = new String[] {  
  3.     ”张三”“李四”“王五”“赵六”  
  4. });  
// 4*4填空内容 
String[] dataArray = new String[] {
"张三", "李四", "王五", "赵六"
});



4、N宫格中,N*N的数独题目或填空结果定义,采用长度为N*N的二维数组表示,如:

Java代码  收藏代码
  1. // 9*9数独题目  
  2. String[][] resultArray = new String[][] {  
  3.     {  
  4.         ”0”“2”“0”“0”“0”“0”“0”“0”“0”  
  5.     }, {  
  6.         ”5”“0”“6”“0”“0”“0”“3”“0”“9”  
  7.     }, {  
  8.         ”0”“8”“0”“5”“0”“2”“0”“6”“0”  
  9.     }, {  
  10.         ”0”“0”“5”“0”“7”“0”“1”“0”“0”  
  11.     }, {  
  12.         ”0”“0”“0”“2”“0”“8”“0”“0”“0”  
  13.     }, {  
  14.         ”0”“0”“4”“0”“1”“0”“8”“0”“0”  
  15.     }, {  
  16.         ”0”“5”“0”“8”“0”“7”“0”“3”“0”  
  17.     }, {  
  18.         ”7”“0”“2”“0”“0”“0”“4”“0”“5”  
  19.     }, {  
  20.         ”0”“4”“0”“0”“0”“0”“0”“7”“0”  
  21.     }  
  22. };  
// 9*9数独题目 
String[][] resultArray = new String[][] {
{
"0", "2", "0", "0", "0", "0", "0", "0", "0"
}, {
"5", "0", "6", "0", "0", "0", "3", "0", "9"
}, {
"0", "8", "0", "5", "0", "2", "0", "6", "0"
}, {
"0", "0", "5", "0", "7", "0", "1", "0", "0"
}, {
"0", "0", "0", "2", "0", "8", "0", "0", "0"
}, {
"0", "0", "4", "0", "1", "0", "8", "0", "0"
}, {
"0", "5", "0", "8", "0", "7", "0", "3", "0"
}, {
"7", "0", "2", "0", "0", "0", "4", "0", "5"
}, {
"0", "4", "0", "0", "0", "0", "0", "7", "0"
}
};



5、N宫格中,每个格子的索引定义,采用从0开始的整数来表示,那么N*N格子索引范围为:0~(N*N-1),如:9*9的索引范围为0~80,那么,格子索引和行列索引可以进行相关的换算;


3.2 基础摒除法的Java实现

1、行摒除

Java代码  收藏代码
  1. / 
  2.   行校验 
  3.   @param resultArray 
  4.   @param row 
  5.   @param value 
  6.   @return 
  7.  /  
  8. private static boolean checkRow(final String[][] resultArray, int row, String value) {  
  9.     int arrayLen = resultArray.length;  
  10.     for (int i = 0; i < arrayLen; i++) {  
  11.         if (value.equals(resultArray[row][i])) {  
  12.             return false;  
  13.         }  
  14.     }  
  15.     return true;  
  16. }  
/ 
* 行校验
* @param resultArray
* @param row
* @param value
* @return
*/
private static boolean checkRow(final String[][] resultArray, int row, String value) {
int arrayLen = resultArray.length;
for (int i = 0; i < arrayLen; i++) {
if (value.equals(resultArray[row][i])) {
return false;
}
}
return true;
}



2、列摒除

Java代码  收藏代码
  1. / 
  2.   列校验 
  3.   @param resultArray 
  4.   @param col 
  5.   @param value 
  6.   @return 
  7.  /  
  8. private static boolean checkColumn(final String[][] resultArray, int col, String value) {  
  9.     int arrayLen = resultArray.length;  
  10.     for (int i = 0; i < arrayLen; i++) {  
  11.         if (value.equals(resultArray[i][col])) {  
  12.             return false;  
  13.         }  
  14.     }  
  15.     return true;  
  16. }  
/ 
* 列校验
* @param resultArray
* @param col
* @param value
* @return
*/
private static boolean checkColumn(final String[][] resultArray, int col, String value) {
int arrayLen = resultArray.length;
for (int i = 0; i < arrayLen; i++) {
if (value.equals(resultArray[i][col])) {
return false;
}
}
return true;
}



3、N宫摒除

这里实现比较难的一点在于,根据给定行、列计算其所在宫的行列开始值

Java代码  收藏代码
  1. / 
  2.   宫校验 
  3.   @param resultArray 
  4.   @param row 
  5.   @param col 
  6.   @param value 
  7.   @return 
  8.  /  
  9. private static boolean checkBlock(final String[][] resultArray, int row, int col, String value) {  
  10.     int arrayLen = resultArray.length;  
  11.     int blockLen = (int) Math.sqrt(arrayLen);  
  12.     int blockRowIndex = (int) row / blockLen;  
  13.     int blockColIndex = (int) col / blockLen;  
  14.     int blockRowStart = blockLen  blockRowIndex;  
  15.     int blockColStart = blockLen * blockColIndex;  
  16.   
  17.     for (int i = 0; i < blockLen; i++) {  
  18.         int rowIndex = blockRowStart + i;  
  19.         for (int j = 0; j < blockLen; j++) {  
  20.             int colIndex = blockColStart + j;  
  21.             if (value.equals(resultArray[rowIndex][colIndex])) {  
  22.                 return false;  
  23.             }  
  24.         }  
  25.     }  
  26.     return true;  
  27. }  
/ 
* 宫校验
* @param resultArray
* @param row
* @param col
* @param value
* @return
*/
private static boolean checkBlock(final String[][] resultArray, int row, int col, String value) {
int arrayLen = resultArray.length;
int blockLen = (int) Math.sqrt(arrayLen);
int blockRowIndex = (int) row / blockLen;
int blockColIndex = (int) col / blockLen;
int blockRowStart = blockLen * blockRowIndex;
int blockColStart = blockLen * blockColIndex;

for (int i = 0; i &lt; blockLen; i++) {    int rowIndex = blockRowStart + i;    for (int j = 0; j &lt; blockLen; j++) {        int colIndex = blockColStart + j;        if (value.equals(resultArray[rowIndex][colIndex])) {            return false;        }    }}return true;
}



4、对角线摒除(左上至右下)

Java代码  收藏代码
  1. / 
  2.   对角线校验(左上至右下) 
  3.   @param resultArray 
  4.   @param value 
  5.   @return 
  6.  */  
  7. private static boolean checkLeftTop2RightBottom(final String[][] resultArray, int row, int col, String value) {  
  8.     if (row == col) {  
  9.         int arrayLen = resultArray.length;  
  10.         for (int i = 0; i < arrayLen; i++) {  
  11.             if (value.equals(resultArray[i][i])) {  
  12.                 return false;  
  13.             }  
  14.         }  
  15.     }  
  16.     return true;  
  17. }  
/ 
* 对角线校验(左上至右下)
* @param resultArray
* @param value
* @return
*/
private static boolean checkLeftTop2RightBottom(final String[][] resultArray, int row, int col, String value) {
if (row == col) {
int arrayLen = resultArray.length;
for (int i = 0; i < arrayLen; i++) {
if (value.equals(resultArray[i][i])) {
return false;
}
}
}
return true;
}



5、对角线摒除(左下至右上)

Java代码  收藏代码
  1. / 
  2.   对角线校验(左下至右上) 
  3.   @param resultArray 
  4.   @param value 
  5.   @return 
  6.  */  
  7. private static boolean checkLeftBottom2RightTop(final String[][] resultArray, int row, int col, String value) {  
  8.     int arrayLen = resultArray.length;  
  9.     if ((row + col) == (arrayLen - 1)) {  
  10.         for (int i = 0; i < arrayLen; i++) {  
  11.             if (value.equals(resultArray[arrayLen - i - 1][i])) {  
  12.                 return false;  
  13.             }  
  14.         }  
  15.     }  
  16.     return true;  
  17. }  
/ 
* 对角线校验(左下至右上)
* @param resultArray
* @param value
* @return
*/
private static boolean checkLeftBottom2RightTop(final String[][] resultArray, int row, int col, String value) {
int arrayLen = resultArray.length;
if ((row + col) == (arrayLen - 1)) {
for (int i = 0; i < arrayLen; i++) {
if (value.equals(resultArray[arrayLen - i - 1][i])) {
return false;
}
}
}
return true;
}



6、基础摒除法

Java代码  收藏代码
  1. / 
  2.   执行所有校验 
  3.   @param resultArray 
  4.   @param row 
  5.   @param col 
  6.   @param value 
  7.   @param checkCross 
  8.   @return 
  9.  /  
  10. private static boolean checkAll(final String[][] resultArray, int row, int col, String value, boolean checkCross) {  
  11.     // 行校验  
  12.     if (!checkRow(resultArray, row, value)) {  
  13.         return false;  
  14.     }  
  15.   
  16.     // 列校验  
  17.     if (!checkColumn(resultArray, col, value)) {  
  18.         return false;  
  19.     }  
  20.   
  21.     // 宫校验  
  22.     if (!checkBlock(resultArray, row, col, value)) {  
  23.         return false;  
  24.     }  
  25.   
  26.     // 对角线校验  
  27.     if (checkCross) {  
  28.         // 对角线校验(左上至右下)  
  29.         if (!checkLeftTop2RightBottom(resultArray, row, col, value)) {  
  30.             return false;  
  31.         }  
  32.         // 对角线校验(左下至右上)  
  33.         if (!checkLeftBottom2RightTop(resultArray, row, col, value)) {  
  34.             return false;  
  35.         }  
  36.     }  
  37.   
  38.     return true;  
  39. }  
/ 
* 执行所有校验
* @param resultArray
* @param row
* @param col
* @param value
* @param checkCross
* @return
*/
private static boolean checkAll(final String[][] resultArray, int row, int col, String value, boolean checkCross) {
// 行校验
if (!checkRow(resultArray, row, value)) {
return false;
}

// 列校验if (!checkColumn(resultArray, col, value)) {    return false;}// 宫校验if (!checkBlock(resultArray, row, col, value)) {    return false;}// 对角线校验if (checkCross) {    // 对角线校验(左上至右下)    if (!checkLeftTop2RightBottom(resultArray, row, col, value)) {        return false;    }    // 对角线校验(左下至右上)    if (!checkLeftBottom2RightTop(resultArray, row, col, value)) {        return false;    }}return true;
}



3.3 解题实现

解题采用递归的方式进行,直至解题完成并打印出结果,相关实现代码如下

1、校验是否已填好的实现如下:

Java代码  收藏代码
  1. / 
  2.   校验是否已经填好 
  3.   @param value 
  4.   @return 
  5.  /  
  6. private static boolean isUnselect(String value) {  
  7.     return “”.equals(value) || “0”.equals(value);  
  8. }  
/ 
* 校验是否已经填好
* @param value
* @return
*/
private static boolean isUnselect(String value) {
return "".equals(value) || "0".equals(value);
}



2、递归过程的填空结果传递,需要复制二维数组,其实现如下:

Java代码  收藏代码
  1. / 
  2.   复制数组 
  3.   @param array 
  4.   @return 
  5.  /  
  6. private static String[][] copyArray(final String[][] array) {  
  7.     int rowCount = array.length;  
  8.     int colCount = array[0].length;  
  9.     String[][] copy = new String[rowCount][colCount];  
  10.     for (int i = 0; i < rowCount; i++) {  
  11.         for (int j = 0; j < colCount; j++) {  
  12.             copy[i][j] = array[i][j];  
  13.         }  
  14.     }  
  15.     return copy;  
  16. }  
/ 
* 复制数组
* @param array
* @return
*/
private static String[][] copyArray(final String[][] array) {
int rowCount = array.length;
int colCount = array[0].length;
String[][] copy = new String[rowCount][colCount];
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < colCount; j++) {
copy[i][j] = array[i][j];
}
}
return copy;
}



3、打印解题结果的实现代码如下:

Java代码  收藏代码
  1. / 
  2.   输出结果 
  3.   @param resultArray 
  4.  */  
  5. private static void printResult(final String[][] resultArray) {  
  6.     System.out.println(”\n——————————–”);  
  7.     int arrayLen = resultArray.length;  
  8.     for (int i = 0; i < arrayLen; i++) {  
  9.         System.out.println(Arrays.asList(resultArray[i]));  
  10.     }  
  11. }  
/ 
* 输出结果
* @param resultArray
*/
private static void printResult(final String[][] resultArray) {
System.out.println("\n--------------------------------");
int arrayLen = resultArray.length;
for (int i = 0; i < arrayLen; i++) {
System.out.println(Arrays.asList(resultArray[i]));
}
}



4、递归解题算法(核心)

Java代码  收藏代码
  1. / 
  2.   数独解题 
  3.   @param dataArray 待选列表 
  4.   @param resultArray 前面(resultIndex-1)个的填空结果 
  5.   @param resultIndex 选择索引,从0开始 
  6.   @param checkCross 是否是对角线数独 
  7.  /  
  8. private static void sudoSelect(String[] dataArray, final String[][] resultArray, int resultIndex, boolean checkCross) {  
  9.     int resultLen = resultArray.length;  
  10.     if (resultIndex >= (int) Math.pow(resultLen, 2)) {  
  11.         // 全部填完时,输出排列结果  
  12.         printResult(resultArray);  
  13.         return;  
  14.     }  
  15.   
  16.     int row = (int) resultIndex / resultLen;  
  17.     int col = resultIndex % resultLen;  
  18.     if (isUnselect(resultArray[row][col])) {  
  19.         // 逐个尝试,递归选择下一个  
  20.         for (int i = 0; i < dataArray.length; i++) {  
  21.             if (checkAll(resultArray, row, col, dataArray[i], checkCross)) {  
  22.                 // 排列结果不存在该项,才可选择  
  23.                 String[][] resultCopy = copyArray(resultArray);  
  24.   
  25.                 resultCopy[row][col] = dataArray[i];  
  26.                 sudoSelect(dataArray, resultCopy, resultIndex + 1, checkCross);  
  27.             }  
  28.         }  
  29.     } else {  
  30.         // 递归选择下一个  
  31.         String[][] resultCopy = copyArray(resultArray);  
  32.         sudoSelect(dataArray, resultCopy, resultIndex + 1, checkCross);  
  33.     }  
  34. }  
/ 
* 数独解题
* @param dataArray 待选列表
* @param resultArray 前面(resultIndex-1)个的填空结果
* @param resultIndex 选择索引,从0开始
* @param checkCross 是否是对角线数独
*/
private static void sudoSelect(String[] dataArray, final String[][] resultArray, int resultIndex, boolean checkCross) {
int resultLen = resultArray.length;
if (resultIndex >= (int) Math.pow(resultLen, 2)) {
// 全部填完时,输出排列结果
printResult(resultArray);
return;
}

int row = (int) resultIndex / resultLen;int col = resultIndex % resultLen;if (isUnselect(resultArray[row][col])) {    // 逐个尝试,递归选择下一个    for (int i = 0; i &lt; dataArray.length; i++) {        if (checkAll(resultArray, row, col, dataArray[i], checkCross)) {            // 排列结果不存在该项,才可选择            String[][] resultCopy = copyArray(resultArray);            resultCopy[row][col] = dataArray[i];            sudoSelect(dataArray, resultCopy, resultIndex + 1, checkCross);        }    }} else {    // 递归选择下一个    String[][] resultCopy = copyArray(resultArray);    sudoSelect(dataArray, resultCopy, resultIndex + 1, checkCross);}
}



3.4 其它

1、根据待选数组,初始化生成二维结果数组;

Java代码  收藏代码
  1. / 
  2.   初始化结果数组 
  3.   @param dataArray 待选列表 
  4.   @return 
  5.  /  
  6. public static String[][] initResultArray(String[] dataArray) {  
  7.     int arrayLen = dataArray.length;  
  8.     String[][] resultArray = new String[arrayLen][arrayLen];  
  9.     for (int i = 0; i < arrayLen; i++) {  
  10.         for (int j = 0; j < arrayLen; j++) {  
  11.             resultArray[i][j] = ”0”;  
  12.         }  
  13.     }  
  14.     return resultArray;  
  15. }  
/ 
* 初始化结果数组
* @param dataArray 待选列表
* @return
*/
public static String[][] initResultArray(String[] dataArray) {
int arrayLen = dataArray.length;
String[][] resultArray = new String[arrayLen][arrayLen];
for (int i = 0; i < arrayLen; i++) {
for (int j = 0; j < arrayLen; j++) {
resultArray[i][j] = "0";
}
}
return resultArray;
}



2、根据N*N长度的字符串,初始化生成二维结果数组;

Java代码  收藏代码
  1. / 
  2.   初始化结果数组 
  3.   @param resultString 结果字符串 
  4.   @return 
  5.  /  
  6. public static String[][] initResultArray(String resultString) {  
  7.     int arrayLen = (int) Math.sqrt(resultString.length());  
  8.     String[][] resultArray = new String[arrayLen][arrayLen];  
  9.     for (int i = 0; i < arrayLen; i++) {  
  10.         for (int j = 0; j < arrayLen; j++) {  
  11.             resultArray[i][j] = ”“ + resultString.charAt(i * arrayLen + j);  
  12.         }  
  13.     }  
  14.     return resultArray;  
  15. }  
/ 
* 初始化结果数组
* @param resultString 结果字符串
* @return
*/
public static String[][] initResultArray(String resultString) {
int arrayLen = (int) Math.sqrt(resultString.length());
String[][] resultArray = new String[arrayLen][arrayLen];
for (int i = 0; i < arrayLen; i++) {
for (int j = 0; j < arrayLen; j++) {
resultArray[i][j] = "" + resultString.charAt(i * arrayLen + j);
}
}
return resultArray;
}



4、测试

4.1 测试代码

1、为测试方便,进行了几个封装

Java代码  收藏代码
  1. / 
  2.   9*9数独给定已选字符串求解 
  3.   @param resultString 数独题目 
  4.  /  
  5. public static void sudoSelect(String resultString) {  
  6.     String[][] resultArray = initResultArray(resultString);  
  7.     sudoSelect(new String[] {  
  8.             ”1”“2”“3”“4”“5”“6”“7”“8”“9”  
  9.     }, resultArray);  
  10. }  
  11.   
  12. / 
  13.   N*N数独给定结果数组求解 
  14.   @param dataArray 待选列表 
  15.   @param resultArray 已选结果数组 
  16.  /  
  17. public static void sudoSelect(String[] dataArray, final String[][] resultArray) {  
  18.     sudoSelect(dataArray, resultArray, false);  
  19. }  
  20.   
  21. / 
  22.   排列选择(从列表中选择n个排列) 
  23.   @param dataArray 待选列表 
  24.   @param resultArray 已选结果 
  25.   @param checkCross 是否校验对角线 
  26.  /  
  27. public static void sudoSelect(String[] dataArray, final String[][] resultArray, boolean checkCross) {  
  28.     sudoSelect(dataArray, resultArray, 0, checkCross);  
  29. }  
/ 
* 9*9数独给定已选字符串求解
* @param resultString 数独题目
*/
public static void sudoSelect(String resultString) {
String[][] resultArray = initResultArray(resultString);
sudoSelect(new String[] {
“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”
}, resultArray);
}

/**
* N*N数独给定结果数组求解
* @param dataArray 待选列表
* @param resultArray 已选结果数组
*/
public static void sudoSelect(String[] dataArray, final String[][] resultArray) {
sudoSelect(dataArray, resultArray, false);
}

/**
* 排列选择(从列表中选择n个排列)
* @param dataArray 待选列表
* @param resultArray 已选结果
* @param checkCross 是否校验对角线
*/
public static void sudoSelect(String[] dataArray, final String[][] resultArray, boolean checkCross) {
sudoSelect(dataArray, resultArray, 0, checkCross);
}



2、测试入口

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2.     // 求解给定数独所有可能  
  3.     sudoSelect(new String[] {  
  4.             ”1”“2”“3”“4”“5”“6”“7”“8”“9”  
  5.     }, new String[][] {  
  6.             {  
  7.                     ”9”“1”“2”“0”“0”“7”“0”“5”“0”  
  8.             }, {  
  9.                     ”0”“0”“3”“0”“5”“9”“0”“2”“1”  
  10.             }, {  
  11.                     ”0”“0”“5”“4”“1”“2”“0”“0”“9”  
  12.             }, {  
  13.                     ”0”“8”“0”“0”“4”“5”“9”“0”“2”  
  14.             }, {  
  15.                     ”0”“0”“0”“0”“7”“0”“5”“0”“0”  
  16.             }, {  
  17.                     ”5”“0”“4”“0”“6”“0”“0”“1”“0”  
  18.             }, {  
  19.                     ”0”“0”“0”“5”“0”“6”“0”“0”“0”  
  20.             }, {  
  21.                     ”2”“5”“0”“7”“0”“0”“8”“0”“0”  
  22.             }, {  
  23.                     ”0”“3”“0”“0”“0”“0”“0”“9”“5”  
  24.             }  
  25.     });  
  26.   
  27.     // 求解给定数独所有可能  
  28.     // http://tieba.baidu.com/p/4813549830  
  29.     // #9806 002300609000000075100060000504100008060050040800007102000030001250000000907004200  
  30.     // #9807 010000000000294000008300709180002040050000080030800096401003800000471000000000020  
  31.     // #9808 100200905000080000400600023010005060000060000050400030840001007000070000507002001  
  32.     // #9809 300500090400000500002310000053080010000090000060050370000021800001000004080007006  
  33.     // #9810 010500000090073000804020000400000100780060029002000005000030207000480060000006090  
  34.     sudoSelect(”002300609000000075100060000504100008060050040800007102000030001250000000907004200”);  
  35.     sudoSelect(”010000000000294000008300709180002040050000080030800096401003800000471000000000020”);  
  36.     sudoSelect(”100200905000080000400600023010005060000060000050400030840001007000070000507002001”);  
  37.     sudoSelect(”300500090400000500002310000053080010000090000060050370000021800001000004080007006”);  
  38.     sudoSelect(”010500000090073000804020000400000100780060029002000005000030207000480060000006090”);  
  39. }  
public static void main(String[] args) { 
// 求解给定数独所有可能
sudoSelect(new String[] {
“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”
}, new String[][] {
{
“9”, “1”, “2”, “0”, “0”, “7”, “0”, “5”, “0”
}, {
“0”, “0”, “3”, “0”, “5”, “9”, “0”, “2”, “1”
}, {
“0”, “0”, “5”, “4”, “1”, “2”, “0”, “0”, “9”
}, {
“0”, “8”, “0”, “0”, “4”, “5”, “9”, “0”, “2”
}, {
“0”, “0”, “0”, “0”, “7”, “0”, “5”, “0”, “0”
}, {
“5”, “0”, “4”, “0”, “6”, “0”, “0”, “1”, “0”
}, {
“0”, “0”, “0”, “5”, “0”, “6”, “0”, “0”, “0”
}, {
“2”, “5”, “0”, “7”, “0”, “0”, “8”, “0”, “0”
}, {
“0”, “3”, “0”, “0”, “0”, “0”, “0”, “9”, “5”
}
});

// 求解给定数独所有可能// http://tieba.baidu.com/p/4813549830// #9806 002300609000000075100060000504100008060050040800007102000030001250000000907004200// #9807 010000000000294000008300709180002040050000080030800096401003800000471000000000020// #9808 100200905000080000400600023010005060000060000050400030840001007000070000507002001// #9809 300500090400000500002310000053080010000090000060050370000021800001000004080007006// #9810 010500000090073000804020000400000100780060029002000005000030207000480060000006090sudoSelect("002300609000000075100060000504100008060050040800007102000030001250000000907004200");sudoSelect("010000000000294000008300709180002040050000080030800096401003800000471000000000020");sudoSelect("100200905000080000400600023010005060000060000050400030840001007000070000507002001");sudoSelect("300500090400000500002310000053080010000090000060050370000021800001000004080007006");sudoSelect("010500000090073000804020000400000100780060029002000005000030207000480060000006090");
}



4.2 测试结果

1、运行测试代码,控制台输出结果如下:

Java代码  收藏代码
  1. ——————————–  
  2. [912637458]  
  3. [643859721]  
  4. [875412639]  
  5. [187345962]  
  6. [369271584]  
  7. [524968317]  
  8. [498526173]  
  9. [251793846]  
  10. [736184295]  
  11. ——————————–  
  12. [482375619]  
  13. [396421875]  
  14. [175869324]  
  15. [524193768]  
  16. [761258943]  
  17. [839647152]  
  18. [648732591]  
  19. [253916487]  
  20. [917584236]  
  21. ——————————–  
  22. [619758234]  
  23. [573294618]  
  24. [248316759]  
  25. [186932547]  
  26. [954167382]  
  27. [732845196]  
  28. [491623875]  
  29. [825471963]  
  30. [367589421]  
  31. ——————————–  
  32. [176234985]  
  33. [325189476]  
  34. [498657123]  
  35. [713925864]  
  36. [284763519]  
  37. [659418732]  
  38. [842391657]  
  39. [931576248]  
  40. [567842391]  
  41. ——————————–  
  42. [376548291]  
  43. [418279563]  
  44. [592316748]  
  45. [953784612]  
  46. [127693485]  
  47. [864152379]  
  48. [645921837]  
  49. [731865924]  
  50. [289437156]  
  51. ——————————–  
  52. [316548972]  
  53. [295173648]  
  54. [874629513]  
  55. [453792186]  
  56. [781365429]  
  57. [962814735]  
  58. [648931257]  
  59. [529487361]  
  60. [137256894]  
——————————– 
[9, 1, 2, 6, 3, 7, 4, 5, 8]
[6, 4, 3, 8, 5, 9, 7, 2, 1]
[8, 7, 5, 4, 1, 2, 6, 3, 9]
[1, 8, 7, 3, 4, 5, 9, 6, 2]
[3, 6, 9, 2, 7, 1, 5, 8, 4]
[5, 2, 4, 9, 6, 8, 3, 1, 7]
[4, 9, 8, 5, 2, 6, 1, 7, 3]
[2, 5, 1, 7, 9, 3, 8, 4, 6]

[7, 3, 6, 1, 8, 4, 2, 9, 5]

[4, 8, 2, 3, 7, 5, 6, 1, 9]
[3, 9, 6, 4, 2, 1, 8, 7, 5]
[1, 7, 5, 8, 6, 9, 3, 2, 4]
[5, 2, 4, 1, 9, 3, 7, 6, 8]
[7, 6, 1, 2, 5, 8, 9, 4, 3]
[8, 3, 9, 6, 4, 7, 1, 5, 2]
[6, 4, 8, 7, 3, 2, 5, 9, 1]
[2, 5, 3, 9, 1, 6, 4, 8, 7]

[9, 1, 7, 5, 8, 4, 2, 3, 6]

[6, 1, 9, 7, 5, 8, 2, 3, 4]
[5, 7, 3, 2, 9, 4, 6, 1, 8]
[2, 4, 8, 3, 1, 6, 7, 5, 9]
[1, 8, 6, 9, 3, 2, 5, 4, 7]
[9, 5, 4, 1, 6, 7, 3, 8, 2]
[7, 3, 2, 8, 4, 5, 1, 9, 6]
[4, 9, 1, 6, 2, 3, 8, 7, 5]
[8, 2, 5, 4, 7, 1, 9, 6, 3]

[3, 6, 7, 5, 8, 9, 4, 2, 1]

[1, 7, 6, 2, 3, 4, 9, 8, 5]
[3, 2, 5, 1, 8, 9, 4, 7, 6]
[4, 9, 8, 6, 5, 7, 1, 2, 3]
[7, 1, 3, 9, 2, 5, 8, 6, 4]
[2, 8, 4, 7, 6, 3, 5, 1, 9]
[6, 5, 9, 4, 1, 8, 7, 3, 2]
[8, 4, 2, 3, 9, 1, 6, 5, 7]
[9, 3, 1, 5, 7, 6, 2, 4, 8]

[5, 6, 7, 8, 4, 2, 3, 9, 1]

[3, 7, 6, 5, 4, 8, 2, 9, 1]
[4, 1, 8, 2, 7, 9, 5, 6, 3]
[5, 9, 2, 3, 1, 6, 7, 4, 8]
[9, 5, 3, 7, 8, 4, 6, 1, 2]
[1, 2, 7, 6, 9, 3, 4, 8, 5]
[8, 6, 4, 1, 5, 2, 3, 7, 9]
[6, 4, 5, 9, 2, 1, 8, 3, 7]
[7, 3, 1, 8, 6, 5, 9, 2, 4]

[2, 8, 9, 4, 3, 7, 1, 5, 6]

[3, 1, 6, 5, 4, 8, 9, 7, 2]
[2, 9, 5, 1, 7, 3, 6, 4, 8]
[8, 7, 4, 6, 2, 9, 5, 1, 3]
[4, 5, 3, 7, 9, 2, 1, 8, 6]
[7, 8, 1, 3, 6, 5, 4, 2, 9]
[9, 6, 2, 8, 1, 4, 7, 3, 5]
[6, 4, 8, 9, 3, 1, 2, 5, 7]
[5, 2, 9, 4, 8, 7, 3, 6, 1]
[1, 3, 7, 2, 5, 6, 8, 9, 4]



2、经校验结果正确。


5、思考

1、给定的数独题目可能不止一个解,本方法可以给出所有的可能结果,极限情况:当给定的是一个空的二维数组时,则可以输出N*N的所有终盘组合;


如:可以通过下面的测试代码,得到4*4数独的所有终盘组合,其数量为288

Java代码  收藏代码
  1. String[] dataArray = new String[] {  
  2.     ”1”“2”“3”“4”  
  3. };  
  4. String[][] resultArray = initResultArray(dataArray);  
  5. sudoSelect(dataArray, resultArray);  
String[] dataArray = new String[] { 
"1", "2", "3", "4"
};
String[][] resultArray = initResultArray(dataArray);
sudoSelect(dataArray, resultArray);



9*9数独的所有终盘组合,百度贴吧给出的终盘数量为6,670,903,752,021,072,936,960(约为6.67×10的21次方)种组合,有兴趣的童靴可以使用下面的测试代码进行测试(反正我运行了好久都没运行完

Java代码  收藏代码
  1. String[] dataArray = new String[] {  
  2.     ”1”“2”“3”“4”“5”“6”“7”“8”“9”  
  3. };  
  4. String[][] resultArray = initResultArray(dataArray);  
  5. sudoSelect(dataArray, resultArray);  
String[] dataArray = new String[] { 
"1", "2", "3", "4", "5", "6", "7", "8", "9"
};
String[][] resultArray = initResultArray(dataArray);
sudoSelect(dataArray, resultArray);



2、算法的时间复杂度比较大,可以在递归尝试1~N时,跳过行、列、宫中已存在的数值,后续可以优化,当然也可以采用其它的解题方法,但代码实现相对就会比较复杂了;


3、在相关实现代码的基础上,实现数独生成器也就不难了,具体思路可以如下:

(1)随机生成行、列的索引,这就形成了随机格子,然后判断格子是否为“空格”,若已填则重新随机直至格子为“空格”;

(2)生成随机值填充(1)中出现的随机格子,通过基础摒除法校验,直至随机的值可用,若一直不可用,则进入(1)重新生成;

(3)循环生成并填充不定数量的格子(一般建议是23—30个),然后尝试解题,若无解则进入(1)重新生成;

(4)数独题目生成了,可根据题目已填充格子的数量以及解的数量进行划分难度;


上述的过程中,可能经常出现生成的题目无解,效率比较低,但该方法完全随机且保证了有解。

网上有其它的生成策略,比如:采用挖空法,即对已知的终盘进行挖空不定数量的随机行列格子,另外,可以结合宫内行列调整和宫外行列的调换来实现。



6、源代码

完整源代码点击这里下载

0 0
原创粉丝点击