C ++
Những gì tôi sẽ trình bày ở đây là một thuật toán, được minh họa bằng một ví dụ cho trường hợp 3x3. Về mặt lý thuyết nó có thể được mở rộng cho trường hợp NxN, nhưng điều đó sẽ cần một máy tính mạnh hơn nhiều và / hoặc một số điều chỉnh khéo léo. Tôi sẽ đề cập đến một số cải tiến khi tôi đi qua.
Trước khi đi xa hơn, hãy lưu ý các đối xứng của lưới Sudoku, tức là các phép biến đổi dẫn đến một lưới khác theo cách tầm thường. Đối với kích thước khối 3, các đối xứng như sau:
Đối xứng ngang
**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
Đối xứng dọc
**The N=3 sudoku is said to consist of 3 "stacks" of 3 "columns" each.**
the count is the same as for horizontal.
Lưu ý rằng sự phản xạ ngang và dọc của lưới có thể đạt được bằng cách kết hợp những thứ này, vì vậy chúng không cần phải tính. Có một đối xứng không gian nữa được xem xét, đó là chuyển vị, là một yếu tố của 2
. Điều này cho tổng đối xứng không gian của
2*(N!*(N!)^N)^2 = 2*(6*216)^2=3359232 spatial symmetries for the case N=3.
Sau đó, có một đối xứng khác, rất quan trọng, được gọi là tái định cư.
Relabelling gives a further (N^2)!=9!=362880 symmetries for the case N=3. So the total
number of symmetries is 362880*3359232=1218998108160.
Tổng số giải pháp không thể được tìm thấy đơn giản bằng cách nhân số lượng các giải pháp đối xứng duy nhất với số này, bởi vì có một số (ít hơn 1%) các giải pháp tự động. Điều đó có nghĩa là đối với các giải pháp đặc biệt này, có một hoạt động đối xứng ánh xạ chúng với chính chúng hoặc nhiều hoạt động đối xứng ánh xạ chúng đến cùng một giải pháp khác.
Để ước tính số lượng giải pháp, tôi tiếp cận vấn đề theo 4 bước:
1. Điền vào một mảng r[362880][12]
với tất cả các hoán vị có thể có của các số từ 0 đến 8. (đây là lập trình và nó nằm trong C, vì vậy chúng tôi sẽ không sử dụng 1 đến 9.) Nếu bạn thông minh, bạn sẽ nhận thấy rằng chỉ mục thứ hai là 12 chứ không phải 9. Điều này là do, trong khi thực hiện điều này, lưu ý rằng chúng ta sẽ coi đây là một "hàng", chúng tôi cũng tính toán thêm ba số nguyên r[9,10,11] == 1<<a | 1<<b | 1<<c
trong đó 9,10,11 đề cập đến ngăn xếp thứ nhất, thứ hai và thứ ba và a, b, c là ba số có trong mỗi ngăn xếp cho hàng đó.
2. Điền vào một mảng b
với tất cả các giải pháp có thể có của một dải gồm 3 hàng. Để giữ mức nhỏ này một cách hợp lý, chỉ bao gồm các giải pháp trong đó hàng trên cùng là 012.345.678. Tôi làm điều này bằng vũ lực, bằng cách tạo ra tất cả các hàng giữa có thể và ANDing r[0][10,11,12]
với r[i][10,11,12]
. Bất kỳ giá trị dương nào cũng có nghĩa là có hai số giống nhau trong cùng một hình vuông và băng tần không hợp lệ. Khi có sự kết hợp hợp lệ cho hai hàng đầu tiên, tôi tìm kiếm hàng thứ 3 (dưới cùng) với cùng một kỹ thuật.
Tôi đã định kích thước mảng là b [2000000] [9] nhưng chương trình chỉ tìm thấy 1306368 giải pháp. Tôi không biết có bao nhiêu, vì vậy tôi rời khỏi kích thước mảng như thế. Đây thực sự chỉ là một nửa các giải pháp khả thi cho một băng tần (được xác minh trên wikipedia), vì tôi chỉ quét hàng thứ 3 từ giá trị hiện tại để i
trở lên. Nửa còn lại của các giải pháp có thể được tìm thấy một cách tầm thường bằng cách trao đổi hàng thứ 2 và thứ 3.
Cách thông tin được lưu trữ trong mảng b
ban đầu hơi khó hiểu. thay vì sử dụng mỗi số nguyên để lưu trữ các số 0..8
được tìm thấy ở một vị trí nhất định, ở đây mỗi số nguyên xem xét một trong các số 0..8
và cho biết số cột nào có thể được tìm thấy. do đó b[x][7]==100100001
sẽ chỉ ra rằng đối với giải pháp x, số 7 được tìm thấy trong các cột 0,5 và 8 (từ phải sang trái.) Lý do cho biểu diễn này là chúng ta cần tạo ra các khả năng còn lại cho ban nhạc bằng cách tái định vị và điều này đại diện làm cho nó thuận tiện để làm điều này.
Hai bước trên bao gồm thiết lập và mất khoảng một phút (có thể ít hơn nếu tôi loại bỏ đầu ra dữ liệu không cần thiết. Hai bước dưới đây là tìm kiếm thực tế.)
3 Tìm kiếm ngẫu nhiên các giải pháp cho hai băng tần đầu tiên không xung đột (nghĩa là không có cùng một số hai lần trong một cột nhất định. Chúng tôi chọn một giải pháp ngẫu nhiên cho băng tần 1, giả sử luôn luôn hoán vị 0 và một giải pháp ngẫu nhiên cho băng tần 2 với một hoán vị ngẫu nhiên. Một kết quả thường được tìm thấy trong ít hơn 9999 lần thử (tỷ lệ trúng giai đoạn đầu tiên trong phạm vi hàng nghìn) và mất một phần của giây. Theo hoán vị, ý tôi là đối với băng thứ hai, chúng tôi lấy giải pháp từ b [] [] trong đó hàng đầu tiên luôn là 012.345.678 và đặt lại tên cho nó để có thể có bất kỳ chuỗi số nào trên hàng đầu tiên.
4 Khi tìm thấy lần truy cập trong bước 3, hãy tìm kiếm giải pháp cho băng thứ ba không đụng độ với hai nhóm kia. Chúng tôi không muốn thực hiện chỉ một lần thử, nếu không thời gian xử lý cho bước 3 sẽ bị lãng phí. Mặt khác, chúng tôi không muốn đặt một lượng lớn nỗ lực vào việc này.
Chỉ để cho vui, đêm qua tôi đã làm theo cách ngu ngốc nhất có thể, nhưng nó vẫn rất thú vị (vì không có gì từ lâu, sau đó tìm thấy một số lượng lớn các giải pháp trong các vụ nổ.) Phải mất cả đêm để có được một datapoint, ngay cả với một chút hack (!z)
Tôi đã hủy bỏ k
vòng lặp cuối cùng ngay khi chúng tôi biết đây không phải là một giải pháp hợp lệ (giúp nó chạy nhanh hơn gần 9 lần.) Nó đã tìm thấy 1186585 giải pháp cho lưới hoàn chỉnh sau khi tìm kiếm tất cả 362880 giải pháp cho tất cả 1306368 giải pháp chính khối, tổng cộng 474054819840 khả năng. Đó là tỷ lệ trúng 1 trên 400000 cho giai đoạn thứ hai. Tôi sẽ thử lại sớm với một tìm kiếm ngẫu nhiên thay vì quét. Nó sẽ đưa ra một câu trả lời hợp lý chỉ trong vài triệu lần thử, chỉ mất vài giây.
Câu trả lời tổng thể phải là (362880 * (1306368 * 2)) ^ 3 * tỷ lệ trúng = 8,5E35 * tỷ lệ trúng. Bằng cách tính lại từ số trong câu hỏi, tôi mong đợi tỷ lệ trúng là 1 / 1.2E14. Những gì tôi đã có cho đến nay với datapoint duy nhất của tôi là 1 / (400000 * 1000), tức là khoảng một triệu. Đây có thể là sự bất thường của cơ hội, lỗi trong chương trình của tôi hoặc lỗi trong toán học của tôi. Tôi sẽ không biết đó là gì cho đến khi tôi chạy thêm một vài thử nghiệm.
Tôi sẽ để nó ở đây cho tối nay. Văn bản có một chút sơ sài, tôi sẽ sớm thu dọn nó và hy vọng sẽ thêm một số kết quả, và có thể một vài từ về cách làm cho nó nhanh hơn và làm thế nào để mở rộng khái niệm lên N = 4. Tôi không nghĩ rằng tôi sẽ thực hiện quá nhiều thay đổi cho chương trình của mình, mặc dù :-)
À .. chương trình:
#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();
}