数独生成器
本文介紹了一種用Java實現(xiàn)的數(shù)獨生成器。
數(shù)獨盤面是個九宮,每一宮又分為九個小格。在這八十一格中給出一定的已知數(shù)字和解題條件,利用邏輯和推理,在其他的空格上填入1-9的數(shù)字。使1-9每個數(shù)字在每一行、每一列和每一宮中都只出現(xiàn)一次,所以又稱“九宮格”。
算法:
本文的實現(xiàn)采用的是回溯法。也就是說,從盤面的第一個格出發(fā),按順序遍歷所有格子。對每一個格子隨機(jī)生成一個數(shù)字,并判斷該數(shù)字在當(dāng)前的盤面下是否是合法的。如果不合法,比如同一行已經(jīng)有相同的數(shù)字了,則隨機(jī)換一個數(shù)字。如果當(dāng)前位置所有數(shù)字都不合法,那么就回退到前一個位置,換一個數(shù)字,以此類推。
要點:
- 在為每一個格子隨機(jī)生成數(shù)字的時候,為了提高效率,要保證生成的數(shù)據(jù)是不重復(fù)的。這里使用的方法是,在初始化階段就為每一個格子生成一個包含1~9的隨機(jī)順序的List。這樣在遍歷到某一個格子時,只需要按順序把這個格子所對應(yīng)的list里的值以此取出,就可以獲取隨機(jī)并且不重復(fù)的1~9的值。生成隨機(jī)順序列表可以利用Java的Collections.shuffle方法:
- ?為了判斷指定位置的某一個值是否合法,我們需要判斷三個條件:
- 該點所在行是否有相同值
- 該點所在列是否有相同值
- 該點所在九宮是否有相同值 我們知道,判斷是否有重復(fù)值的最快的方法是用HashSet。因此,我們?yōu)槊恳恍?#xff0c;每一列,以及每個九宮格都維護(hù)了一個HashSet。每當(dāng)確定一個值以后,就相應(yīng)地把該值添加到它所在的行,列和九宮格所對應(yīng)的HashSet中。同樣,在判斷某一點的值是否沖突時,只要檢查該點的行,列和九宮格所對應(yīng)的HashSet中是否有相同的值即可。
- 遍歷和回退采用的是遞歸的方法,這樣使得邏輯更加清晰。
代碼
下面是完整代碼:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*;public class Sudoku {private static final Logger LOG = LoggerFactory.getLogger( Sudoku.class );private static List<Integer>[][] grid = new List[9][];private static int[][] gridIndex = new int[9][];private static HashSet<Integer>[] rowSets = new HashSet[9];private static HashSet<Integer>[] colSets = new HashSet[9];;private static HashSet<Integer>[] blockSets = new HashSet[9];;private static List<Integer> randomValues() {List<Integer> result = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);Collections.shuffle(result);return result;}private static int blockIndex(int row, int col) {return (row / 3) * 3 + (col / 3);}private static boolean check(int row, int col) {if (row == 9 || col == 9) {return true;}while (gridIndex[row][col] < 9) {Integer value = grid[row][col].get(gridIndex[row][col]);if (rowSets[row].contains(value) ||colSets[col].contains(value) ||blockSets[blockIndex(row, col)].contains(value)) {gridIndex[row][col]++;continue;}// the current value looks good, set mapsrowSets[row].add(value);colSets[col].add(value);blockSets[blockIndex(row, col)].add(value);// check next pointint nextCol = (col + 1) % 9;int nextRow = row + (col + 1) / 9;if (check(nextRow, nextCol) == false) {// rollbackrowSets[row].remove(value);colSets[col].remove(value);blockSets[blockIndex(row, col)].remove(value);gridIndex[row][col]++;continue;}return true;}gridIndex[row][col] = 0;return false;}private static void printResult() {for (int i = 0; i < 9; i++) {for (int j = 0; j < 9; j++) {System.out.printf("%d ", grid[i][j].get(gridIndex[i][j]));}System.out.println("");}}public static void main(String[] args) {// init random value listsfor (int i = 0; i < grid.length; i++) {grid[i] = new List[9];gridIndex[i] = new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0};for (int j = 0; j < grid[i].length; j++) {grid[i][j] = randomValues();}}// init hashsets for rows, cols and blocksfor (int i = 0; i < 9; i++) {rowSets[i] = new HashSet<>();colSets[i] = new HashSet<>();blockSets[i] = new HashSet<>();}check(0, 0);printResult();} }運(yùn)行結(jié)果:
9 6 5 8 3 7 4 1 2 1 7 8 6 2 4 5 9 3 4 2 3 1 5 9 6 7 8 8 3 1 2 9 5 7 6 4 2 9 6 7 4 1 8 3 5 5 4 7 3 8 6 1 2 9 6 8 4 9 7 3 2 5 1 3 1 2 5 6 8 9 4 7 7 5 9 4 1 2 3 8 6總結(jié)
- 上一篇: 基于功率分析的侧信道攻击简介
- 下一篇: 2021-03-03