Рекурсивный возврат судоку, слишком ранний возврат

Итак, я пишу решатель судоку на С++ и столкнулся с небольшой загвоздкой. Ниже приведен код моей платы решения. Он работает для первых 3 рядов головоломки, но не повторяется при попадании в конец 4-го ряда. Глядя на код на gdb, он попадает в конец 4-й строки, возвращается к 6-му столбцу, пытается и затем не возвращается до конца.

Пара других замечаний о коде: матрица, содержащая доску судоку, начинается с 1,1, а не с 0,0. Итак, когда сначала вызываетсяsolveBoard, параметры равны (1, 1, 0). Я также добавил функции setCell и checkConflicts для большего понимания. У меня есть три вектора rowConf, colConf и sqConf для хранения значений, которые уже были помещены в соответствующую строку, столбец или квадрат. Я был в этом в течение нескольких часов и не могу заставить его пройти дальше 3-го ряда. Любая помощь очень ценится. Спасибо!

РЕДАКТИРОВАТЬ: добавлено clearCell()

bool board::solveBoard(int i, int j, int count)
{

    if (j > 9)
    {
        j = 1;
        i++;

        printBoard();
        if (isSolved())
        {
            printBoard();
            cout <<"The Board has been solved!" <<endl
                 <<" The number of recursive calls was: " <<count <<endl;
            return true;
        }
     }

     if (isBlank(i, j))
     {
         for (int n = 1; n < 10; n++)
         {
             if (setCell(i, j, (char)n + '0'))
             {
                 if (solveBoard(i, j + 1, count + 1))
                 {
                     return true;
                 }
              }
          }
      }
      else
      {
          return (solveBoard(i, j + 1, count + 1));
      }

      clearCell(i, j);
      return false;
}

bool board::setCell(int i, int j, char val)
{
    int intVal;

    intVal = atoi(&val);

    if (i >= 1 && i <= BoardSize && j >= 1 && j <= BoardSize &&
        intVal >= 1 && intVal <= BoardSize)
    {
        if (!(checkConflicts(intVal, i, j, squareNumber(i, j))))
        {
        return false;
        }

        value[i][j] = intVal;

        // Set flags of the conflicts
        rowConf[i][intVal] = true;
        colConf[j][intVal] = true;
        squConf[squareNumber(i, j)][intVal] = true;

        return true;
    }
    else
    {
        throw rangeError("bad value in setCell");
    }
}

bool board::checkConflicts(int val, int i, int j, int k)
{
    if (i < 1 && i > BoardSize && j < 1 && j > BoardSize &&
        k < 1 && k > BoardSize && val < 1 && val > BoardSize)
    {
        throw rangeError("bad value in checkConflicts()");
    }

    if (rowConf[i][val] || colConf[j][val] || squConf[k][val])
    {
        return false;
    }
    else
    {
        return true;
    }
}

Initial Board:
 -----------------------------
| 3       |    8    |          -----------------------------
|         | 7       |       5  -----------------------------
| 1       |         |          ----------------------------- 
 -----------------------------
|         |         | 3  6     -----------------------------
|       2 |       4 |          -----------------------------
|    7    |         |          -----------------------------
 -----------------------------
|         |    6    | 1  3     -----------------------------
|    4  5 | 2       |          -----------------------------
|         |         | 8        -----------------------------
 -----------------------------

Final Output:
 -----------------------------
| 3  2  4 | 1  8  5 | 6  7  9  -----------------------------
| 6  8  9 | 7  2  3 | 4  1  5  -----------------------------
| 1  5  7 | 4  9  6 | 2  8  3  -----------------------------
 -----------------------------
|         |         | 3  6     -----------------------------
|       2 |       4 |          -----------------------------
|    7    |         |          -----------------------------
 -----------------------------
|         |    6    | 1  3     -----------------------------
|    4  5 | 2       |          -----------------------------
|         |         | 8        -----------------------------
 -----------------------------

void board::clearCell(int i, int j)
{
    int intVal;

    if (i >= 1 && i <= BoardSize && j >= 1 && j <= BoardSize)
    {
        if (value[i][j] != -1)
        {
            intVal = value[i][j];
            rowConf[i][intVal] = false;
            colConf[j][intVal] = false;
            squConf[squareNumber(i, j)][intVal] = false;
            value[i][j] = -1;
         }
    }
    else
    {
        throw rangeError("bad value in setCell");
    }
}

person zberry92    schedule 04.11.2012    source источник
comment
Выводит ли это, что доска решена? Это было бы одной из возможностей для завершения рекурсии.   -  person phant0m    schedule 04.11.2012
comment
Также предоставьте функцию clearCell.   -  person phant0m    schedule 04.11.2012
comment
Я добавил документ с чистой ячейкой. Он никогда не объявляется решенным. Происходит то, что по какой-то причине он не возвращается к началу, чтобы попробовать новый метод, поскольку нет доступных ходов для размещения, но вместо перехода к следующей итерации цикла for для первой пустой ячейки он надеется на один рекурсия и заканчивается.   -  person zberry92    schedule 04.11.2012
comment
Да, в конечном итоге он возвращается к состоянию, когда i = 1 и j = 2. Когда он пытается поместить значение, чтобы попробовать новую дорожку, rowConf для этой строки говорит, что там есть все значения, кроме 9. Таким образом, он помещает 9, переходит к 1,3 и больше не может размещать варианты, потому что код считает, что допустимых вариантов нет. По какой-то причине он не устанавливает для rowConf этой строки значение false.   -  person zberry92    schedule 04.11.2012
comment
Хорошо, моя функция clearCell определенно хромает. Это неправильно удаляет флаги в моих векторах конфликта.   -  person zberry92    schedule 04.11.2012
comment
Знаете что, можете ли вы добавить все и другие функции? Это облегчает задачу, вместо того, чтобы постепенно просить больше;)   -  person phant0m    schedule 04.11.2012
comment
Вы решили свою проблему или вам все еще нужна помощь?   -  person phant0m    schedule 07.11.2012
comment
Я пытался ввести ваши тестовые данные (Исходная плата) в свой собственный решатель, а также в другие решатели, и они не нашли решения! Возможно, вам стоит начать свои тесты с платы, на которой есть решение ;)   -  person Jens Schwarzer    schedule 09.11.2012


Ответы (2)


Ваша проблема скорее всего здесь:

if (isBlank(i, j))
 {
     for (int n = 1; n < 10; n++)
     {
         if (setCell(i, j, (char)n + '0'))
         {
             if (solveBoard(i, j + 1, count + 1))
             {
                 return true;
             }
          }
      }
  }

Каким-то образом он проходит через этот раздел, поэтому он не проходит через else в конце, но поскольку он не возвращался раньше, он застревает.

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

if (isBlank(i, j))
{
    for (int n = 1; n < 10; n++)
    {
        if (setCell(i, j, (char)n + '0'))
        {
            if (solveBoard(i, j + 1, count + 1))
            {
                return true;
            } else {
                echo 'Looks like it ended on the farthest-level..';
          } 
      } else {
          echo 'Looks like it ended on the second-farthest level.';
      }
  }
person Fernando Cordeiro    schedule 05.11.2012
comment
Почему у вас есть несколько предложений else? Кроме того, ваш else в основном говорит: "If it didn't work the first time, let's try that once more". Это не сработает. - person phant0m; 05.11.2012
comment
@phant0m, спасибо, редактирую. Это просто еще один для каждого, если это не работает. В любом случае, я хочу сказать, что это похоже на то, где он выходит из кода OP, и OP должен найти способ отследить это. - person Fernando Cordeiro; 06.11.2012

Функция atoi ожидает строку в качестве аргумента, то есть массив символов, оканчивающийся символом '\0', ASCII NUL. Вы указываете параметр как указатель на символ (эквивалентный некоторому массиву символов), но не гарантируете, что он завершается нулем. Пожалуйста, замените intVal = atoi(&val); на intVal = (int)val - '0';

И ваш checkConflicts должен иметь || операторов вместо && в первых if.

Это, вероятно, не причины ошибки, но, безусловно, нуждается в исправлении.

person CiaPan    schedule 09.03.2014