Рекурсивный метод решения судоку

В настоящее время я изучаю свой второй курс программирования на 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;
    }

}

Итак, у кого-нибудь есть идеи?


person user2155599    schedule 11.03.2013    source источник
comment
вы пробовали его отлаживать?   -  person kunal    schedule 11.03.2013


Ответы (3)


Кажется, что ваша программа просто проверяет, может ли заданное число поместиться в заданный слот, проверяет строку, столбец и локальную сетку 3x3 и помещает его туда навсегда, если эти три проверки пройдены.

Это проблематично, поскольку большинство судоку не дают ни одной возможности при таких простых проверках.

Вам нужно будет составить список возможных значений для каждого места, а затем начать использовать такие методы, как двойные пары для дальнейшего решения.

Поскольку это компьютер и он быстрый, после того, как вы воспользуетесь некоторыми из этих методов, вы сможете начать его перебор.

Другой подход состоял бы в том, чтобы перевести задачу судоку в формулу SAT, передать ее в решатель SAT и преобразовать решение обратно в решение судоку. В наши дни существуют очень продвинутые решатели SAT, которые очень быстро обрабатывают судоку 9x9. Здесь более подробное объяснение.

person Community    schedule 14.03.2013

В вашем методе решения вы проверяете, является ли судоку[i][j]==0, прежде чем вносить изменения. Это означает, что однажды поставив число, правильное оно или неправильное, вы уже никогда его не измените. Вы должны быть в состоянии отступить от ошибочных ходов.

person phatfingers    schedule 16.03.2013
comment
Это может быть такое же простое исправление, как удаление оператора if. - person phatfingers; 17.03.2013

Вы можете найти упрощенную программу судоку Java здесь. .html

если вы можете поделиться полным исходным кодом, включая метод checkFill, мы можем помочь вам в дальнейшей отладке

person Joe2013    schedule 11.03.2013
comment
Я не пробовал отлаживать его, так как у меня нет большого опыта в этом. - person user2155599; 11.03.2013
comment
Тогда относитесь к этому как к возможности получить опыт с ним. Это бесценный навык, который вам часто понадобится. - person meriton; 21.04.2013