Решатель судоку Повторяющийся стек с методом проб и ошибок

Я создаю решатель судоку, который использует метод «пробы и неудача» для решения любой проблемы. Мой алгоритм:

1) Обновление (метод, который удаляет любое возможное значение, которое уже задано как окончательное значение для элемента в той же строке, столбце или квадрате)

2) Получить минимальный элемент, который имеет минимальное количество возможных значений

3) начните решать, предполагая, что первое возможное значение является окончательным значением

4) сохранить текущее состояние в стек

5) Попробуйте решить

5-а) Если решено, вернуться

5-б) если не решено и с недействительным судоку, то всплывает предыдущее состояние

6) Повторите шаг 3) для всех возможных значений (9)

7) Повторяйте шаг 2), пока головоломка не будет решена.

это мой код

Stack<Element[][]> myStack= new Stack<>();
private Element[][] mySudoku;
public void solve(){
        update();//remove all final values from all possible values for each element
        if(isSudokuSolved(mySudoku)){
                return;
        }
        //find a cell that is not confirmed and has the minimal candidates
        int celli=-1,cellj=-1, p=10;
        for(int i=0;i<9;i++){
            for(int j=0;j<9;j++){
                if(mySudoku[i][j].getValue()==0){
                        if(mySudoku[i][j].getPossibleValues().size()<p){
                                celli=i;
                                cellj=j;
                                p=mySudoku[i][j].getPossibleValues().size();
                        }
                }
            }
        }

        try {
            for (int c = 0; c < mySudoku[celli][cellj].getPossibleValues().size() - 1; c++) {
                //save state  
                Element[][] copy=deepCopy(mySudoku);//copy the current state
                myStack.push(copy);
                //apply candidate to cell
                mySudoku[celli][cellj].setValue(mySudoku[celli][cellj].getPossibleValues().get(c));
                update();//check is solved
                if(checkValidInputSudoku(mySudoku)){
                    solve();
                }else{
                   try {
                        mySudoku = myStack.pop();
                    } catch (EmptyStackException est) {
                        //do nothing
                    } 
                }

            }
        } catch (Exception e) {

        }

        //if we have reached here then we are at the last possible value for the candidates so confirm candidate in cell
        if(celli!=-1 && cellj!=-1 && p!=10) {//Some problems happen here "out of Boundry -1 Error"
            mySudoku[celli][cellj].setValue(mySudoku[celli][cellj].getPossibleValues().get(mySudoku[celli][cellj].getPossibleValues().size()-1));
        }
}//end of solve method

Я потратил более 6 часов, пытаясь выяснить проблему. Я проверил метод Update(), метод deepCopy() и метод checkValidInputSudoku(). Все они работают нормально. Заранее спасибо


person Sami Eltamawy    schedule 30.04.2013    source источник


Ответы (1)


Я вижу одну проблему в вашем коде. У вас есть петля, которая отпиливает ветку, на которой сидит:

for(int c = 0; c < mySudoku[celli][cellj].getPossibleValues().size() - 1; c++) {
    ...
    mySudoku[celli][cellj].setValue(mySudoku[celli]cellj].getPossibleValues().get(c));
    ...
}

Кроме того, у вас отсутствует одно из значений, оно должно быть for(c=0; c!=size; ++c), т.е. не size - 1. Кроме того, вызов getPossibleValues() всего один раз сделал бы этот код более читабельным. Наконец, ловить и игнорировать недополнение стека просто глупо, потому что, насколько я могу судить, он скрывает ошибки в вашем алгоритме. Если вы не знаете, как обработать ошибку, не просто заглушите ее. Поскольку java требует, чтобы вы поймали его, поместите его в самое внешнее место или, по крайней мере, прервите или сделайте что-нибудь, но не игнорируйте его!

Еще одна вещь: вы рекурсивно передаете данные контекста через mySodoku и myStack. Это полностью упускает из виду точку рекурсии (или, по крайней мере, то, как она обычно используется), потому что стек вызовов функций — это единственный стек, который вам нужен. Использование их для передачи параметров только усложняет задачу. Вместо этого функция должна возвращать частичную головоломку содоку и возвращать либо полностью решенную головоломку, либо ноль. Использование легче отличить, чем исключение, которое вы используете сейчас, и это обычная и ожидаемая вещь, а не исключительная. Затем, пробуя разные варианты, вы по очереди устанавливаете в ячейку значения и выполняете рекурсию до тех пор, пока вызов не вернет значение null. Если ни один из вариантов не возвращает решение, вы очищаете ячейку и возвращаете значение null самостоятельно.

solve(sodoku):
    if sodoku is solved:
        return true
    if sodoku is invalid:
        return false

    c = some empty cell
    for v in 1...9:
        // set to a value and recurse
        c = v
        if solve(sodoku):
            // found a solution
            return true
    // no solution found, clear cell and return failure
    c = null
    return false

Кстати: эта стратегия называется «возврат». Использование ячейки с наименьшим количеством возможных значений называется «обрезкой», что позволяет отсечь целые ветви от дерева поиска. Фактическое определение возможных значений также помогает избежать нескольких бесполезных попыток.

person Ulrich Eckhardt    schedule 30.04.2013
comment
Большое спасибо за эти советы., но я НЕ понимаю необходимость c=какая-то пустая ячейка, почему бы мне не изменить во всей головоломке mySudoku и не вернуть предыдущее состояние в случае неудачи? Какой тип od res и soduko я верну? - person Sami Eltamawy; 01.05.2013
comment
я должен дать res начальное значение? - person Sami Eltamawy; 01.05.2013
comment
Причина обнуления ячейки заключается в том, что в ней не должно оставаться неправильное значение. Однако я предполагаю, что существует только одна содоку, и она передается по ссылке между функциями. В этом случае возвращать null в случае неудачи глупо, и это должно быть логическое значение. Я исправлю свой псевдокод. - person Ulrich Eckhardt; 01.05.2013