Java實(shí)現(xiàn)解數(shù)獨(dú)的小程序
前言
數(shù)獨(dú)相信很多人都玩過(guò),趣味性很強(qiáng),十分的耐玩??捎袥](méi)有程序員想過(guò)玩實(shí)現(xiàn)一個(gè)數(shù)獨(dú)布局的算法呢?算法是個(gè)很有意思,很神奇的東西。
算法如下,需要預(yù)先給出幾個(gè)固定的值,目前解決的一個(gè)最難的數(shù)獨(dú)是大概26個(gè)已知值的情況,理論上應(yīng)該能解決任意已知值的數(shù)獨(dú),不過(guò)不知道會(huì)不會(huì)迭代棧溢出……因?yàn)樵?6個(gè)已知值的情況下就迭代了3000多次了,囧~~~
結(jié)果顯示如下:

這是已知值:
1 1 2 1 4 8 1 5 5 1 6 1 1 7 7 1 8 3 2 1 1 2 2 6 2 4 4 3 5 9 3 7 8 3 8 4 4 7 9 5 8 7 6 1 3 6 6 8 6 7 4 6 8 6 7 3 7 7 6 4 7 7 1 8 3 3 8 6 7 9 3 4 9 4 6 9 7 3 9 8 2
(PS:如9 8 2表示第9行第二列的值是2)
將上面的數(shù)字保存到num.txt文件中,再把底下附的源代碼保存為Sudoku.java。
然后在cmd命令行模型下輸入:
javac Sudoku.java java Sudoku <num.txt
即可得到結(jié)果。
這個(gè)解法是我之前看到八皇后排列問(wèn)題的解法后結(jié)合應(yīng)用的,在數(shù)獨(dú)中采用了這種解法,不過(guò)應(yīng)該算是比較暴力的拆解,所以我后面命名成violentBreak。。。
雖然只是一個(gè)很小的事,但是能嘗試著用編程去解決日常遇到的事,突然覺(jué)得很開(kāi)心,學(xué)以致用!
java源代碼:
import java.lang.System.*;
import java.util.ArrayList;
import java.util.Scanner;
/**This class named Sudoku can auto calculate Sudoku but
*you should input some nums which are already known.
*For instance:
*1 5 3
*2 4 7
*There two rows means [1][5]=3 [2][4]=7
*i.e. In row 1 col 5 is value:5
*you can write all known num into one txt file
*and input into this program .
*such as java Sudoku < num.txt
*/
/**代碼邏輯解析:
* 1、建立一個(gè)9X9矩陣保存數(shù)獨(dú)正確的值
2、建立一個(gè)9X9列表,每個(gè)列表里保存著該位置,數(shù)獨(dú)可以填入的可能值
如ArrayList[1][1]={1,2,3}即1,1這個(gè)位置的數(shù)獨(dú)可能可以填入其中一個(gè)
當(dāng)矩陣該位置已保存了正確值時(shí),清空對(duì)應(yīng)位置的ArrayList
3、當(dāng)列表ArrayList里只有一個(gè)可能值時(shí),那個(gè)值就是數(shù)獨(dú)的正確值,將該值填入數(shù)獨(dú)矩陣
并更新ArrayList
PS:以上算法只能用于求困難級(jí)別的數(shù)獨(dú),因?yàn)槲以谕嬗螒虻臅r(shí)候發(fā)現(xiàn),每個(gè)時(shí)刻必定會(huì)有
一個(gè)數(shù)獨(dú)空位,是只能填入一個(gè)值的,所以這個(gè)算法才能運(yùn)行
當(dāng)一個(gè)數(shù)獨(dú)(即已知位置的值變少時(shí)),可能會(huì)出現(xiàn)所有的空位都最起碼有兩個(gè)值時(shí),
需要改進(jìn)算法,通過(guò)代入來(lái)判斷這個(gè)數(shù)獨(dú)是否成立
4、于是后期我加入了暴力破解法,在上面的步驟執(zhí)行后無(wú)法得出數(shù)獨(dú),即使用暴力破解
*
*/
public class Sudoku{
//弄十的原因是為了好記憶,0,0不用,只用1-9的list
private ArrayList<Integer>[][] availableNum=new ArrayList[10][10];
private int[][] correctNum=new int[10][10];
private Scanner scan=new Scanner(System.in);
private int countNum=0;
{
for(int i=0;i<10;i++){
for(int j=0;j<10;j++){
availableNum[i][j]=new ArrayList<>();
}
}
for(int row=1;row<10;row++){
for(int col=1;col<10;col++){
for(int i=1;i<10;i++)
availableNum[row][col].add(new Integer(i));
}
}
//先都初始化為-1,即此時(shí)沒(méi)有填充數(shù)字
for(int i=0;i<10;i++)
for(int j=0;j<10;j++)
correctNum[i][j]=-1;
}
public Sudoku(){}
//在對(duì)應(yīng)數(shù)獨(dú)位置插入正確值
public void insert(int row,int col,int value){
correctNum[row][col]=value;
availableNum[row][col]=null;
delete(row,col,value);
}
//每插入一個(gè)數(shù)值,就刪除相應(yīng)的行列和小框框3X3數(shù)獨(dú)里對(duì)應(yīng)的ArrayList里可能的該值
public void delete(int row,int col,int value){
//delte row
for(int i=1;i<10;i++){
if (availableNum[row][i]!=null)
availableNum[row][i].remove(new Integer(value));
}
//delete column
for(int i=1;i<10;i++){
if (availableNum[i][col]!=null)
availableNum[i][col].remove(new Integer(value));
}
//delete box num
int[] itsCenter=judgeCenterPos(row,col);
for(int temp1=itsCenter[0]-1;temp1<=itsCenter[0]+1;temp1++)
for(int temp2=itsCenter[1]-1;temp2<=itsCenter[1]+1;temp2++)
if(availableNum[temp1][temp2]!=null){
availableNum[temp1][temp2].remove(new Integer(value));
}
}
//判斷插入的值時(shí)處于哪個(gè)小框框數(shù)獨(dú)里
public int[] judgeCenterPos(int row,int col){
int[] itsCenter=new int[2];
for(int centerRow=2;centerRow<9;centerRow+=3)
for(int centerCol=2;centerCol<9;centerCol+=3){
if( Math.abs(row-centerRow)<=1 &&
Math.abs(col-centerCol)<=1 ){
itsCenter[0]=centerRow;
itsCenter[1]=centerCol;
return itsCenter;
}
}
System.out.println("Some unchecked error was happened");
return itsCenter;
}
//判斷空格里所能填的數(shù)字是不是只能有一個(gè),當(dāng)返回-1時(shí)通過(guò)檢測(cè)報(bào)錯(cuò)
public int[] judgeIfOnlyOne(){
for(int row=1;row<10;row++)
for(int col=1;col<10;col++){
if(availableNum[row][col]!=null)
if(availableNum[row][col].size()==1)
return new int[]{row,col};
}
return new int[]{-1,-1};
}
// 判斷為唯一,但是空格里還有多于1個(gè)的數(shù)時(shí),我們直接將哪個(gè)正確的值填入
public void insertIfCan(){
for(int row=1;row<=7;row+=3){
for(int col=1;col<=7;col+=3){
for(int z=1;z<10;z++){
int count=0;
Integer temp=new Integer(z);
int itemp=0,jtemp=0;
outer:
for(int i=row;i<row+3;i++){
for(int j=col;j<col+3;j++){
if(availableNum[i][j]!=null){
if(availableNum[i][j].contains(temp)){
count++;
itemp=i;
jtemp=j;
if (count>1)
break outer;
}
}
}
}
if(count==1 && itemp!=0){
insert(itemp,jtemp,z);
}
}
}
}
}
//判斷數(shù)獨(dú)的矩陣是否填滿,沒(méi)有則繼續(xù)
public boolean judgeMatrixFull(){
for(int i=1;i<10;i++)
for(int j=1;j<10;j++)
if(correctNum[i][j]==-1)
return false;
return true;
}
//先輸入已知位置的數(shù)字
public void inputNumKnown(){
while(scan.hasNextInt()){
int row=scan.nextInt();
int col=scan.nextInt();
int value=scan.nextInt();
insert(row,col,value);
delete(row,col,value);
}
}
//打印數(shù)獨(dú)結(jié)果
public void printSudoku(){
printSudoku(correctNum);
}
public void printSudoku(int[][] arr){
System.out.println("Sudoku result:");
for(int i=1;i<10;i++){
for(int j=1;j<10;j++)
System.out.print(arr[i][j]+" ");
System.out.println(" ");
}
}
public void printList(){
for(int i=1;i<10;i++)
for(int j=1;j<10;j++){
System.out.print(i+" "+j+":");
if(availableNum[i][j]!=null)
for(int z=0;z<availableNum[i][j].size();z++){
System.out.print(availableNum[i][j].get(z)+" ");
}
System.out.println(" ");
}
}
//暴力破解
public void violentBreak(){
int i=1,j=1;
outer:
for(;i<10;i++)
for(;j<10;j++)
if(correctNum[i][j]!=-1)
break outer;
violentInsert(i,j,correctNum[i][j],correctNum);
}
public void violentInsert(int row,int col,int value,int[][] arr){
countNum++;
int[][] tempMatrix=new int[10][10];
for(int i=1;i<10;i++)
for(int j=1;j<10;j++)
tempMatrix[i][j]=arr[i][j];
tempMatrix[row][col]=value;
//不能insert的話說(shuō)明填滿了
int[] insertPos=canInsert(tempMatrix);
if(insertPos[0]==-1){
System.out.println("all insert is done.This is the last Sudoku:");
printSudoku(tempMatrix);
return;
}
for(int val=1;val<=10;val++){
if(val==10){
tempMatrix=null; //讓JVM回收垃圾
//System.out.println("value=10 happened.");
return;
}
if(judgeIfViolentInsert(insertPos[0],insertPos[1],val,tempMatrix)){
//System.out.println("insert happened.");
violentInsert(insertPos[0],insertPos[1],val,tempMatrix);
}
}
}
public int[] canInsert(int[][] tempMatrix){
int[] pos={-1,-1};
for(int i=1;i<10;i++)
for(int j=1;j<10;j++){
if(tempMatrix[i][j]==-1){
pos[0]=i;
pos[1]=j;
return pos;
}
}
return pos;
}
public boolean judgeIfViolentInsert(int row,int col,int value,int[][] tempMatrix){
for(int j=1;j<10;j++)
if(value==tempMatrix[row][j])
return false;
for(int i=1;i<10;i++)
if(value==tempMatrix[i][col])
return false;
int[] itsCenter=judgeCenterPos(row,col);
for(int temp1=itsCenter[0]-1;temp1<=itsCenter[0]+1;temp1++)
for(int temp2=itsCenter[1]-1;temp2<=itsCenter[1]+1;temp2++)
if(value==tempMatrix[temp1][temp2])
return false;
return true;
}
//數(shù)獨(dú)開(kāi)始運(yùn)算的函數(shù)
public void start(){
int[] nextInsert=new int[2];
int count=0;
this.inputNumKnown();
while(!judgeMatrixFull()){
nextInsert=judgeIfOnlyOne();
if(nextInsert[0]==-1){
this.insertIfCan();
count++;
if(count==15){
System.out.println("Cannot fullfill this sodoku through finding the only one left.");
System.out.println("count:"+count);
break;
}
continue;
}
int value=availableNum[nextInsert[0]][nextInsert[1]].get(0);
insert(nextInsert[0],nextInsert[1],value);
}
printSudoku();
//滿了就不用暴力破解了
if(judgeMatrixFull())
return;
System.out.println("Now we should break this Sudoku by violent method.");
violentBreak();
System.out.println("Recursion times:"+countNum);
}
public static void main(String[] args){
Sudoku test1=new Sudoku();
test1.start();
int[] a=new int[2];
System.out.println(a);
System.out.println(a[0]);
}
}
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流。
- java 實(shí)現(xiàn)迷宮回溯算法示例詳解
- java回溯算法解數(shù)獨(dú)問(wèn)題
- Java實(shí)現(xiàn)走迷宮回溯算法
- Java基于循環(huán)遞歸回溯實(shí)現(xiàn)八皇后問(wèn)題算法示例
- Java實(shí)現(xiàn)數(shù)獨(dú)小游戲
- python實(shí)現(xiàn)數(shù)獨(dú)游戲 java簡(jiǎn)單實(shí)現(xiàn)數(shù)獨(dú)游戲
- Java基于二維數(shù)組實(shí)現(xiàn)的數(shù)獨(dú)問(wèn)題示例
- java版數(shù)獨(dú)游戲界面實(shí)現(xiàn)(二)
- 簡(jiǎn)單實(shí)現(xiàn)java數(shù)獨(dú)游戲
- 教你怎么用Java回溯算法解數(shù)獨(dú)
相關(guān)文章
Java?Controller實(shí)現(xiàn)參數(shù)驗(yàn)證與統(tǒng)一異常處理流程詳細(xì)講解
Controller是Spring接受并處理網(wǎng)頁(yè)請(qǐng)求的組件,是整個(gè)應(yīng)用的入口,因此學(xué)會(huì)Controller的常用注解對(duì)理解一個(gè)應(yīng)用是重中之重。SpringBoot的Controller中經(jīng)常會(huì)用到注解@Controller、@RestController、@RequestMapping、@RequestBody等2023-01-01
Idea跑的項(xiàng)目沒(méi)問(wèn)題將程序install成jar包運(yùn)行報(bào)錯(cuò)空指針的問(wèn)題
這篇文章主要介紹了Idea跑的項(xiàng)目沒(méi)問(wèn)題,將程序install成jar包運(yùn)行報(bào)錯(cuò)空指針的問(wèn)題,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
MyBatis 參數(shù)映射機(jī)制實(shí)踐記錄
這篇文章主要介紹了MyBatis 參數(shù)映射機(jī)制實(shí)踐記錄,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-12-12
Spring動(dòng)態(tài)管理定時(shí)任務(wù)之ThreadPoolTaskScheduler解讀
這篇文章主要介紹了Spring動(dòng)態(tài)管理定時(shí)任務(wù)之ThreadPoolTaskScheduler解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
常用數(shù)據(jù)庫(kù)的驅(qū)動(dòng)程序及JDBC URL分享
這篇文章主要介紹了常用數(shù)據(jù)庫(kù)的驅(qū)動(dòng)程序及 JDBC URL,需要的朋友可以看下2014-01-01
System.getProperty(user.dir)定位問(wèn)題解析
System.getProperty(user.dir) 獲取的是啟動(dòng)項(xiàng)目的容器位置,用IDEA是項(xiàng)目的根目錄,部署在tomcat上是tomcat的啟動(dòng)路徑,即tomcat/bin的位置,這篇文章主要介紹了System.getProperty(user.dir)定位問(wèn)題,需要的朋友可以參考下2023-05-05
SSH框架網(wǎng)上商城項(xiàng)目第28戰(zhàn)之使用Ajax技術(shù)局部更新商品數(shù)量和總價(jià)
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第28戰(zhàn)之使用Ajax技術(shù)局部更新商品數(shù)量和總價(jià),感興趣的小伙伴們可以參考一下2016-06-06

