web-dev-qa-db-fra.com

Un algorithme cool pour vérifier un champ de sudoku?

Est-ce que quelqu'un connaît un algorithme simple pour vérifier si une configuration de Sudoku est valide? L'algorithme le plus simple que j'ai créé est (pour un tableau de taille n) en pseudocode

for each row
  for each number k in 1..n
    if k is not in the row (using another for-loop)
      return not-a-solution

..do the same for each column

Mais je suis tout à fait sûr qu'il doit y avoir une meilleure solution (dans le sens de plus élégante). L'efficacité est tout à fait sans importance.

24
user18670

Vous devez vérifier toutes les contraintes du Sudoku:

  • vérifier la somme sur chaque ligne
  • vérifier la somme sur chaque colonne
  • vérifier la somme sur chaque case
  • vérifier les numéros en double sur chaque ligne
  • vérifier les numéros en double sur chaque colonne
  • vérifier les numéros en double sur chaque case

ce n'est pas 6 contrôles tout à fait .. en utilisant une approche de force brute. Une sorte d’optimisation mathématique peut être utilisée si vous connaissez la taille du tableau (3x3 ou 9x9)

Éditer : explication de la contrainte de somme: Vérifier la somme en premier (et s’arrêter si la somme n’est pas 45) est beaucoup plus rapide (et plus simple) que la recherche de doublons.Il offre un moyen simple de rejeter une mauvaise solution. 

23
Radu094

Peter Norvig a un excellent article sur la résolution de puzzles de sudoku (avec python),

http://norvig.com/sudoku.html

C'est peut-être trop pour ce que vous voulez faire, mais c'est quand même une excellente lecture

24
daniel

Juste une pensée: n'avez-vous pas besoin de vérifier également les nombres dans chaque carré 3x3? 

J'essaie de comprendre s'il est possible de satisfaire les conditions de lignes et de colonnes sans avoir un sudoku correct

7
Luk

Vérifiez chaque ligne, colonne et case de manière à ce qu'elle contienne les numéros 1 à 9, sans doublons. La plupart des réponses ici en parlent déjà.

Mais comment faire cela efficacement? Réponse: Utilisez une boucle comme

result=0;
for each entry:
  result |= 1<<(value-1)
return (result==511);

Chaque nombre définira un bit du résultat. Si tous les 9 numéros sont uniques, les 9 bits les plus bas seront définis. Ainsi, le test de "vérification des doublons" est simplement une vérification que tous les 9 bits sont définis, ce qui correspond au résultat du test == 511 . Vous devez effectuer 27 de ces vérifications .. un pour chaque ligne. , colonne et boîte.

7
SPWorley

Créez un tableau de booléens pour chaque ligne, colonne et carré. L'indice du tableau représente la valeur qui a été placée dans cette ligne, cette colonne ou ce carré. En d’autres termes, si vous ajoutez 5 à la deuxième ligne, la première colonne, vous définissez les lignes [2] [5] sur true, ainsi que les colonnes [1] [5] et les carrés [4] [5], pour indiquer que la ligne, la colonne et le carré ont maintenant une valeur 5.

Quelle que soit la manière dont votre conseil d’origine est représenté, il peut s’agir d’un moyen simple et très rapide de le vérifier pour son exhaustivité et son exactitude. Prenez simplement les nombres dans l’ordre dans lequel ils apparaissent sur le tableau et commencez à créer cette structure de données. Lorsque vous placez des chiffres dans le tableau, l'opération O(1) permet de déterminer si des valeurs sont dupliquées dans une ligne, une colonne ou un carré donné. (Vous voudrez également vérifier que chaque valeur est un nombre légitime: s’ils vous attribuent un blanc ou un nombre trop élevé, vous savez que le tableau n’est pas complet.) Lorsque vous arrivez à la fin du tableau, vous Je saurai que toutes les valeurs sont correctes et qu’aucune vérification supplémentaire n’est requise.

Quelqu'un a également souligné que vous pouvez utiliser n'importe quelle forme de Set pour ce faire. Les tableaux ainsi agencés ne sont qu’une forme particulièrement légère et performante d’un ensemble qui convient bien à un petit ensemble fixe de nombres consécutifs. Si vous connaissez la taille de votre conseil, vous pouvez également choisir de masquer un peu, mais c'est probablement un peu trop fastidieux, étant donné que l'efficacité ne représente pas un gros problème pour vous.

4
StriplingWarrior

C'est ma solution en Python, je suis content de voir que c'est la plus courte à ce jour :) 

Le code: 

def check(sud):
    zippedsud = Zip(*sud)

    boxedsud=[]
    for li,line in enumerate(sud):
        for box in range(3):
            if not li % 3: boxedsud.append([])    # build a new box every 3 lines
            boxedsud[box + li/3*3].extend(line[box*3:box*3+3])

    for li in range(9):
        if [x for x in [set(sud[li]), set(zippedsud[li]), set(boxedsud[li])] if x != set(range(1,10))]:
            return False
    return True  

Et l'exécution:

sudoku=[
[7, 5, 1,  8, 4, 3,  9, 2, 6],
[8, 9, 3,  6, 2, 5,  1, 7, 4], 
[6, 4, 2,  1, 7, 9,  5, 8, 3],
[4, 2, 5,  3, 1, 6,  7, 9, 8],
[1, 7, 6,  9, 8, 2,  3, 4, 5],
[9, 3, 8,  7, 5, 4,  6, 1, 2],
[3, 6, 4,  2, 9, 7,  8, 5, 1],
[2, 8, 9,  5, 3, 1,  4, 6, 7],
[5, 1, 7,  4, 6, 8,  2, 3, 9]]

print check(sudoku)        
4
Kami

Vous pouvez extraire toutes les valeurs d'un ensemble (ligne, colonne, zone) dans une liste, la trier, puis la comparer à '(1, 2, 3, 4, 5, 6, 7, 8, 9)

2
Svante

si la somme et la multiplication d'une ligne/col est égale au nombre exact 45/362880

2
user189317

Créez des ensembles de cellules, où chaque ensemble contient 9 cellules, et créez des ensembles pour les colonnes verticales, les lignes horizontales et les carrés 3x3.

Ensuite, pour chaque cellule, identifiez simplement les ensembles auxquels elle appartient et analysez-les.

Je l'ai fait une fois pour un projet de classe. J'ai utilisé un total de 27 ensembles pour représenter chaque ligne, colonne et boîte. Je vérifiais les numéros au fur et à mesure que je les ajoutais à chaque ensemble (chaque placement d'un nombre entraînait l'ajout du nombre à 3 ensembles, une ligne, une colonne et une case) pour que l'utilisateur ne saisisse que les chiffres 1- 9 Le seul moyen de remplir un jeu est de le remplir correctement avec des chiffres uniques. Si les 27 jeux sont tous remplis, le puzzle est résolu. Configurer les mappages de l'interface utilisateur sur les 27 ensembles était un peu fastidieux, mais rendait le reste de la logique facile à mettre en œuvre.

2
Bill the Lizard

Il y a quelque temps, j'ai écrit un vérificateur de sudoku qui vérifie le numéro en double sur chaque ligne, le numéro en double sur chaque colonne et le numéro en double sur chaque case. J'aimerais bien que quelqu'un puisse en trouver un avec quelques lignes de code Linq.

char VerifySudoku(char grid[81])
{
    for (char r = 0; r < 9; ++r)
    {
        unsigned int bigFlags = 0;

        for (char c = 0; c < 9; ++c)
        {
            unsigned short buffer = r/3*3+c/3;

                        // check horizontally
            bitFlags |= 1 << (27-grid[(r<<3)+r+c]) 
                        // check vertically
                     |  1 << (18-grid[(c<<3)+c+r])
                        // check subgrids
                     |  1 << (9-grid[(buffer<<3)+buffer+r%3*3+c%3]);

        }

        if (bitFlags != 0x7ffffff)
            return 0; // invalid
    }

    return 1; // valid
}
1
Hao Wooi Lim

Il serait très intéressant de vérifier si:

when the sum of each row/column/box equals n*(n+1)/2
and the product equals n!
with n = number of rows or columns

cela suffit aux règles d'un sudoku. Parce que cela permettrait un algorithme de O (n ^ 2), en sommant et en multipliant les bonnes cellules.

En regardant n = 9, les sommes devraient être 45, les produits 362880.

Vous feriez quelque chose comme:

for i = 0 to n-1 do
  boxsum[i] := 0;
  colsum[i] := 0;
  rowsum[i] := 0;
  boxprod[i] := 1;
  colprod[i] := 1;
  rowprod[i] := 1;    
end;

for i = 0 to n-1 do
  for j = 0 to n-1 do
    box := (i div n^1/2) + (j div n^1/2)*n^1/2;
    boxsum[box] := boxsum[box] + cell[i,j];
    boxprod[box] := boxprod[box] * cell[i,j];
    colsum[i] := colsum[i] + cell[i,j];
    colprod[i] := colprod[i] * cell[i,j];
    rowsum[j] := colsum[j] + cell[i,j];
    rowprod[j] := colprod[j] * cell[i,j];
   end;
end;

for i = 0 to n-1 do
  if boxsum[i] <> 45
  or colsum[i] <> 45
  or rowsum[i] <> 45
  or boxprod[i] <> 362880
  or colprod[i] <> 362880
  or rowprod[i] <> 362880
   return false;
1

Voici une belle approche lisible en Python:

from itertools import chain                                                                                            

def valid(puzzle):                                                                                                     
    def get_block(x,y):                                                                                                
        return chain(*[puzzle[i][3*x:3*x+3] for i in range(3*y, 3*y+3)])                                               
    rows = [set(row) for row in puzzle]                                                                                
    columns = [set(column) for column in Zip(*puzzle)]                                                                 
    blocks = [set(get_block(x,y)) for x in range(0,3) for y in range(0,3)]                                             
    return all(map(lambda s: s == set([1,2,3,4,5,6,7,8,9]), rows + columns + blocks))         

Chaque carré 3x3 est appelé un bloc et il y en a 9 dans une grille 3x3. On suppose que le casse-tête est entré sous forme de liste de liste, chaque liste interne étant une ligne.

1
bjrnt

Tout d'abord, vous devez créer un booléen "correct". Ensuite, créez une boucle for, comme indiqué précédemment. Le code pour la boucle et tout ensuite (en Java) est comme indiqué, où field est un tableau 2D à côtés égaux, col, un autre ayant les mêmes dimensions, et l, un 1D:

for(int i=0; i<field.length(); i++){
    for(int j=0; j<field[i].length; j++){
        if(field[i][j]>9||field[i][j]<1){
            checking=false;
            break;
        }
        else{
            col[field[i].length()-j][i]=field[i][j];
        }
    }
}

Je ne connais pas l'algorithme exact pour cocher les cases 3x3, mais vous devriez vérifier toutes les lignes du champ et coler avec "/*array name goes here*/[i].contains(1)&&/*array name goes here*/[i].contains(2)" (continue jusqu'à ce que vous atteigniez la longueur d'une ligne) dans une autre boucle pour .

1
user2425429
 def solution (planche): 
 pour i à bord: 
 si somme (i)! = 45: 
 renvoyer "Incorrect" 

 pour i dans l'intervalle (9): 
 temp2 = [] 
 pour x dans l'intervalle (9): 
 temp2.append (carte [i] [x]) 

 si somme (temp2)! = 45: 
 renvoyer "Incorrect" 

 return "Correct" 

 board = [] 
 pour i dans la plage (9): 
 inp = raw_input () 
 temp = [int (i) pour i dans inp] 
 board.append (temp) 

 solution d'impression (board) 

0
John Constantine

Voici le mien en C. Ne passez que chaque case une fois.

int checkSudoku(int board[]) {
  int i;
  int check[13] = { 0 };

  for (i = 0; i < 81; i++) {
    if (i % 9 == 0) {
      check[9] = 0;
      if (i % 27 == 0) {
        check[10] = 0;
        check[11] = 0;
        check[12] = 0;
      }
    }

    if (check[i % 9] & (1 << board[i])) {
      return 0;
    }
    check[i % 9] |= (1 << board[i]);

    if (check[9] & (1 << board[i])) {
      return 0;
    }
    check[9] |= (1 << board[i]);

    if (i % 9 < 3) {
      if (check[10] & (1 << board[i])) {
        return 0;
      }
      check[10] |= (1 << board[i]);
    } else if (i % 9 < 6) {
      if (check[11] & (1 << board[i])) {
        return 0;
      }
      check[11] |= (1 << board[i]);
    } else {
      if (check[12] & (1 << board[i])) {
        return 0;
      }
      check[12] |= (1 << board[i]);
    }
  }
}
0
jpiasetz

Voici un article du professeur de mathématiques J.F. Crook: Un algorithme crayon-papier pour résoudre les puzzles de sudoku

Cet article a été publié en avril 2009 et a reçu beaucoup de publicité comme solution définitive de Sudoku (voir google pour "J.F.Crook Sudoku").

Outre l'algorithme, il existe également une preuve mathématique du fonctionnement de l'algorithme (le professeur a admis qu'il ne trouvait pas le Sudoku très intéressant, il a donc ajouté des maths sur papier pour le rendre plus amusant).

0
zendar

Une optimisation mineure que vous pouvez faire est que vous pouvez vérifier les doublons dans une ligne, une colonne ou une case dans le temps O(n) plutôt que dans O (n ^ 2): lorsque vous parcourez l'ensemble des nombres, ajoutez chacun à un hashset. En fonction de la langue, vous pouvez réellement utiliser un véritable hachage, à savoir une recherche et une insertion dans le temps constantes; puis vérifier les doublons peut être fait dans la même étape en vérifiant si l'insertion a réussi ou non. C'est une amélioration mineure du code, mais passer de O (n ^ 2) à O(n) est une optimisation significative.

0
IceMetalPunk

J'écrirais une interface dont les fonctions recevraient le champ sudoku et renverraient true/false s'il s'agissait d'une solution ..__, puis implémenteraient les contraintes en tant que classes de validation uniques par contrainte.

Pour vérifier, il suffit de parcourir toutes les classes de contraintes et de vérifier que toutes les réponses sont correctes. Pour accélérer, mettez ceux qui échouent le plus probablement au premier plan et arrêtez-vous dans le premier résultat qui pointe vers un champ non valide.

Joli motif générique. ;-)

Vous pouvez bien sûr améliorer ceci pour fournir des indications sur le champ supposé faux, etc.

Première contrainte, il suffit de vérifier si tous les champs sont remplis. (Boucle simple) Deuxième vérification si tous les nombres sont dans chaque bloc (boucles imbriquées) Troisième vérification pour les lignes et les colonnes complètes (presque la même procédure que ci-dessus mais un schéma d'accès différent

0
array = [1,2,3,4,5,6,7,8,9]  
sudoku = int [][]
puzzle = 9 #9x9
columns = map []
units = map [] # box    
unit_l = 3 # box width/height
check_puzzle()    


def strike_numbers(line, line_num, columns, units, unit_l):
    count = 0
    for n in line:
        # check which unit we're in
        unit = ceil(n / unit_l) + ceil(line_num / unit_l) # this line is wrong - rushed
        if units[unit].contains(n): #is n in unit already?
             return columns, units, 1
        units[unit].add(n)
        if columns[count].contains(n): #is n in column already?
            return columns, units, 1
        columns[count].add(n)
        line.remove(n) #remove num from temp row
    return columns, units, line.length # was a number not eliminated?

def check_puzzle(columns, sudoku, puzzle, array, units):
    for (i=0;i< puzzle;i++):
        columns, units, left_over = strike_numbers(sudoku[i], i, columns, units) # iterate through rows
        if (left_over > 0): return false

Sans vérifier minutieusement, cela devrait fonctionner (avec un peu de débogage) en boucle seulement deux fois. O (n ^ 2) au lieu de O (3 (n ^ 2))

0
Josh Smeaton

Disons que sudoku [0..8,0..8] est le champ sudoku.

bool CheckSudoku(int[,] sudoku)
{
    int flag = 0;

// Check rows
for(int row = 0; row < 9; row++)
{
    flag = 0;
    for (int col = 0; col < 9; col++)
    {
        // edited : check range step (see comments)
        if ((sudoku[row, col] < 1)||(sudoku[row, col] > 9)) 
        {
            return false;
        }

        // if n-th bit is set.. but you can use a bool array for readability
        if ((flag & (1 << sudoku[row, col])) != 0) 
        {
            return false;
        }

        // set the n-th bit
        flag |= (1 << sudoku[row, col]); 
    }
}

// Check columns
for(int col= 0; col < 9; col++)
{
    flag = 0;
    for (int row = 0; row < 9; row++)
    {
        if ((flag & (1 << sudoku[row, col])) != 0)
        {
            return false;
        }
        flag |= (1 << sudoku[row, col]);
    }
}

// Check 3x3 boxes
for(int box= 0; box < 9; box++)
{
    flag = 0;
    for (int ofs = 0; ofs < 9; ofs++)
    {
        int col = (box % 3) * 3;
        int row = ((int)(box / 3)) * 3;

        if ((flag & (1 << sudoku[row, col])) != 0)
        {
            return false;
        }
        flag |= (1 << sudoku[row, col]);
    }
}
return true;

}

0
Marco M.

Voici ce que je viens de faire pour ça:

boolean checkers=true;
String checking="";
    if(a.length/3==1){}
    else{
       for(int l=1; l<a.length/3; l++){
            for(int n=0;n<3*l;n++){
                for(int lm=1; lm<a[n].length/3; lm++){
                    for(int m=0;m<3*l;m++){
                    System.out.print("    "+a[n][m]);
                    if(a[n][m]<=0){
                    System.out.print("        (Values must be positive!)        ");
                    }
                    if(n==0){
                        if(m!=0){
                        checking+=", "+a[n][m];
                    }
                    else{
                        checking+=a[n][m];
                    }
                }
                else{
                    checking+=", "+a[n][m];
                }
            }
                    }
            System.out.print("        "+checking);
            System.out.println();
                }
       }
            for (int i=1;i<=a.length*a[1].length;i++){
        if(checking.contains(Integer.toString(i))){

        }
        else{
            checkers=false;
        }
            }
        }
    checkers=checkCol(a);
    if(checking.contains("-")&&!checking.contains("--")){
        checkers=false;
    }
    System.out.println();
    if(checkers==true){
        System.out.println("This is correct! YAY!");
    }
    else{
        System.out.println("Sorry, it's not right. :-(");
    }
}
private static boolean checkCol(int[][]a){
    boolean checkers=true;
    int[][]col=new int[][]{{0,0,0},{0,0,0},{0,0,0}};
    for(int i=0; i<a.length; i++){
        for(int j=0; j<a[i].length; j++){
            if(a[i][j]>9||a[i][j]<1){
                checkers=false;
                break;
            }
            else{
                col[a[i].length-j][i]=a[i][j];
            }
        }
    }
    String alia="";
    for(int i=0; i<col.length; i++){
        for(int j=1; j<=col[i].length; j++){
            alia=a[i].toString();
            if(alia.contains(""+j)){
                alia=col[i].toString();
                if(alia.contains(""+j)){}
                else{
                    checkers=false;
                }   
            }
            else{
                checkers=false;
            }
        }
    }
    return checkers;
}
0
user2425429

Vous pouvez vérifier si le sudoku est résolu de deux manières similaires:

  • Vérifiez si le numéro est unique dans chaque ligne, colonne et bloc. 

Une solution naïve consisterait à parcourir chaque carré et à vérifier si le nombre est unique dans la ligne, bloc de colonnes occupé par ce nombre.

Mais il y a un meilleur moyen.

  • Le sudoku est résolu si chaque ligne, colonne et bloc contient une permutation des nombres (1 à 9)

Cela nécessite seulement de vérifier chaque ligne, colonne et bloc, au lieu de le faire pour chaque nombre. Une implémentation simple consisterait à avoir un champ de bits de nombres de 1 à 9 et à les supprimer lorsque vous répétez les colonnes, les lignes et les blocs. Si vous essayez de supprimer un numéro manquant ou si le champ n'est pas vide à la fin, alors sudoku n'est pas correctement résolu.

0
this

Voici une version très concise de Swift, qui utilise uniquement un tableau d’Ints pour suivre les groupes de 9 nombres et qui effectue une itération sur le sudoku une fois. 

import UIKit

func check(_ sudoku:[[Int]]) -> Bool {

    var groups = Array(repeating: 0, count: 27)

    for x in 0...8 {
        for y in 0...8 {
            groups[x] += 1 << sudoku[x][y] // Column (group 0 - 8)
            groups[y + 9] += 1 << sudoku[x][y] // Row (group 9 - 17)
            groups[(x + y * 9) / 9 + 18] += 1 << sudoku[x][y] // Box (group 18 - 27)
        }
    }

    return groups.filter{ $0 != 1022 }.count == 0
}

let sudoku = [
    [7, 5, 1,  8, 4, 3,  9, 2, 6],
    [8, 9, 3,  6, 2, 5,  1, 7, 4],
    [6, 4, 2,  1, 7, 9,  5, 8, 3],
    [4, 2, 5,  3, 1, 6,  7, 9, 8],
    [1, 7, 6,  9, 8, 2,  3, 4, 5],
    [9, 3, 8,  7, 5, 4,  6, 1, 2],
    [3, 6, 4,  2, 9, 7,  8, 5, 1],
    [2, 8, 9,  5, 3, 1,  4, 6, 7],
    [5, 1, 7,  4, 6, 8,  2, 3, 9]
]

if check(sudoku) {
    print("Pass")
} else {
    print("Fail")
}
0
Nick Locking

Supposons que votre tableau va de 1 à n. 

Nous allons créer un tableau de vérification, le remplir et le vérifier. 

grid [0-(n-1)][0-(n-1)]; //this is the input grid
//each verification takes n^2 bits, so three verifications gives us 3n^2
boolean VArray (3*n*n) //make sure this is initialized to false


for i = 0 to n
 for j = 0 to n
  /*
   each coordinate consists of three parts
   row/col/box start pos, index offset, val offset 
  */

  //to validate rows
  VArray( (0)     + (j*n)                             + (grid[i][j]-1) ) = 1
  //to validate cols
  VArray( (n*n)   + (i*n)                             + (grid[i][j]-1) ) = 1
  //to validate boxes
  VArray( (2*n*n) + (3*(floor (i/3)*n)+ floor(j/3)*n) + (grid[i][j]-1) ) = 1
 next    
next

if every array value is true then the solution is correct. 

Je pense que cela fera l'affaire, même si je suis sûr d'avoir commis quelques erreurs stupides. J'aurais peut-être même complètement manqué le bateau. 

0
Bryan