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<<c
wobei 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 b
mit 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 i
oben 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, b
ist zunächst etwas verwirrend. Anstatt jede Ganzzahl zum Speichern der 0..8
an einer bestimmten Position gefundenen Zahlen zu verwenden , berücksichtigt hier jede Ganzzahl eine der Zahlen 0..8
und gibt an, in welchen Spalten sie gefunden werden kann. Dies b[x][7]==100100001
wü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 k
Schleife 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();
}