Wie viele Sudoku-Rätsel gibt es?


10

Dies ist weder ein Sudoku-Löser noch ein Sudoku-Prüfer.

Ihre Herausforderung besteht darin, eine Funktion oder ein Skript zu schreiben, die / das als Eingabe die "Block" -Größe eines 2D-Sudoku-Puzzles (3 für das klassische 9x9-Board , 4 für ein 16x16-Board usw.) eine Annäherung der Zahl berechnet von verschiedenen Rätseln (Lösungen), die für diese Größe existieren.

Bei Eingabe 3 sollte Ihr Programm beispielsweise mit der gewünschten Genauigkeit eine Annäherung an die Zahl 6.670.903.752.021.072.936.960 drucken, die der bekannten Anzahl unterschiedlicher 9x9-Sudoku-Rätsel entspricht , oder an 5.472.730.538, wenn die verschiedenen Symmetrien berücksichtigt werden. In Ihrer Lösung sollte angegeben werden, ob Symmetrien gezählt oder ignoriert werden.

"Gewünschte Genauigkeit" bleibt undefiniert: Ihr Programm wird möglicherweise für eine bestimmte Zeit ausgeführt und gibt dann das Ergebnis aus oder berechnet es bis zu einer bestimmten Anzahl von signifikanten Stellen oder wird sogar für immer ausgeführt, wobei immer bessere Näherungswerte gedruckt werden. Der Punkt ist, dass es möglich sein sollte, das Ergebnis in einer endlichen Zeit mit jeder erforderlichen Genauigkeit zu berechnen. ("42" ist also keine akzeptable Antwort.) Es ist akzeptabel, die Genauigkeit Ihres Ergebnisses auf die verfügbaren Maschinenschwimmer zu beschränken.

Kein Zugriff auf Online-Ressourcen, kein Speichern des Quellcodes im Dateinamen usw.


PS: Ich weiß, dass dies ein schwieriges Problem ist (NP-vollständig, wenn ich mich nicht irre). Diese Frage verlangt jedoch nur eine ungefähre statistische Lösung. Sie können beispielsweise zufällige Konfigurationen ausprobieren, die eine (oder besser zwei) Einschränkungen erfüllen, berechnen, wie viele davon vorhanden sind, und dann überprüfen, wie oft Sie ein Puzzle erhalten, das alle drei Einschränkungen erfüllt. Dies funktioniert in angemessener Zeit für kleine Größen (sicherlich für Größe = 3 und möglicherweise 4), aber der Algorithmus sollte generisch genug sein, um für jede Größe zu funktionieren.

Der beste Algorithmus gewinnt.


PS2: Ich habe von Code-Golf zu Code-Challenge gewechselt, um die Schwierigkeit des Problems besser widerzuspiegeln und intelligentere Lösungen zu fördern, anstatt dumme, aber gut Golf-fähige. Aber da der "beste Algorithmus" anscheinend unklar ist, möchte ich versuchen, ihn richtig zu definieren.

Welche Lösung würde bei genügend Zeit und ohne Berücksichtigung konstanter Faktoren (einschließlich CPU- und Interpreter-Geschwindigkeit) oder gleichwertig unter Berücksichtigung ihres asymptotischen Verhaltens am schnellsten zum exakten Ergebnis konvergieren?


11
Ist das nicht eigentlich ein wirklich hartes Problem ? Fragen Sie nur nach dem kürzesten Weg, um eine Funktion zur Erzeugung der Zahlen {1, 1, 288, 6e21} zu erzeugen, oder erweitern Sie diese irgendwie auf n> 3?
Algorithmushai

Die genaue Lösung ist ein unglaublich schwieriges Problem, aber eine Annäherung kann mit einigen Zufallsstichproben und einigen Sekunden moderner CPU-Zeit berechnet werden. Natürlich sind intelligentere Lösungen willkommen!
Tobia

2
@Tobia Dieser Ansatz wurde verwendet, um die ungefähre Anzahl der Rubik-Würfelpositionen zu ermitteln, für die N Bewegungen erforderlich sind, um kociemba.org/cube.htm zu lösen, sodass auf diese Weise eine Annäherung möglich ist. Wenn ich jedoch ein Programm schreibe, das jede Zeile löst und dann testet, ob die Spalten und Quadrate gelöst sind, hat es (9!) ^ 9 = 1E50 Möglichkeiten zur Bruteforce, von denen nur 6E21 Treffer sind (gemäß der Frage) .) Es werden durchschnittlich 1.6E28 Versuche pro Treffer benötigt. Das ist ziemlich langsam. Wenn ich jetzt sicherstellen könnte, dass sowohl Zeilen als auch Spalten korrekt sind und nur Quadrate überprüfen, würde ich irgendwohin gelangen. Ah! Ich habe eine Idee ...
Level River St

@steveverrill Sehen Sie? :-)
Tobia

Gibt es keine analytische Lösung?
Newbrict

Antworten:


3

C ++

Was ich hier vorstellen werde, ist ein Algorithmus, der anhand eines Beispiels für einen 3x3-Fall veranschaulicht wird. Es könnte theoretisch auf den NxN-Fall ausgedehnt werden, aber das würde einen viel leistungsstärkeren Computer und / oder einige geniale Verbesserungen erfordern. Ich werde im Laufe der Zeit einige Verbesserungen erwähnen.

Bevor wir fortfahren, wollen wir die Symmetrien des Sudoku-Gitters beachten, dh die Transformationen, die auf triviale Weise zu einem anderen Gitter führen. Für Blockgröße 3 sind die Symmetrien wie folgt:

Horizontale Symmetrie

**The N=3 sudoku is said to consist of 3 "bands" of 3 "rows" each**
permute the three bands: 3! permutations = 6
permute the rows in each band: 3 bands, 3! permutations each =(3!)^3=216

Vertikale Symmetrie

**The N=3 sudoku is said to consist of 3 "stacks" of 3 "columns" each.**
the count is the same as for horizontal.

Beachten Sie, dass horizontale und vertikale Reflexionen des Gitters durch eine Kombination dieser erreicht werden können, sodass sie nicht gezählt werden müssen. Es ist noch eine räumliche Symmetrie zu berücksichtigen, nämlich die Transponierung, die ein Faktor von ist 2. Dies ergibt die gesamte räumliche Symmetrie von

2*(N!*(N!)^N)^2 = 2*(6*216)^2=3359232 spatial symmetries for the case N=3.

Dann gibt es noch eine andere, sehr wichtige Symmetrie, die als Relabelling bezeichnet wird.

Relabelling gives a further (N^2)!=9!=362880 symmetries for the case N=3. So the total 
number of symmetries is 362880*3359232=1218998108160.

Die Gesamtzahl der Lösungen kann nicht einfach durch Multiplizieren der Anzahl der symmetriespezifischen Lösungen mit dieser Anzahl ermittelt werden, da es eine Anzahl (weniger als 1%) automorpher Lösungen gibt. Das bedeutet, dass es für diese speziellen Lösungen eine Symmetrieoperation gibt, die sie sich selbst zuordnet, oder mehrere Symmetrieoperationen, die sie derselben anderen Lösung zuordnen.

Um die Anzahl der Lösungen abzuschätzen, gehe ich das Problem in vier Schritten an:

1.Füllen Sie ein Array r[362880][12]mit allen möglichen Permutationen der Zahlen 0 bis 8. (Dies ist Programmierung und es ist in C, daher werden wir nicht 1 bis 9 verwenden.) Wenn Sie klug sind, werden Sie feststellen, dass der zweite Index ist 12 nicht 9. Dies liegt daran, dass wir dabei drei weitere ganze Zahlen berechnen, r[9,10,11] == 1<<a | 1<<b | 1<<cwobei sich 9,10,11 auf den ersten, zweiten und dritten Stapel beziehen , wobei wir berücksichtigen, dass dies eine "Zeile" ist und a, b, c sind die drei Zahlen, die in jedem Stapel für diese Zeile vorhanden sind.

2.Füllen Sie ein Array bmit allen möglichen Lösungen eines Bandes aus 3 Zeilen. Um dies relativ klein zu halten, schließen Sie nur die Lösungen ein, bei denen die oberste Zeile 012.345.678 ist. Ich tue dies mit brutaler Gewalt, durch alle möglichen mittleren Reihen und ANDing Erzeugung r[0][10,11,12]mit r[i][10,11,12]. Jeder positive Wert bedeutet, dass sich zwei identische Zahlen auf demselben Quadrat befinden und das Band ungültig ist. Wenn es eine gültige Kombination für die ersten beiden Zeilen gibt, suche ich die dritte (untere) Zeile mit derselben Technik.

Ich habe das Array als b [2000000] [9] dimensioniert, aber das Programm findet nur 1306368 Lösungen. Ich wusste nicht, wie viele es waren, also habe ich die Array-Dimension so verlassen. Dies ist eigentlich nur die Hälfte der möglichen Lösungen für ein einzelnes Band (verifiziert auf Wikipedia), da ich nur die 3. Zeile vom aktuellen Wert nach ioben scanne. Die verbleibende Hälfte der Lösungen kann trivial durch Austausch der 2. und 3. Reihe gefunden werden.

Die Art und Weise, wie die Informationen im Array gespeichert werden, bist zunächst etwas verwirrend. Anstatt jede Ganzzahl zum Speichern der 0..8an einer bestimmten Position gefundenen Zahlen zu verwenden , berücksichtigt hier jede Ganzzahl eine der Zahlen 0..8und gibt an, in welchen Spalten sie gefunden werden kann. Dies b[x][7]==100100001würde darauf hinweisen, dass für Lösung x die Zahl 7 in den Spalten 0,5 und 8 (von rechts nach links) gefunden wird. Der Grund für diese Darstellung ist, dass wir den Rest der Möglichkeiten für das Band durch erneutes Etikettieren generieren müssen, und dies Darstellung macht es bequem, dies zu tun.

Die beiden obigen Schritte umfassen das Setup und dauern ungefähr eine Minute (möglicherweise weniger, wenn ich die unnötige Datenausgabe entfernt habe. Die beiden folgenden Schritte sind die eigentliche Suche.)

3 Suchen Sie nach dem Zufallsprinzip nach Lösungen für die ersten beiden Bänder, die nicht zusammenstoßen (dh nicht zweimal dieselbe Nummer in einer bestimmten Spalte haben. Wir wählen eine Zufallslösung für Band 1 unter der Annahme, dass immer die Permutation 0 gilt, und eine Zufallslösung für Band 2 mit eine zufällige Permutation. Ein Ergebnis wird normalerweise in weniger als 9999 Versuchen gefunden (Trefferquote der ersten Stufe im Tausenderbereich) und dauert einen Bruchteil einer Sekunde. Mit Permutation meine ich, dass wir für das zweite Band eine Lösung aus b [] nehmen. [] wobei die erste Zeile immer 012.345.678 ist und neu beschriftet wird, so dass jede mögliche Folge von Zahlen in der ersten Zeile möglich ist.

4 Wenn in Schritt 3 ein Treffer gefunden wird, suchen Sie nach einer Lösung für das dritte Band, die nicht mit den beiden anderen zusammenstößt. Wir möchten nicht nur einen Versuch machen, da sonst die Verarbeitungszeit für Schritt 3 verschwendet würde. Auf der anderen Seite wollen wir uns nicht übermäßig anstrengen.

Nur zum Spaß habe ich es letzte Nacht so dumm wie möglich gemacht, aber es war immer noch interessant (weil es ewig nichts gab und dann eine große Anzahl von Lösungen in Bursts gefunden hat). Es hat die ganze Nacht gedauert, um einen Datenpunkt zu bekommen, selbst mit dem kleinen Hack (!z)Ich habe die letzte kSchleife abgebrochen, sobald wir wissen, dass dies keine gültige Lösung ist (wodurch sie fast neunmal schneller ausgeführt wird). Es wurden 1186585 Lösungen für das gesamte Raster gefunden, nachdem alle 362880-Neuetiketten aller 1306368 kanonischen Lösungen nach der letzten durchsucht wurden Block, insgesamt 474054819840 Möglichkeiten. Das ist eine Trefferquote von 1 zu 400000 für die zweite Stufe. Ich werde es bald erneut mit einer zufälligen Suche und nicht mit einem Scan versuchen. Es sollte in nur wenigen Millionen Versuchen eine vernünftige Antwort geben, was nur wenige Sekunden dauern sollte.

Die Gesamtantwort sollte (362880 * (1306368 * 2)) ^ 3 * Trefferquote = 8,5E35 * Trefferquote sein. Wenn ich aus der Zahl in der Frage zurückrechne, erwarte ich eine Trefferquote von 1 / 1,2E14. Was ich bisher mit meinem einzelnen Datenpunkt erreicht habe, ist 1 / (400000 * 1000), was einem Faktor von ungefähr einer Million entspricht. Dies kann eine Zufallsanomalie, ein Fehler in meinem Programm oder ein Fehler in meiner Mathematik sein. Ich werde nicht wissen, was es ist, bis ich ein paar weitere Tests durchführe.

Ich werde das heute Nacht hier lassen. Der Text ist ein bisschen kratzig, ich werde ihn bald aufräumen und hoffentlich weitere Ergebnisse hinzufügen und vielleicht ein paar Worte darüber, wie man ihn schneller macht und wie man das Konzept auf N = 4 erweitert. Ich glaube aber nicht, dass ich noch zu viele Änderungen an meinem Programm vornehmen werde :-)

Ah .. das Programm:

#include "stdafx.h"
#define _CRT_RAND_S
#include <algorithm>  
#include <time.h>

unsigned int n[] = { 0,1,2,3,4,5,6,7,8 }, r[362880][12], b[2000000][9],i,j,k,l,u,v,w,x,y,z;

int main () {

  //Run through all possible permutations of n[] and load them into r[][] 
  i=0;  
  do {
      r[i][9] = r[i][10] = r[i][11]=0;
      for (l = 0; l < 9; l++){
          r[i][l] = n[l];
          r[i][9 + l / 3] |= 1 << n[l];
      }
      if((i+1)%5040==0) printf("%d%d%d %d%d%d %d%d%d %o %o %o %o \n"
          ,r[i][0],r[i][1],r[i][2],r[i][3],r[i][4],r[i][5],r[i][6],r[i][7],r[i][8],r[i][9],r[i][10],r[i][11],r[i][9]+r[i][10]+r[i][11]);
      i++;
  } while ( std::next_permutation(n,n+9) );

  //Initialise b[][]
  for (l = 0; l<2000000; l++) for (k = 0; k<9; k++) b[l][k]=0;
  //fill b[][] with all solutions of the first band, where row0 ={0,1,2,3,4,5,6,7,8} and row1<row2 
  l=0;
  for (i = 0; i<362880; i++) 
  if (!(r[0][9] & r[i][9] | r[0][10] & r[i][10] | r[0][11] & r[i][11])){printf("%d %d \n",i,l);
     for (j=i; j<362880;j++) 
       if(!(r[0][9]&r[j][9] | r[0][10]&r[j][10] | r[0][11]&r[j][11] | r[j][9]&r[i][9] | r[j][10]&r[i][10] | r[j][11]&r[i][11] )){
           for (k = 0; k < 9; k++){
               b[l][r[0][k]]|=1<<k;
               b[l][r[i][k]]|=1<<k;
               b[l][r[j][k]]|=1<<k;
            } 
            l++;
       }
//        printf("%d%d%d %d%d%d %d%d%d %o %o %o %o \n"
//        ,r[i][0],r[i][1],r[i][2],r[i][3],r[i][4],r[i][5],r[i][6],r[i][7],r[i][8],r[i][9],r[i][10],r[i][11],r[i][9]+r[i][10]+r[i][11]);
//        printf("%d%d%d %d%d%d %d%d%d %o %o %o %o \n"
//        ,r[j][0],r[j][1],r[j][2],r[j][3],r[j][4],r[j][5],r[j][6],r[j][7],r[j][8],r[j][9],r[j][10],r[j][11],r[j][9]+r[j][10]+r[j][11]);
//        printf("%d %d %o %o %o %o %o %o %o %o %o \n",i,l,b[l][0],b[l][1],b[l][2],b[l][3],b[l][4],b[l][5],b[l][6],b[l][7],b[l][8]);
  }

  // find a random solution for the first 2 bands
  l=0;
  do{
      rand_s(&u); u /= INT_MIN / -653184; //1st band selection
      rand_s(&v); v /= INT_MIN / -181440; //2nd band permutation
      rand_s(&w); w /= INT_MIN / -653184; //2nd band selection
      z = 0;
      for (k = 0; k < 9; k++) z |= b[u][k] & b[w][r[v][k]];
      l++;
  } while (z);
  printf("finished random after %d tries \n",l);
  printf("found solution with top band %d permutation 0, and middle band %d permutation %d \n",u,w,v);
  getchar();

  // scan all possibilities for the last band
  l=0;
  for (i = 0; i < 362880; i++) for (j = 0; j < 1306368; j++){
              z=0;
              for(k=0;(k<9)&&(!z);k++) z|= b[u][k] & b[j][r[i][k]] | b[j][r[i][k]] & b[w][r[v][k]];
              if (!z){ l++; printf("solution %d : i= %d j=%d",l,i,j); }
  }
  printf("finished bottom band scan at %d millisec \n", clock()); getchar();
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.