Java版本的DLX解決數獨算法

DLX又稱作精確覆蓋問題,而今天要說的就是把解數獨這個問題轉化爲一個精確覆蓋問題,然後來解決它。
其實這是一個很好的解決問題的思路,很多問題都可以轉化爲一個精確覆蓋問題,然後用接下來這個算法來解決它。
關於DLX的思想,網上有不少的解釋,這裏我就不再贅述,我會在我的另一篇博客裏講解原理。

首先是整個代碼的結構。
總共分爲四個類,分別是:

  1. 主類(StartPoint)
  2. 轉換器類(Transfer)
  3. 節點類(Node)
  4. 求解器類(Solver)

這就是我們的代碼的主要結構,下面我們將展示代碼的細節。

主類(StartPoint)

這是主類,也是整個程序的入口,負責從文件讀取數據,調用各種其他類,然後輸出結果到文件中,具體如下:

package dLX;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Vector;

public class StartPoint {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		File input=new File("D:\\problem.txt");
		File output  = new File("D:\\solution.txt");
		try {
			output.createNewFile();
			FileReader in=new FileReader(input);
			FileWriter out=new FileWriter(output);
			char problem[]=new char[(int)input.length()];
			in.read(problem);
			if(problem.length<81){
				out.write("輸入不合法");
				in.close();
				out.close();
			    return;
			}
			Transfer tsf= new Transfer();
			Vector<Vector<Integer>> matrix = tsf.sudoku2matrix(problem);// 矩陣轉換 
			Solver sudoku=new Solver(matrix,matrix.size(),324);//初始化解題類 
			
			if (!sudoku.Search(0)){//調用解題函數 
				out.write("無解");
				in.close();
				out.close();
			    return;
			}
			Vector<Integer> solution = tsf.matrix2sudoku(matrix, sudoku.getResult());
			for (int ix = 0; ix < 81; ++ix){ 
				out.write(solution.get(ix)+""); 
				out.write((ix+1)%9!=0?"" : "\n");
			 }
			 in.close();
			 out.close();
			    
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	

}

節點類(Node)

因爲DLX很關鍵的一個數據結構是鏈表,所以就需要節點,這裏我們就寫了一個節點類:

package dLX;

public class Node {
	Node up, down, left, right, colRoot, rowRoot;
	int Num;//行對象特有,記錄行數
    int Size;//列對象特有,記錄該列元素數
    
    public Node(){
    	Size=0;
    	Num=-1;
    }
    
    public Node(int i){
    	Size=0;
    	Num=i;
    }
}

轉換器類

因爲我們人看到的數獨矩陣並不是我們解題需要的01矩陣,所有我們需要一個轉換的類,這個類負責把輸入的數獨矩陣轉換成01矩陣便於解題,當解題結束時,再負責把結果轉換成便於人類閱讀的數獨矩陣,這就是這個類的作用:

package dLX;

import java.util.Vector;

public class Transfer {
	Vector<Vector<Integer>> sudoku2matrix(char[] problem){
		Vector<Vector<Integer>> matrix=new Vector<Vector<Integer>>();
	    for (int ix = 0; ix < 81; ++ix){ 
	        int val = problem[ix] - '0';
	        Vector<Integer> current_row=new Vector<Integer>();
	        for(int i=0;i<324;i++){
	        	current_row.add(new Integer(0));
	        }
	        if (val != 0){
	            current_row.set(ix, new Integer(1));
	            current_row.set(81 + ix/9*9 + val -1, new Integer(1));
	            current_row.set(162 + ix%9*9 +val -1, new Integer(1));
	            current_row.set(243 + (ix/9/3*3+ix%9/3)*9 +val -1, new Integer(1));
	            matrix.add(current_row);
	            continue;
	        }
	        for (int jx = 0; jx < 9; ++jx){
	        	Vector<Integer> current_row2=new Vector<Integer>();
	        	for(int i=0;i<324;i++){
		        	current_row2.add(new Integer(0));
		        }
	        	current_row2.set(ix, new Integer(1));
	            current_row2.set(81 + ix/9*9 + jx, new Integer(1));
	            current_row2.set(162 + ix%9*9 +jx , new Integer(1));
	            current_row2.set(243 + (ix/9/3*3+ix%9/3)*9 +jx , new Integer(1));
	            matrix.add(current_row2);
	        }
	    }
	    return matrix;
	}
	
	
	Vector<Integer> matrix2sudoku(Vector<Vector<Integer>> matrix, Vector<Integer> result){
		Vector<Integer> solution=new Vector<Integer>();
		for(int i=0;i<81;i++){
			solution.add(new Integer(0));
		}
	    for (int ix = 0; ix < 81; ++ix){
	    	Vector<Integer> current = matrix.get(result.get(ix)-1);
	        int pos = 0, val = 0;
	        for (int jx = 0; jx < 81; ++jx){
	            if (current.get(jx) == 1){
					break;
				} 
	            ++pos;
	        }
	        for (int kx = 81; kx < 162; ++kx){
	            if (current.get(kx) == 1)
	                break;
	            ++val;
	        }
	        solution.set(pos, val%9 + 1);
	    }
	    return solution;
	}
}

求解器類

這個類就是解決問題的核心了,它負責核心的解決問題,接收轉換類轉換得到的01矩陣來解決問題,提供結果,具體代碼如下:

package dLX;

import java.util.Vector;

public class Solver {
	Node Head;
	Vector<Integer> result;
	int _row, _col, _updates;
	public static final int INT_MAX=2147483647;
	
	public Solver(Vector<Vector<Integer>> matrix, int m, int n){
		result=new Vector<Integer>();
		Head = new Node();
		Head.up = Head;
		Head.down = Head;
		Head.right = Head;
		Head.left = Head;
		_row=m;
		_col=n;
		_updates=0;
    	init();
    	link(matrix);
	}

	void init(){
	    Node newNode;
		//表頭位置向後插入,構造列對象
	    for (int ix = 0; ix < _col; ++ix){
	        newNode = new Node();
	        newNode.up = newNode;
	        newNode.down = newNode;
	        newNode.right = Head.right;
	        newNode.left = Head;
	        newNode.right.left = newNode;
	        Head.right = newNode;
	    }
	    //表頭位置向下插入,構造行對象
	    for (int ix = 0; ix < _row; ++ix){
	        newNode = new Node(_row-ix);//注意序號是_row-ix
	        newNode.down = Head.down;
	        newNode.up = Head;
	        newNode.down.up = newNode;
	        Head.down = newNode;
	    }
	}
	
	void link(Vector<Vector<Integer>> matrix){
	    Node  current_row,  current_col,  newNode,  current;//當前行對象,當前列對象,新節點,當前節點
	    current_row = Head;
	    for (int row = 0; row < _row; ++row){
	        current_row = current_row.down;
	        current_col = Head;
	        for (int col = 0; col < _col; ++col){
	            current_col = current_col.right;
	            if (matrix.get(row).get(col) == 0)//矩陣上爲0的位置不設置節點
	                continue;
	            newNode = new Node();
	            newNode.colRoot = current_col;
	            newNode.rowRoot = current_row;//設置當前節點對應的行列對象
	 
	            newNode.down = current_col;
	            newNode.up = current_col.up;
	            newNode.up.down = newNode;
	            current_col.up = newNode;//鏈接當前節點到列雙向鏈尾端
	 
	            if (current_row.Size == 0){//行雙向鏈不應該把行對象包含進來
	                current_row.right = newNode;
	                newNode.left = newNode;
	                newNode.right = newNode;
	                current_row.Size++;
	            }
	            current = current_row.right;//設置當前節點(即行對象右的節點)
	            newNode.left = current.left;
	            newNode.right = current;
	            newNode.left.right = newNode;
	            current.left = newNode;//鏈接當前節點到行雙向鏈尾端
	 
	            current_col.Size++;
	        }
	    }
	}
	
	void cover(Node  cRoot){//覆蓋列
	    ++_updates;
	    cRoot.left.right = cRoot.right;
	    cRoot.right.left = cRoot.left;//刪除該列對象
	    Node  i,  j;
	    i = cRoot.down;
	    while (i != cRoot){
	        j = i.right; 
	        while (j != i){
	            j.down.up = j.up;
	            j.up.down = j.down;
	            j.colRoot.Size--;
	            j = j.right;
	        }
	        i = i.down;
	    }
	}
	
	void recover(Node  cRoot){
	    Node  i,  j;
	    i = cRoot.up;
	    while (i != cRoot){
	        j = i.left;
	        while (j != i){
	            j.colRoot.Size++;
	            j.down.up = j;
	            j.up.down = j;
	            j = j.left;
	        }
	        i = i.up;
	    }
	    cRoot.right.left = cRoot;
	    cRoot.left.right = cRoot;
	}
	
	boolean Search(int k){
		
	    if (Head.right == Head)//列空,則成功找到一組行的集合
	        return true;
	    
	    Node  cRoot,  c;
	    cRoot=new Node();
	    int minSize = INT_MAX;
	    for(c = Head.right; c != Head; c = c.right){//選擇情況最少的列進行嘗試 
	        if (c.Size < minSize){
	            minSize = c.Size;
	            cRoot = c;
	            if (minSize == 1){
	            	break;
	            }
	                
	            if (minSize == 0){//有一列爲空,失敗
	            	return false;
	            }
	        }
	    }
	    cover(cRoot);//覆蓋這一列 
	 
	    Node  current_row, current;
	    for (current_row = cRoot.down; current_row != cRoot; current_row = current_row.down){
	        result.add(current_row.rowRoot.Num);//將該行加入result中
	        for (current = current_row.right; current != current_row; current = current.right){
	            cover(current.colRoot);//delete other c
	        }
	        if (Search(k+1)){
	        	return true;
	        }
	        for (current = current_row.left; current != current_row; current = current.left)
	            recover(current.colRoot);//還原 
	        	result.remove(result.size()-1);//發現該行不符合要求,還原result
	    }
	    recover(cRoot);
	    return false;
	}
	
	
	Vector<Integer> getResult(){ 
		return result;
	}
	
	int getUpdates(){ 
		return _updates;
	}
	
	
	
	
	
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章