2013-03-11 130 views
1

我目前正在學習我在java中的第二門編程課程,並且需要完成一門任務才能通過課程。基本上它是關於編寫一個遞歸解決數獨與回溯的程序。這是我想出的算法。我用一個9x9數組來表示開始時填充零的網格。 checkFill檢查是否可以將數字(var)插入位置[i] [j]。問題是它只能部分解決數獨它總是返回false(沒有解決方案),一些單元格仍然包含零。我在這裏做錯了什麼?用於解決數獨的遞歸方法

import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.*; 

public class Sudoku { 
    private int[][] sudoku; 
    private JFrame frame; 
    private JFormattedTextField[][] sudokuSquares; 
    private JButton solve, clear; 

    public Sudoku() { 
     sudoku = new int[9][9]; 
     frame = new JFrame("Sudoku Solver"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     JPanel northPanel = new JPanel(); 
     northPanel.setLayout(new GridLayout(9, 9)); 
     northPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 
     sudokuSquares = new JFormattedTextField[9][9]; 
     Font font1 = new Font("SansSerif", Font.BOLD, 20); 
     for(int i = 0; i < 9; i++) { 
      for(int j = 0; j < 9; j++) { 
       sudokuSquares[i][j] = new JFormattedTextField(); 
       sudokuSquares[i][j].setHorizontalAlignment(JTextField.CENTER); 
       sudokuSquares[i][j].setFont(font1); 
       sudokuSquares[i][j].setBorder(BorderFactory.createEtchedBorder(javax.swing.border.EtchedBorder.RAISED)); 
       northPanel.add(sudokuSquares[i][j]); 
      } 
     } 
     setColor(); 
     frame.add(northPanel, BorderLayout.NORTH); 
     JPanel southPanel = new JPanel(); 
     solve = new JButton("Solve"); 
     solve.addActionListener(new SolveButtonListener()); 
     clear = new JButton("Clear"); 
     clear.addActionListener(new ClearButtonListener()); 
     southPanel.add(clear); 
     southPanel.add(solve); 
     frame.add(southPanel, BorderLayout.SOUTH); 
     frame.setResizable(false); 
     frame.setSize(280, 330); 
     frame.setVisible(true); 
    } 

    private void solveSudoku() { 
     boolean hasSolution = solve(0, 0); 
     if(hasSolution) { 
      JOptionPane.showMessageDialog(frame, "Sudoku has been successfully solved"); 
     } else { 
      JOptionPane.showMessageDialog(frame, "Sudoku has no solution"); 
     } 

    } 

    private class SolveButtonListener implements ActionListener { 
     @Override 
     /** 
     * Checks input for errors and outputs the sudoku matrix after it's been solved. 
     */ 
     public void actionPerformed(ActionEvent e) { 
      String s; 
      setColor(); 
      for(int i = 0; i < 9; i++) { 
       for(int j = 0; j < 9; j++) { 
        s = sudokuSquares[i][j].getText(); 
        if(s.isEmpty()) { 
         s = "0"; 
        } else if(s.length() > 1 || !Character.isDigit(s.charAt(0)) || s.charAt(0) == '0') { 
         sudokuSquares[i][j].setBackground(Color.RED); 
         JOptionPane.showMessageDialog(frame, "Invalid entry! Please enter a number between 1-9"); 
         return; 
        } 
        sudoku[i][j] = Integer.parseInt(s); 
       } 
      } 
      solveSudoku(); 
      for(int i = 0; i < 9; i++) { 
       for(int j = 0; j < 9; j++) { 
        sudokuSquares[i][j].setText(String.valueOf(sudoku[i][j])); 
       } 
      } 
     } 
    } 

    private class ClearButtonListener implements ActionListener { 
     @Override 
     public void actionPerformed(ActionEvent e) { 
      for(int i = 0; i < 9; i++) { 
       for(int j = 0; j < 9; j++) { 
        sudokuSquares[i][j].setText(""); 
       } 
      } 
      setColor(); 
     } 
    } 

    /** 
    * Sets the background color of sudoku cells 
    */ 
    private void setColor() { 
     for(int i = 0; i < 9; i++) { 
      for(int j = 0; j < 9; j++) { 
       if((i/3 < 1 || i/3 >= 2) && (j/3 < 1 || j/3 >= 2) || 
         (i/3 >= 1 && i/3 < 2) && (j/3 >= 1 && j/3 < 2)) { 
        sudokuSquares[i][j].setBackground(new Color(220, 220, 220)); 
       } else { 
        sudokuSquares[i][j].setBackground(Color.WHITE); 
       } 
      } 
     } 
    } 

    /** 
    * Solves the sudoku through recursive technique 
    * @param i row 
    * @param j column 
    * @return returns true if the sudoku has a solution, returns false otherwise 
    */ 
    private boolean solve(int i, int j) { 
     if(i > 8) { 
      return true; 
     } 
     if(sudoku[i][j] == 0) { 
      for(int var = 1; var < 10; var++) { 
       if(checkFill(i, j, var)) { 
        sudoku[i][j] = var; 
        if(j >= 8) { 
         solve(i + 1, 0); 
        } else { 
         solve(i, j+1); 
        } 
       } 
      } 
     } else { 
      if(j >= 8) { 
       solve(i + 1, 0); 
      } else { 
       solve(i, j+1); 
      } 
     } 
     return false; 
    } 

    /** 
    * Checks if var could be inserted at position [i][j] 
    * @param i row 
    * @param j column 
    * @param var number to be checked for possible insertion 
    * @return 
    */ 
    private boolean checkFill(int i, int j, int var) { 
     for(int a = 0; a < 9; a++) { 
      if(sudoku[a][j] == var) { 
       return false; 
      } 
     } 
     for(int a = 0; a < 9; a++) { 
      if(sudoku[i][a] == var) { 
       return false; 
      } 
     } 
     int tempI = (i/3) * 3; 
     int tempJ = (j/3) * 3; 
     for(int a = 0; a < 3; a++) { 
      for(int b = 0; b < 3; b++) { 
       if(sudoku[tempI + a][tempJ + b] == var) 
        return false; 
      } 
     } 
     return true; 
    } 

} 

那麼有人有什麼想法?

+1

你試過調試過嗎? – kunal 2013-03-11 06:03:41

回答

2

看來,你的程序將只檢查一個給定的數量可以放入一個給定的插槽,檢查行,列和本地3x3網格,如果這三個檢查通過,則將其永久放置在那裏。

這是有問題的,因爲大多數數獨在這種簡單的檢查中不會產生單一的可能性。

您需要列出每個點的可能值,然後開始使用諸如double pairs等技術來進一步解決問題。

由於它是一臺計算機,速度很快,所以在使用其中一些技巧之後,您可以開始蠻橫強制它。

另一種方法是將數獨問題轉換爲SAT公式,將其提供給SAT解算器,然後將解決方案轉換回數獨解決方案。目前有非常先進的SAT求解器,可以很快處理9x9數獨。 Here是一個更深入的解釋。

0

你可以在這裏找到一個簡化的java數獨程序。 http://www.heimetli.ch/ffh/simplifiedsudoku.html

,如果你可以分享你完整的源代碼包括方法checkFill,我們可以幫助你在調試運行進一步

+0

我還沒有嘗試調試它,因爲我沒有太多的經驗。 – user2155599 2013-03-11 07:15:24

+1

然後將此視爲一次獲得體驗的機會。這是你需要經常需要的寶貴技能。 – meriton 2013-04-21 14:41:11

1

在您的解決方法中,您在進行更改之前測試sudoku [i] [j] == 0。這意味着一旦你發出一個號碼,無論是對還是錯,你都不會改變它。你需要能夠退出錯誤的舉動。

+0

這可能與刪除if語句一樣簡單。 – phatfingers 2013-03-17 05:39:52