Sudoku Solver

Page web ISIMA
Fichier .org source
Défi de programmation - Sudoku Solver

1

1. Introduction

Le but de ce problème est de simplement résoudre un sudoku.
Un sudoku est une grille de 9x9, divisée en grille de 3x3. Chaque case doit être remplie avec un numéro entre 1 et 9, chaque numéro doit être présent une unique fois sur sa ligne, colonne et sous-grille.
Dans cet exercice, une case vide est représentée par un 0.

Exemple de sudoku :

sudoku.png

2. Approche utilisée

2.1. Description synoptique

Le principe de mon algorithme est de tester les cases une par une, si il y a déjà un chiffre on peut passer à la suivante, mais si la case est vide (si elle contient un 0), on test chaque chiffres de 1 à 9 si il est possible sur cette case (si il n’est ni présent de la ligne, colonne, ou sous-grille). Ensuite on passe à la case suivante.

2.2. Algorithme

Fonction isValid :
Prend en entrée une grille et une position.
Si position = 9*9:

  • Renvoyer 1

Si la case d’indice position de la grille différente de 0:

  • Renvoyer isValid(grille, position+1)

Boucle sur k de 1 à 9 (inclus) :

  • Si k n’est ni sur la ligne, colonne et sous-grille de la case =position=m
    • Case d’indice position = k
    • Si isValid(grille, position+1):
      • Renvoyer 1

Case d’indice position = 0
Renvoyer 0

Programme principal :
grille = entrée utilisateur
isValid(grille, 0)
afficher grille

3. Resolution

3.1. Programme

#include <stdio.h>

void display(int grille[9][9]) {
    for (int i=0; i<9; ++i) {
        for (int j=0; j<9; ++j) {
            printf("%d", grille[i][j]);
        }
        printf("\n");
    }
}

int missingOnLine(int k, int grille[9][9], int i) {
    for (int j=0; j < 9; j++)
        if (grille[i][j] == k)
            return 0;
    return 1;
}

int missingOnCol(int k, int grille[9][9], int j) {
    for (int i=0; i < 9; i++)
        if (grille[i][j] == k)
            return 0;
    return 1;
}

int missingOnBlock(int k, int grille[9][9], int i, int j) {
    int i2 = i-(i%3), j2 = j-(j%3);
    for (i=i2; i < i2+3; i++)
        for (j=j2; j < j2+3; j++)
            if (grille[i][j] == k)
                return 0;
    return 1;
}

int isValid(int grille[9][9], int position) {
    // Si on a parcouru tout le sudoku, on sort de la fonction
    if (position == 9*9)
        return 1;

    int i = position/9, j = position%9;

    if (grille[i][j] != 0)
        return isValid(grille, position+1);

    for (int k=1; k <= 9; k++) {
        if (missingOnLine(k,grille,i) && missingOnCol(k,grille,j) && missingOnBlock(k,grille,i,j)) {
            grille[i][j] = k;

            if (isValid(grille, position+1))
                return 1;
        }
    }
    grille[i][j] = 0;
    return 0;
}

int main() {
    int grille[9][9];

    for (int i = 0; i < 9; i++) {
        char line[10];
        scanf("%[^\n]", line); fgetc(stdin);

        for (int j=0; j<9; ++j) {
            grille[i][j] = line[j] - '0';
        }
    }
    isValid(grille, 0);
    display(grille);

    return 0;
}

3.2. Test

A partir de la grille incomplète suivante (sudoku.txt) :

800000000
003600000
070090200
050007000
000045700
000100030
001000068
008500010
090000400

Le programme complète la grille et l’affiche :

812753649
943682175
675491283
154237896
369845721
287169534
521974368
438526917
796318452

4. Comparaison avec une autre solution

A partir de cette solution externe :

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define N (3)   // Square root of the edge length

char board  [ N*N ][ N*N+1 ];
char board_t[ N*N ][ N*N+1 ]; // Transposed board

bool good( int x, int y, char n );  // Test if number fits
bool solve();

int main()
{
    for (int y = 0; y < N*N; y++) {
        fgets(board[y], N*N+1, stdin);
        fgetc(stdin);
        board[y][N*N] = board_t[y][N*N] = 0;
        for(int x = 0; x < N*N; x++)
            board_t[x][y] = board[y][x];
    }
    solve();
    for (int i = 0; i < N*N; i++ )
        puts( board[i] );

    return 0;
}

bool good( int x, int y, char n ) {
    int xs = x / N * N;
    int ys = y / N * N;
    for( int i = 0; i < N; i++ )
        for( int j = 0; j < N; j++ )
            if( board[ys+i][xs+j] == n )
                return false;
    if( strchr( board[y], n ) || strchr( board_t[x], n ) )
        return false;
    return true;
}

bool solve() {
    for( int y = 0; y < N*N; y++ ) {
        for( int x = 0; x < N*N; x++ ) {
            if( board[y][x] > '0' )
                continue;
            for( char n = '1'; n <= '9'; n++ ) {
                if( good( x, y, n ) ) {
                    board[y][x] = board_t[x][y] = n;
                    if( solve() )
                        return true;
                    board[y][x] = board_t[x][y] = '0';
                }
            }
            return false;
        }
    }
    return true;
}

Cette solution utilise une approche de brute forcing, elle propose de parcourir une par une chaque case du sudoku, et de tester pour chaque case, chaque nombre de 1 à 9, et recommence jusqu’à avoir trouvé une grille correcte.

5. Bilan

Le problème du sudoku est un problème que j’avais déjà résolu dans le passé (en python), il a été plutôt simple à reproduire en C.

Auteur: Rémi SCHIRRA

Created: 2024-05-03 ven. 14:05