package cn.tiger.funny;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 计算数独的所有解
* @author jyuan
*
*/
public class Sudoku {
private final Integer[] numbers = {1,2,3,4,5,6,7,8,9};
private List<Integer[][]> answer = new ArrayList<Integer[][]>();
/**
* 获取数独的所有解
* @param squared 数独
* @return 数独的所有解
*/
public List<Integer[][]> calculate(Integer[][] squared) {
long start = System.currentTimeMillis();
FillInTheDeterminedValue(squared);
startCalculate(squared);
long end = System.currentTimeMillis();
System.out.println("take times:" + (end-start) + "ms.");
print();
return answer;
}
/**
* 数独中根据现有的数据,将能确定的值先填上
* @param squared 数独
*/
private void FillInTheDeterminedValue(Integer[][] squared) {
boolean falg = true;
do {
if(!determinedViaValue(squared)
&& !determinedViaExclusionMethod(squared)) {
falg = false;
}
} while (falg);
}
/**
* 如果当前小格相关联的行,列,格已经包含其他八个数字,则当前小格数字确认
* @param squared 数独
* @return 如果数独发生改变,则return true
*/
private boolean determinedViaValue(Integer[][] squared) {
boolean changed = false;
for (int i = 0; i < squared.length; i++) {
for (int j = 0; j < squared[0].length; j++) {
if(squared[i][j] != 0) {
continue;
}
Set<Integer> exclusion = getUsedInIndex_x(squared, i);
exclusion.addAll(getUsedInIndex_y(squared, j));
exclusion.addAll(getUsedInSquared(squared, i, j));
if(exclusion.size()==8) {
for (Integer number : numbers) {
if(!exclusion.contains(number)) {
changed = true;
squared[i][j] = number;
}
}
}
}
}
return changed;
}
/**
* 通过排除法确认唯一值
* eg.
* 如果同一行别的格出现过数字7,则这一列都不能为数字7
* @param squared 数独
* @return 如果数独发生改变,则return true
*/
private boolean determinedViaExclusionMethod(Integer[][] squared) {
boolean changed = false;
for(int num : numbers) {
boolean[][] table = new boolean[9][9];
setTable(squared, table, num);
changed = excludeRow(squared, table, num)
|| excludeColumn(squared, table, num)
|| excludeCell(squared, table, num);
}
return changed;
}
/**
* 如果对应数独位置数字为0,则table对应位置不变。
* 如果对应数独位置数字不为零且不等于num,则将table该位置设为true。
* 如果对应数独位置数字不为零且等于num,则将该位置对应的行、列、以及所在九格全部设为true。
*
* target:
* 如果table每行每列每小九格仅有一个false,则这一格false必为num。
*
* @param squared 数独
* @param table 排除法工具表,对应数独
* @param num 当前检测的数字
*/
private void setTable(Integer[][] squared, boolean[][] table, int num) {
for (int i = 0; i < squared.length; i++) {
for (int j = 0; j < squared[0].length; j++) {
if(squared[i][j] == 0) {
continue;
} else if (squared[i][j] != 0 && squared[i][j] != num) {
table[i][j] = true;
} else if (squared[i][j] != 0 && squared[i][j] == num) {
for(int index = 0 ; index < 9 ; index++) {
table[index][j] = true;
table[i][index] = true;
}
int smallSquared = getSmallSquared(i, j);
switch (smallSquared) {
case 1: {
setSmallSquaredInTable(table, 1, 1);
break;
}
case 2: {
setSmallSquaredInTable(table, 1, 4);
break;
}
case 3: {
setSmallSquaredInTable(table, 1, 7);
break;
}
case 4: {
setSmallSquaredInTable(table, 4, 1);
break;
}
case 5: {
setSmallSquaredInTable(table, 4, 4);
break;
}
case 6: {
setSmallSquaredInTable(table, 4, 7);
break;
}
case 7: {
setSmallSquaredInTable(table, 7, 1);
break;
}
case 8: {
setSmallSquaredInTable(table, 7, 4);
break;
}
case 9: {
setSmallSquaredInTable(table, 7, 7);
break;
}
default:
throw new IllegalArgumentException("Unexpected value: " + smallSquared);
}
}
}
}
}
/**
* 排除法检查每行是否有确定值,如果有则改动并返回true
* @param squared 数独
* @param table 排除法工具表,对应数独
* @param num 当前检测的数字
* @return 是否改动数独
*/
private boolean excludeRow(Integer[][] squared, boolean[][] table, int num) {
boolean changed = false;
for(int index_i = 1 ; index_i <=7; index_i+=3) {
for(int index_j = 1 ; index_j <=7; index_j+=3) {
int count = 0;
for (int i = index_i-1; i <= index_i+1; i++) {
for (int j = index_j-1; j <= index_j+1; j++) {
if(table[i][j] == true) {
count++;
}
}
}
if(count==8) {
for (int i = index_i-1; i <= index_i+1; i++) {
for (int j = index_j-1; j <= index_j+1; j++) {
if(table[i][j] == false) {
changed = true;
squared[i][j] = num;
}
}
}
}
}
}
return changed;
}
/**
* 排除法检查每列是否有确定值,如果有则改动并返回true
* @param squared 数独
* @param table 排除法工具表,对应数独
* @param num 当前检测的数字
* @return 是否改动数独
*/
private boolean excludeColumn(Integer[][] squared, boolean[][] table, int num) {
boolean changed = false;
for (int i = 0; i < squared.length; i++) {
int count = 0;
for (int j = 0; j < squared[0].length; j++) {
if(table[i][j] == true) {
count++;
}
}
if(count==8) {
for (int j = 0; j < squared[0].length; j++) {
if(table[i][j] == false) {
changed = true;
squared[i][j] = num;
}
}
}
}
return changed;
}
/**
* 排除法检查对应小九格是否有确定值,如果有则改动并返回true
* @param squared 数独
* @param table 排除法工具表,对应数独
* @param num 当前检测的数字
* @return 是否改动数独
*/
private boolean excludeCell(Integer[][] squared, boolean[][] table, int num) {
boolean changed = false;
for (int i = 0; i < squared.length; i++) {
int count = 0;
for (int j = 0; j < squared[0].length; j++) {
if(table[j][i] == true) {
count++;
}
}
for (int j = 0; j < squared[0].length; j++) {
if(count==8) {
if(table[j][i] == false) {
changed = true;
squared[j][i] = num;
}
}
}
}
return changed;
}
/**
* 将table对应小九格中的值设为true
* @param table 排除法工具表,对应数独
* @param index_i 对应小九格中心位置的x坐标
* @param index_j 对应小九格中心位置的y坐标
*/
private void setSmallSquaredInTable(boolean[][] table, int index_i, int index_j) {
for (int i = index_i-1; i <= index_i+1; i++) {
for (int j = index_j-1; j <= index_j+1; j++) {
if(table[i][j] == false) {
table[i][j] = true;
}
}
}
}
/**
* 获得两个数组交集
* @param arr1
* @param arr2
* @return 两个数组交集
*/
public Integer[] getB(Integer[] arr1, Integer[] arr2) {
Set<Integer> set1 = new HashSet<Integer>(Arrays.asList(arr1));
Set<Integer> set2 = new HashSet<Integer>(Arrays.asList(arr2));
set1.addAll(set2);
Integer[] result = {};
return set1.toArray(result);
}
/**
* 在FillInTheDeterminedValue无法确定值之后,通过尝试所有可能性获得解
* @param squared 数独
*/
private void startCalculate(Integer[][] squared) {
for (int i = 0; i < squared.length; i++) {
for (int j = 0; j < squared[0].length; j++) {
if(i == squared.length-1 && j == squared.length-1 && squared[i][j] != 0) {
answer.add(squared);
}
if (squared[i][j] != 0) {
continue;
} else {
for (int number : numbers) {
if(getUsedInIndex_x(squared, i).contains(number)
|| getUsedInIndex_y(squared, j).contains(number)
|| getUsedInSquared(squared, i, j).contains(number)) {
if(number==9) {
return;
}
continue;
} else {
Integer[][] copy = new Integer[9][9];
for (int iLength = 0 ; iLength < 9; iLength++) {
for (int jLength = 0 ; jLength < 9; jLength++) {
copy[iLength][jLength] = squared[iLength][jLength];
}
}
squared[i][j] = number;
FillInTheDeterminedValue(squared);
startCalculate(squared);
squared = copy;
if(number==9) {
return;
}
}
}
}
}
}
}
/**
* 获得当前小九格中所有存在的数字
* @param squared 数独
* @param index_x 当前位置的x坐标
* @param index_y 当前位置的y坐标
* @return 当前小九格中所有存在的数字Set集合
*/
private Set<Integer> getUsedInSquared(Integer[][] squared, int index_x, int index_y) {
int smallSquared = getSmallSquared(index_x, index_y);
switch (smallSquared) {
case 1: {
return getUsedInSquaredVisCore(squared, 1, 1);
}
case 2: {
return getUsedInSquaredVisCore(squared, 1, 4);
}
case 3: {
return getUsedInSquaredVisCore(squared, 1, 7);
}
case 4: {
return getUsedInSquaredVisCore(squared, 4, 1);
}
case 5: {
return getUsedInSquaredVisCore(squared, 4, 4);
}
case 6: {
return getUsedInSquaredVisCore(squared, 4, 7);
}
case 7: {
return getUsedInSquaredVisCore(squared, 7, 1);
}
case 8: {
return getUsedInSquaredVisCore(squared, 7, 4);
}
case 9: {
return getUsedInSquaredVisCore(squared, 7, 7);
}
default:
throw new IllegalArgumentException("Unexpected value: " + smallSquared);
}
}
/**
* 获得当前行的所有数字
* @param squared 数独
* @param index_x 当前位置的x坐标
* @return 当前行的所有数字Set集合
*/
private Set<Integer> getUsedInIndex_x(Integer[][] squared, int index_x) {
Set<Integer> usedInIndex_x = new HashSet<Integer>();
for (int i = 0; i < squared[index_x].length; i++) {
if (squared[index_x][i] != 0) {
usedInIndex_x.add(squared[index_x][i]);
}
}
return usedInIndex_x;
}
/** 当前列的所有数字
* 获得当前列的所有数字
* @param squared 数独
* @param index_y 当前位置的y坐标
* @return 当前列的所有数字Set集合
*/
private Set<Integer> getUsedInIndex_y(Integer[][] squared, int index_y) {
Set<Integer> usedInIndex_y = new HashSet<Integer>();
for (int i = 0; i < squared.length; i++) {
if (squared[i][index_y] != 0) {
usedInIndex_y.add(squared[i][index_y]);
}
}
return usedInIndex_y;
}
/**
* 获得当前位置的小九格,按1-9排序
* @param index_x 当前位置的x坐标
* @param index_y 当前位置的y坐标
* @return 当前小九格对应的整数
*/
private int getSmallSquared(int index_x, int index_y) {
Integer[] xSquared = getSmallSquaredViaIndex_X(index_x);
Integer[] ySquared = getSmallSquaredViaIndex_Y(index_y);
for (int i : xSquared) {
for (int j : ySquared) {
if(i == j) {
return i;
}
}
}
throw new IllegalArgumentException("Unexpected value: " + index_x + " and " + index_y);
}
/**
* 获得当前位置对应的一列小九格,小九格按1-9排序
* @param index 当前位置的y坐标
* @return 返回三个整数数组
*/
private Integer[] getSmallSquaredViaIndex_Y(int index) {
switch (index) {
case 0:
case 1:
case 2: {
return new Integer[] {1, 4, 7};
}
case 3:
case 4:
case 5: {
return new Integer[] {2, 5, 8};
}
case 6:
case 7:
case 8: {
return new Integer[] {3, 6, 9};
}
default:
throw new IllegalArgumentException("Unexpected value: " + index);
}
}
/**
* 获得当前位置对应的一行小九格,小九格按1-9排序
* @param index 当前位置的x坐标
* @return 返回三个整数数组
*/
private Integer[] getSmallSquaredViaIndex_X(int index) {
switch (index) {
case 0:
case 1:
case 2: {
return new Integer[] {1, 2, 3};
}
case 3:
case 4:
case 5: {
return new Integer[] {4, 5, 6};
}
case 6:
case 7:
case 8: {
return new Integer[] {7, 8, 9};
}
default:
throw new IllegalArgumentException("Unexpected value: " + index);
}
}
/**
* 获得当前小九格中所有存在的数字
* @param squared 数独
* @param index_x 小九格中心位置x坐标
* @param index_y 小九格中心位置y坐标
* @return
*/
private Set<Integer> getUsedInSquaredVisCore(Integer[][] squared, int index_x, int index_y) {
Set<Integer> usedInSquared = new HashSet<Integer>();
for (int i = index_x-1; i <= index_x+1; i++) {
for (int j = index_y-1; j <= index_y+1; j++) {
if(squared[i][j] != 0) {
usedInSquared.add(squared[i][j]);
}
}
}
return usedInSquared;
}
/**
* 答应当前数独
* @param squared 数独
*/
private void print() {
System.out.println("*********************************************");
for (Integer[][] squared : answer) {
System.out.println("------answer------");
for (int x = 0 ; x < squared.length; x ++) {
for (int y = 0 ; y < squared[0].length; y++) {
System.out.print(squared[x][y] + " ");;
}
System.out.println();
}
}
}
public static void main(String[] args) {
// Integer[][] in = new Integer[][] {
// {5, 4, 0, 1, 6, 0, 7, 0, 0},
// {6, 0, 0, 0, 0, 0, 3, 4, 5},
// {0, 9, 0, 0, 4, 3, 1, 0, 0},
// {7, 0, 0, 0, 0, 0, 0, 9, 0},
// {0, 0, 1, 0, 7, 0, 5, 0, 0},
// {0, 2, 0, 0, 0, 0, 0, 0, 6},
// {0, 0, 2, 3, 9, 0, 0, 5, 0},
// {4, 7, 9, 0, 0, 0, 0, 0, 2},
// {0, 0, 6, 0, 8, 2, 0, 1, 7}
// };
// Integer[][] in = new Integer[][] {
// {6, 0, 0, 5, 0, 0, 0, 0, 0},
// {0, 3, 4, 0, 0, 1, 0, 8, 0},
// {0, 0, 0, 0, 0, 4, 0, 6, 3},
// {0, 1, 8, 0, 9, 0, 0, 0, 0},
// {0, 7, 0, 0, 0, 0, 0, 0, 4},
// {0, 0, 0, 7, 4, 0, 0, 5, 0},
// {0, 0, 1, 0, 0, 3, 0, 0, 6},
// {0, 0, 0, 1, 5, 0, 3, 0, 0},
// {3, 0, 0, 8, 0, 0, 5, 9, 0}
// };
// Integer[][] in = new Integer[][] {
// {0, 2, 0, 4, 0, 0, 0, 0, 6},
// {0, 0, 0, 0, 1, 6, 0, 0, 9},
// {1, 0, 6, 2, 5, 0, 0, 0, 3},
// {0, 0, 0, 0, 0, 1, 9, 6, 0},
// {9, 0, 0, 0, 2, 0, 0, 0, 0},
// {0, 7, 0, 3, 0, 0, 0, 0, 8},
// {0, 3, 7, 5, 0, 0, 0, 0, 0},
// {0, 0, 0, 0, 6, 0, 8, 0, 0},
// {0, 0, 0, 0, 0, 4, 0, 5, 7}
// };
// Integer[][] in = new Integer[][] {
// {0, 2, 0, 4, 0, 1, 0, 0, 0},
// {0, 7, 0, 0, 0, 9, 1, 4, 0},
// {0, 1, 8, 0, 0, 0, 0, 0, 0},
// {0, 0, 0, 6, 0, 0, 0, 0, 7},
// {0, 0, 9, 0, 5, 0, 0, 0, 0},
// {0, 0, 5, 0, 0, 0, 0, 2, 6},
// {1, 0, 0, 0, 0, 0, 5, 7, 0},
// {0, 0, 0, 0, 4, 0, 0, 6, 0},
// {0, 0, 0, 5, 0, 3, 8, 0, 0}
// };
// Integer[][] in = new Integer[][] {
// {0, 1, 0, 0, 0, 0, 3, 0, 0},
// {0, 0, 9, 0, 0, 6, 0, 0, 0},
// {5, 0, 0, 0, 4, 0, 8, 0, 0},
// {9, 3, 5, 0, 0, 0, 0, 0, 2},
// {0, 0, 0, 0, 0, 5, 0, 4, 0},
// {0, 0, 0, 2, 6, 0, 7, 3, 0},
// {7, 0, 0, 0, 0, 0, 9, 0, 6},
// {0, 0, 0, 0, 0, 2, 0, 0, 0},
// {0, 0, 3, 0, 1, 0, 2, 0, 0}
// };
Integer[][] in = new Integer[][] {
{8, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 3, 6, 0, 0, 0, 0, 0},
{0, 7, 0, 0, 9, 0, 2, 0, 0},
{0, 5, 0, 0, 0, 7, 0, 0, 0},
{0, 0, 0, 0, 4, 5, 7, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 3, 0},
{0, 0, 1, 0, 0, 0, 0, 6, 8},
{0, 0, 8, 5, 0, 0, 0, 1, 0},
{0, 9, 0, 0, 0, 0, 4, 0, 0}
};
Sudoku sudoku = new Sudoku();
sudoku.calculate(in);
}
}