Java版數獨演算法實現
阿新 • • 發佈:2019-01-02
數獨的歷史:
數獨前身為“九宮格”,最早起源於中國。數千年前,我們的祖先就發明了洛書,其特點較之現在的數獨更為複雜,要求縱向、橫向、斜向上的三個數字之和等於15,而非簡單的九個數字不能重複。儒家典籍《易經》中的“九宮圖”也源於此,故稱“洛書九宮圖”。而“九宮”之名也因《易經》在中華文化發展史上的重要地位而儲存、沿用至今。
1783年,瑞士數學家萊昂哈德·尤拉發明了一種當時稱作“拉丁方塊”(Latin Square)的遊戲,這個遊戲是一個n×n的數字方陣,每一行和每一列都是由不重複的n個數字或者字母組成的。
19世紀70年代,美國的一家數學邏輯遊戲雜誌《戴爾鉛筆字謎和詞語遊戲》(Dell Puzzle Mαgαzines)開始刊登現在稱為“數獨”的這種遊戲,當時人們稱之為“數字拼圖”(Number Place),在這個時候,9×9的81格數字遊戲才開始成型。
1984年4月,在日本遊戲雜誌《字謎通訊Nikoil》(《パズル通訊ニコリ》)上出現了“數獨”遊戲,提出了“獨立的數字”的概念,意思就是“這個數字只能出現一次”或者“這個數字必須是惟一的”,並將這個遊戲命名為“數獨”(sudoku)。
實現方法:
數獨前身為“九宮格”,最早起源於中國。數千年前,我們的祖先就發明了洛書,其特點較之現在的數獨更為複雜,要求縱向、橫向、斜向上的三個數字之和等於15,而非簡單的九個數字不能重複。儒家典籍《易經》中的“九宮圖”也源於此,故稱“洛書九宮圖”。而“九宮”之名也因《易經》在中華文化發展史上的重要地位而儲存、沿用至今。
1783年,瑞士數學家萊昂哈德·尤拉發明了一種當時稱作“拉丁方塊”(Latin Square)的遊戲,這個遊戲是一個n×n的數字方陣,每一行和每一列都是由不重複的n個數字或者字母組成的。
19世紀70年代,美國的一家數學邏輯遊戲雜誌《戴爾鉛筆字謎和詞語遊戲》(Dell Puzzle Mαgαzines)開始刊登現在稱為“數獨”的這種遊戲,當時人們稱之為“數字拼圖”(Number Place),在這個時候,9×9的81格數字遊戲才開始成型。
1984年4月,在日本遊戲雜誌《字謎通訊Nikoil》(《パズル通訊ニコリ》)上出現了“數獨”遊戲,提出了“獨立的數字”的概念,意思就是“這個數字只能出現一次”或者“這個數字必須是惟一的”,並將這個遊戲命名為“數獨”(sudoku)。
實現方法:
import java.util.Random; public class ShuDu { /** 儲存數字的陣列 */ private static int[][] n = new int[9][9]; /** 生成隨機數字的源陣列,隨機數字從該陣列中產生 */ private static int[] num = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; public static int[][] generateShuDu(){ // 生成數字 for (int i = 0; i < 9; i++) { // 嘗試填充的數字次數 int time = 0; // 填充數字 for (int j = 0; j < 9; j++) { // 產生數字 n[i][j] = generateNum(time); // 如果返回值為0,則代表卡住,退回處理 // 退回處理的原則是:如果不是第一列,則先倒退到前一列,否則倒退到前一行的最後一列 if (n[i][j] == 0) { // 不是第一列,則倒退一列 if (j > 0) { j -= 2; continue; } else {// 是第一列,則倒退到上一行的最後一列 i--; j = 8; continue; } } // 填充成功 if (isCorret(i, j)) { // 初始化time,為下一次填充做準備 time = 0; } else { // 繼續填充 // 次數增加1 time++; // 繼續填充當前格 j--; } } } return n; } /** * 是否滿足行、列和3X3區域不重複的要求 * * @param row * 行號 * @param col * 列號 * @return true代表符合要求 */ private static boolean isCorret(int row, int col) { return (checkRow(row) & checkLine(col) & checkNine(row, col)); } /** * 檢查行是否符合要求 * * @param row * 檢查的行號 * @return true代表符合要求 */ private static boolean checkRow(int row) { for (int j = 0; j < 8; j++) { if (n[row][j] == 0) { continue; } for (int k = j + 1; k < 9; k++) { if (n[row][j] == n[row][k]) { return false; } } } return true; } /** * 檢查列是否符合要求 * * @param col * 檢查的列號 * @return true代表符合要求 */ private static boolean checkLine(int col) { for (int j = 0; j < 8; j++) { if (n[j][col] == 0) { continue; } for (int k = j + 1; k < 9; k++) { if (n[j][col] == n[k][col]) { return false; } } } return true; } /** * 檢查3X3區域是否符合要求 * * @param row * 檢查的行號 * @param col * 檢查的列號 * @return true代表符合要求 */ private static boolean checkNine(int row, int col) { // 獲得左上角的座標 int j = row / 3 * 3; int k = col / 3 * 3; // 迴圈比較 for (int i = 0; i < 8; i++) { if (n[j + i / 3][k + i % 3] == 0) { continue; } for (int m = i + 1; m < 9; m++) { if (n[j + i / 3][k + i % 3] == n[j + m / 3][k + m % 3]) { return false; } } } return true; } /** * 產生1-9之間的隨機數字 規則:生成的隨機數字放置在陣列8-time下標的位置,隨著time的增加,已經嘗試過的數字將不會在取到 * 說明:即第一次次是從所有數字中隨機,第二次時從前八個數字中隨機,依次類推, 這樣既保證隨機,也不會再重複取已經不符合要求的數字,提高程式的效率 * 這個規則是本演算法的核心 * * @param time * 填充的次數,0代表第一次填充 * @return */ private static Random r=new Random(); private static int generateNum(int time) { // 第一次嘗試時,初始化隨機數字源陣列 if (time == 0) { for (int i = 0; i < 9; i++) { num[i] = i + 1; } } // 第10次填充,表明該位置已經卡住,則返回0,由主程式處理退回 if (time == 9) { return 0; } // 不是第一次填充 // 生成隨機數字,該數字是陣列的下標,取陣列num中該下標對應的數字為隨機數字 // int ranNum = (int) (Math.random() * (9 - time));//j2se int ranNum=r.nextInt(9 - time);//j2me // 把數字放置在陣列倒數第time個位置, int temp = num[8 - time]; num[8 - time] = num[ranNum]; num[ranNum] = temp; // 返回數字 return num[8 - time]; } public static void main(String[] args) { int[][] shuDu=generateShuDu(); // 輸出結果 for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { System.out.print(shuDu[i][j] + " "); } System.out.println(); } } }