Рекурсивный алгоритм обратного отслеживания в C для решения судоку - PullRequest
0 голосов
/ 05 июня 2019

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

Может ли кто-нибудь мне помочь?

Метод решения был пуст, поэтому все, что находится внутри этого метода, просто пытается от меня.

sudoku.h

#ifndef _SUDOKU_H_
#define _SUDOKU_H_

#define SIZE 9
#define SQRT_SIZE 3

void init(int begin[SIZE][SIZE]);
void print();
int checkValueInField(int value, int row, int col);
int setValueInField(int value, int row, int col);
int removeValueFromField(int row, int col);
int getValueFromField(int row, int col);
int solve(int row, int col);

#endif /* _SUDOKU_H_ */

sudoku.c

#include "sudoku.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

#define SIZE 9
#define SQRT_SIZE 3

int field[SIZE][SIZE];
int initial[SIZE][SIZE];


/* Initializes the sudoku array.
 * The field initial keeps the original start value for
 * figuring out if a value is fixed or can be changed. */
void init(int begin[SIZE][SIZE]) {
    memcpy(field, begin, SIZE * SIZE * sizeof(int));
    memcpy(initial, begin, SIZE * SIZE * sizeof(int));
}

/* really pretty prints the sudoku array */
void print() {
    int row, col;
    // print the first line
    printf("||");
    for (col = 0; col < SIZE - 1; col++) {
        if (col % SQRT_SIZE == SQRT_SIZE - 1)
            printf("===++");
        else
            printf("===+");
    }
    printf("===||\n");
    // loop through all rows of the array
    for (row = 0; row < SIZE; row++) {
        // print the line with field values
        for (col = 0; col < SIZE; col++) {
            if (col % SQRT_SIZE == 0)
                printf("|| ");
            else
                printf("| ");
            if (field[row][col] == 0)
                printf("  ");
            else
                printf("%d ", field[row][col]);
        }
        // print the separation line;
        // depending on the result of the modulo operation
        // print a single or double line
        printf("||\n||");
        if (row % SQRT_SIZE == SQRT_SIZE - 1) {
            for (col = 0; col < SIZE - 1; col++) {
                if (col % SQRT_SIZE == SQRT_SIZE - 1)
                    printf("===++");
                else
                    printf("===+");
            }
            printf("===||\n");
        }
        else {
            for (col = 0; col < SIZE - 1; col++) {
                if (col % SQRT_SIZE == SQRT_SIZE - 1)
                    printf("---++");
                else
                    printf("---+");
            }
            printf("---||\n");
        }
    }
}

/* Checks if the value is valid and can be set into the field.
 * The function returns false if the value is already present or
 * has been one of the initial values. */
int checkValueInField(int value, int row, int col) {
    int i, r, c;
    int squareRow;
    int squareCol;
    // checks for initial values
    if (initial[row][col] != 0) {
        if (initial[row][col] == value)
            return 1;
        else
            return 0;
    }

    // check horizontally
    for (i = 0; i < SIZE; i++) {
        if (field[row][i] == value) return 0;
    }

    // check vertically
    for (i = 0; i < SIZE; i++) {
        if (field[i][col] == value) return 0;
    }

    // check square
    squareRow = row / SQRT_SIZE;
    squareCol = col / SQRT_SIZE;
    for (r = squareRow * SQRT_SIZE; r < squareRow * SQRT_SIZE + SQRT_SIZE; r++) {
        for (c = squareCol * SQRT_SIZE; c < squareCol * SQRT_SIZE + SQRT_SIZE; c++) {
            if (field[r][c] == value) return 0;
        }
    }

    return 1;
}

/* Set a value in the sudoku field if the field is empty.
 * The method returns false if the field contains a fixed number. */
int setValueInField(int value, int row, int col) {
    if (initial[row][col] == 0) {
        field[row][col] = value;
        return 1;
    }
    else if (initial[row][col] == value)
        return 1;
    return 0;
}

/* Removes a value in the sudoku field if it doesn't contain an initial value.
 * The method returns false if the field contains a fixed number and cannot be
 * removed. */
int removeValueFromField(int row, int col) {
    if (initial[row][col] == 0) {
        field[row][col] = 0;
        return 1;
    }
    return 0;
}

/* Returns the value in the field */
int getValueFromField(int row, int col) {
    return field[row][col];
}

/* Return true if you've found a valid solution for the sudoku. Use the
 * return value to abort the backtracking algorithm if you've found the
 * first solution, otherwise you would search for a possible solution. */
int solve(int row, int col) {
    /* Implement a backtracking for solving the sudoku */
    for (int i = 1; i <= 9; i++) {
        if ((checkValueInField(i, row, col)) == 1) {
            setValueInField(i, row, col);
        }
        solve(row, col + 1);
        solve(row + 1, col);
    }
    return 0;
}

main.c

#include <stdio.h>
#include "sudoku.h"

int main(int argc, char * const argv[]) {
    int initial[SIZE][SIZE] = {
        {0, 1, 0, 0, 0, 9, 0, 5, 0},
        {0, 9, 0, 0, 0, 0, 4, 8, 0},
        {0, 6, 0, 1, 0, 4, 0, 0, 0},
        {0, 0, 5, 0, 0, 0, 9, 3, 0},
        {0, 0, 0, 7, 0, 2, 0, 0, 0},
        {0, 2, 1, 0, 0, 0, 8, 0, 0},
        {4, 0, 0, 0, 8, 0, 6, 0, 9},
        {0, 0, 0, 0, 6, 0, 5, 0, 3},
        {2, 0, 0, 0, 3, 0, 0, 0, 0},
    };

    init(initial);
    print();
    solve(0, 0);
    print();

    return 0;
}

1 Ответ

1 голос
/ 06 июня 2019

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

Рекурсивное перемещение по сетке

Ваше решение будет состоять в решении одной позиции (отслеживается по строке и столбцу) за один раз и рекурсивно проверьте решение следующей позиции.

Таким образом, ваша функция разрешения должна хотя бы пройти через сетку

int solve(int row, int col) {
    // solve next column in the current row
    return solve(row, col + 1);
}

Как вы видите, проблема состоит в том, что col будетрастет бесконечно, не проверяя другие ряды.(кстати, первый элемент массива в C имеет индекс 0)

Поэтому нам нужно перейти к другой строке, как только мы достигнем конца этой (Предполагая, что END_COLUMN_INDEX содержит индекспоследнего столбца)

   if(col == END_COLUMN_INDEX) { // we just reached the end of the current row
        return solve(row+1, 0); // solve the beginning of the next row
   }

Теперь ваше решение автоматически перейдет к следующему ряду, но как насчет того, когда мы достигнем последнего ряда (при условии, что END_ROW_INDEX содержит индекс столбца строки)

   if((col == END_COLUMN_INDEX) && (row == END_ROW_INDEX)) { // we reached the end of the grid meaning that we might have successfully solved the whole grid
       return 1; // return true
   }

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

solve(0,0) -> solve(0,1)
solve(0,1) -> solve(0,2)
solve(0,2) -> solve(0,3)
...
solve(0,END_COLUMN_INDEX - 1) -> solve(0, END_COLUMN_INDEX)
solve(0, END_COLUMN_INDEX) -> solve(1, 0)
...
...
solve(END_ROW_INDEX , END_COLUMN_INDEX - 1) -> solve(END_ROW_INDEX , END_COLUMN_INDEX) -> return true
(the true value is returned through each upper level of recursion)

Сейчас мы рекурсивно движемся по сетке

Решение Судоку

для каждой ячейки необходимо проверить

  1. , заполнена ли уже ячейка (затем вы можете solve следующая ячейка)
  2. проверить значение (от 1 до 9, используя * 1035)*):
  3. если найденное значение в порядке, вы можете сохранить значение в сетке (setValueInField) и попробовать solve следующую ячейку
  4. иначе попробовать следующее значение
  5. что если мы попробовали все значения и ничего не подходит?
  6. то естьВвиду того, что верхний уровень рекурсии неверен, поэтому функция расчета вернет false, чтобы сообщить, что значение в предыдущей ячейке неверно (верхний уровень будет на шаге 1 или 3)

  7. проверка того, что solve -ing следующая возвращенная ячейка (на шаге 1 или 3)

  8. true, означает, что мы решили следующую ячейку и все последующие ячейки
  9. false означает, что у нас есть значение, которое сделало следующую ячейку недоступной для solve, возможно, мы захотим попробовать другие значения (возврат).

при возврате false к верхнему значению рекурсии вы не должны забывать восстановить значение текущей ячейки до ее первоначального значения (removeValueFromField).

Установка этого значениявсе вместе

На данный момент у вас должны быть все руководства, чтобы решить вашу проблему и написать рекурсивную функцию решения судоку.

Кроме того, Интернет полон отличного примера кода для решения судоку.

...