it-swarm.com.de

Ein cooler Algorithmus, um ein Sudoku-Feld zu überprüfen?

Kennt jemand einen einfachen Algorithmus, um zu überprüfen, ob eine Sudoku-Konfiguration gültig ist? Der einfachste Algorithmus, den ich mir ausgedacht habe, ist (für eine Karte der Größe n) im 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

Aber ich bin mir ziemlich sicher, dass es eine bessere (im Sinne einer eleganteren) Lösung geben muss. Effizienz ist ziemlich unwichtig.

24
user18670

Sie müssen nach allen Einschränkungen von Sudoku suchen:

  • Überprüfen Sie die Summe in jeder Zeile
  • Überprüfen Sie die Summe in jeder Spalte
  • Überprüfen Sie die Summe der Kästchen
  • Überprüfen Sie in jeder Zeile nach doppelten Nummern
  • Überprüfen Sie in jeder Spalte nach doppelten Nummern
  • Überprüfen Sie, ob in jedem Feld doppelte Nummern vorhanden sind

das sind 6 Checks insgesamt .. mit einem Brute-Force-Ansatz. Eine mathematische Optimierung kann verwendet werden, wenn Sie die Größe der Platine kennen (z. B. 3x3 oder 9x9).

Edit : Erklärung für die Summeneinschränkung: Das Prüfen der Summe zuerst (und das Stoppen, wenn die Summe nicht 45 ist) ist viel schneller (und einfacher) als das Suchen nach Duplikaten. Es bietet eine einfache Möglichkeit, eine falsche Lösung zu verwerfen. 

23
Radu094

Peter Norvig hat einen großartigen Artikel zum Lösen von Sudoku-Rätseln (mit Python),

http://norvig.com/sudoku.html

Vielleicht ist es zu viel für das, was Sie tun möchten, aber es ist trotzdem eine gute Lektüre

24
daniel

Nur ein Gedanke: Müssen Sie nicht auch die Zahlen in jedem 3x3-Quadrat prüfen? 

Ich versuche herauszufinden, ob es möglich ist, die Zeilen- und Spaltenbedingungen zu erfüllen, ohne ein korrektes Sudoku zu haben

7
Luk

Aktivieren Sie jede Zeile, Spalte und jedes Kästchen so, dass sie die Nummern 1 bis 9 enthält und keine Duplikate enthält. Die meisten Antworten hier besprechen dies bereits.

Aber wie geht das effizient? Antwort: Verwenden Sie eine Schleife

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

Jede Zahl setzt ein Bit des Ergebnisses. Wenn alle 9 Zahlen eindeutig sind, werden die niedrigsten 9 Bits gesetzt. Der Test "Duplikate prüfen" ist also nur eine Prüfung, ob alle 9 Bits gesetzt sind. Dies entspricht dem Testergebnis == 511. Sie müssen 27 dieser Prüfungen durchführen. eine für jede Zeile, Spalte und Box.

7
SPWorley

Erstellen Sie ein Array von Booleans für jede Zeile, Spalte und jedes Quadrat. Der Index des Arrays stellt den Wert dar, der in dieser Zeile, Spalte oder im Quadrat platziert wurde. Mit anderen Worten, wenn Sie der zweiten Zeile, der ersten Spalte, eine 5 hinzufügen, würden Sie Zeilen [2] [5] zusammen mit den Spalten [1] [5] und Quadraten [4] [5] auf true setzen dass die Zeile, die Spalte und das Quadrat jetzt einen 5-Wert haben.

Unabhängig davon, wie Ihre ursprüngliche Platine dargestellt wird, kann dies eine einfache und sehr schnelle Methode sein, um sie auf Vollständigkeit und Richtigkeit zu prüfen. Nehmen Sie einfach die Zahlen in der Reihenfolge, in der sie auf der Tafel erscheinen, und beginnen Sie mit dem Aufbau dieser Datenstruktur. Wenn Sie Zahlen in die Tafel einfügen, wird daraus eine O(1) -Operation, um zu bestimmen, ob Werte in einer bestimmten Zeile, Spalte oder einem Quadrat dupliziert werden. (Sie sollten auch prüfen, ob jeder Wert eine legitime Zahl ist: Wenn Sie eine leere oder zu hohe Zahl erhalten, wissen Sie, dass die Tafel nicht vollständig ist.) Wenn Sie an das Ende der Tafel gelangen, werden Sie Ich weiß, dass alle Werte korrekt sind und keine weiteren Prüfungen erforderlich sind.

Jemand hat auch darauf hingewiesen, dass Sie dazu jede Form von Set verwenden können. Auf diese Weise angeordnete Arrays sind nur eine besonders leichte und performante Form eines Sets, das für einen kleinen, aufeinander folgenden festen Satz von Zahlen gut geeignet ist. Wenn Sie die Größe Ihres Boards kennen, können Sie auch die Bit-Maskierung wählen, aber das ist wahrscheinlich etwas zu langweilig, wenn man bedenkt, dass Effizienz für Sie nicht so groß ist.

4

Dies ist meine Lösung in Python. Ich bin froh zu sehen, dass es die bisher kürzeste ist :) 

Der 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  

Und die Ausführung:

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

Sie können alle Werte in einem Satz (Zeile, Spalte, Box) in eine Liste extrahieren, sortieren und dann mit '(1, 2, 3, 4, 5, 6, 7, 8, 9) vergleichen.

2
Svante

wenn die Summe und die Multiplikation einer Zeile/Spalte der rechten Zahl 45/362880 entspricht

2
user189317

Erstellen Sie Zellensätze, wobei jeder Satz 9 Zellen enthält, und erstellen Sie Sätze für vertikale Spalten, horizontale Zeilen und 3x3-Quadrate.

Dann können Sie für jede Zelle einfach die Sets identifizieren, zu denen sie gehört, und diese analysieren.

Ich habe das einmal für ein Klassenprojekt gemacht. Ich habe insgesamt 27 Sets verwendet, um jede Zeile, Spalte und Box darzustellen. Ich würde die Zahlen überprüfen, während ich sie zu jedem Satz hinzufügte (jede Platzierung einer Zahl bewirkt, dass die Zahl zu 3 Sätzen, einer Zeile, einer Spalte und einem Kästchen hinzugefügt wird), um sicherzustellen, dass der Benutzer nur die Ziffern eingibt. 9 Ein Satz kann nur dann gefüllt werden, wenn er ordnungsgemäß mit eindeutigen Ziffern gefüllt wurde. Wenn alle 27 Sätze gefüllt wurden, war das Rätsel gelöst. Das Einrichten der Zuordnungen von der Benutzeroberfläche zu den 27 Sätzen war ein wenig langweilig, machte jedoch den Rest der Logik zu einem Kinderspiel.

2
Bill the Lizard

Vor einiger Zeit habe ich einen Sudoku-Checker geschrieben, der in jeder Zeile nach doppelten Nummern, in jeder Spalte und in jeder Box nach doppelten Nummern sucht. Ich würde es lieben, wenn jemand mit ein paar Zeilen Linq-Code auftauchen könnte.

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

Es wäre sehr interessant zu prüfen, ob:

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

das genügt den Regeln eines Sudokus. Denn das würde einen Algorithmus von O (n ^ 2) erlauben, die korrekten Zellen summieren und multiplizieren.

Betrachten wir n = 9, sollten die Summen 45 und die Produkte 362880 betragen.

Sie würden so etwas tun:

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

Hier ist ein schöner lesbarer Ansatz in 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))         

Jedes 3x3-Quadrat wird als Block bezeichnet, und es gibt 9 davon in einem 3x3-Gitter. Es wird angenommen, dass das Puzzle als Listenliste eingegeben wird, wobei jede innere Liste eine Reihe ist.

1
bjrnt

Zuerst müssten Sie ein boolesches "richtig" machen. Machen Sie dann wie zuvor beschrieben eine for-Schleife. Der Code für die Schleife und alles danach (in Java) ist wie angegeben, wobei field ein 2D-Array mit gleichen Seiten ist, col ein anderes mit den gleichen Abmessungen und l ein 1D-Array ist:

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];
        }
    }
}

Ich kenne den genauen Algorithmus für das Markieren der 3x3-Boxen nicht, aber Sie sollten alle Zeilen in Feld und Spalte mit "/*array name goes here*/[i].contains(1)&&/*array name goes here*/[i].contains(2)" (bis zum Erreichen der Zeilenlänge) in einer anderen for -Schleife markieren.

1
user2425429
 def Lösung (Board): 
 für i in Board: 
 wenn Summe (i)! = 45: 
 Rückgabe "Falsch" 
 
 für i in Bereich (9): 
 temp2 = [] 
 für x in Bereich (9): 
 temp2.append (board [i] [x] ) 
 
 wenn Summe (temp2)! = 45: 
 "Falsch" zurückgeben 
 
 "Korrekt" 
 
 board = [] 
 für i in Bereich (9): 
 inp = raw_input () 
 temp = [int (i) für i in inp] 
 board.append (temp) 
 
 Drucklösung (board) 
 
0

Hier ist meins in C. Nur jedes Feld einmal passieren.

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

Hier ist ein Artikel von Mathe-Professor J. F. Crook: Ein Bleistift-und-Papier-Algorithmus zum Lösen von Sudoku-Rätseln

Dieses Papier wurde im April 2009 veröffentlicht und erhielt als endgültige Sudoku-Lösung viel Publizität (siehe Google für "J.F.Crook Sudoku").

Neben dem Algorithmus gibt es auch einen mathematischen Beweis dafür, dass der Algorithmus funktioniert (Professor gab zu, dass er Sudoku nicht sehr interessant finde, deshalb legte er etwas Mathe in Papier, um mehr Spaß zu haben).

0
zendar

Eine kleinere Optimierung, die Sie vornehmen können, besteht darin, dass Sie in O(n) Zeit eher nach Duplikaten in einer Zeile, Spalte oder Box suchen als nach O (n ^ 2): wenn Sie die Menge von Zahlen durchlaufen , fügen Sie jedes zu einem Hashset hinzu. Abhängig von der Sprache können Sie tatsächlich ein echtes Hashset verwenden, dh konstante Zeitsuche und Einfügung. Dann können Sie im selben Schritt nach Duplikaten suchen, indem Sie prüfen, ob das Einfügen erfolgreich war oder nicht. Es ist eine geringfügige Verbesserung des Codes, aber von O (n ^ 2) zu O(n) zu gehen, ist eine signifikante Optimierung.

0
IceMetalPunk

Ich würde eine Schnittstelle schreiben, die Funktionen enthält, die das Sudoku-Feld empfangen und true/false zurückgibt, wenn es sich um eine Lösung handelt. Implementieren Sie dann die Einschränkungen als einzelne Validierungsklassen pro Einschränkung.

Um zu überprüfen, iterieren Sie einfach alle Constraint-Klassen und wenn alle bestanden sind, ist das Sudoku korrekt. Um das zu beschleunigen, setzen Sie diejenigen, die am wahrscheinlichsten ausfallen, in den Vordergrund und stoppen Sie im ersten Ergebnis, das auf ein ungültiges Feld verweist.

Ziemlich generisches Muster. ;-)

Sie können dies natürlich verbessern, um Hinweise darauf zu geben, welches Feld vermutlich falsch ist und so weiter.

Erste Einschränkung, einfach prüfen, ob alle Felder ausgefüllt sind. (Einfache Schleife) Zweitens prüfen, ob sich alle Zahlen in jedem Block befinden (verschachtelte Schleifen) Dritte Prüfung auf vollständige Zeilen und Spalten (fast dasselbe Verfahren wie oben, jedoch unterschiedliches Zugriffsschema).

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

Ohne gründlich zu prüfen, sollte dies von Kopf bis Fuß funktionieren (mit etwas Debugging), während nur zweimal geschlungen wird. O (n ^ 2) anstelle von O (3 (n ^ 2))

0
Josh Smeaton

Nehmen wir an, int sudoku [0..8,0..8] ist das Sudoku-Feld.

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.

Folgendes habe ich gerade dafür getan:

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

Sie können auf folgende zwei Arten prüfen, ob Sudoku gelöst ist:

  • Prüfen Sie, ob die Nummer in jeder Zeile, Spalte und jedem Block eindeutig ist. 

Eine naive Lösung wäre, durch jedes Quadrat zu iterieren und zu prüfen, ob die Nummer in der Zeile, Spalte, die diese Nummer belegt, eindeutig ist.

Aber es gibt einen besseren Weg.

  • Sudoku wird gelöst, wenn jede Zeile, Spalte und Block eine Permutation der Zahlen enthält (1 bis 9)

Dazu müssen Sie nur jede Zeile, jede Spalte und jeden Block überprüfen, anstatt dies für jede Zahl zu tun. Eine einfache Implementierung wäre, ein Bitfeld aus Zahlen von 1 bis 9 zu haben und es zu entfernen, wenn Sie die Spalten, Zeilen und Blöcke durchlaufen. Wenn Sie versuchen, eine fehlende Nummer zu entfernen, oder wenn das Feld nach dem Beenden nicht leer ist, wird Sudoku nicht richtig gelöst.

0
this

Hier ist eine sehr prägnante Version in Swift, die nur ein Array von Ints verwendet, um die Gruppen von 9 Zahlen zu verfolgen, und nur einmal das Sudoku durchläuft. 

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

Nehmen wir an, Ihr Board geht von 1 bis n. 

Wir erstellen ein Verifikationsarray, füllen es und verifizieren es dann. 

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. 

Ich denke, das wird den Trick tun, obwohl ich mir sicher bin, dass ich da ein paar dumme Fehler gemacht habe. Ich hätte das Boot vielleicht sogar ganz vermisst. 

0
Bryan