Có bao nhiêu câu đố Sudoku tồn tại?


10

Đây không phải là người giải Sudoku, cũng không phải người kiểm tra Sudoku.

Thử thách của bạn là viết một hàm hoặc tập lệnh, được đưa vào làm đầu vào kích thước "khối" của câu đố Sudoku 2D (là 3 cho bảng 9x9 cổ điển , 4 cho bảng 16x16 , v.v.) sẽ tính toán xấp xỉ số các câu đố (giải pháp) riêng biệt tồn tại cho kích thước đó.

Ví dụ, với đầu vào 3, chương trình của bạn sẽ in một giá trị gần đúng, với độ chính xác mong muốn, của số 6.670,903,752,021,072,936,960 là số lượng câu đố Sudoku 9x9 khác nhau đã biết , hoặc 5,472,730,538 khi tính đến các đối xứng khác nhau. Giải pháp của bạn nên nêu rõ liệu các đối xứng được tính hay bỏ qua.

"Độ chính xác mong muốn" không được xác định: chương trình của bạn có thể chạy trong một thời gian nhất định và sau đó đưa ra kết quả của nó, hoặc tính toán nó với một số chữ số có nghĩa nhất định hoặc thậm chí chạy mãi mãi, in gần đúng hơn và tốt hơn. Vấn đề là có thể làm cho nó tính kết quả với bất kỳ độ chính xác cần thiết nào, trong một thời gian hữu hạn. (Vì vậy, "42" không phải là một câu trả lời chấp nhận được.) Hạn chế độ chính xác của kết quả của bạn đối với các phao máy có sẵn là chấp nhận được.

Không truy cập tài nguyên trực tuyến, không lưu trữ mã nguồn trong tên tệp, v.v.


PS: Tôi biết đây là một vấn đề khó khăn (NP-đầy đủ nếu tôi không nhầm.) Nhưng câu hỏi này chỉ yêu cầu một giải pháp thống kê gần đúng. Ví dụ: bạn có thể thử các cấu hình ngẫu nhiên thỏa mãn một (hoặc hai tốt hơn) các ràng buộc, tính toán có bao nhiêu trong số đó tồn tại và sau đó kiểm tra tần suất bạn nhận được một câu đố thỏa mãn cả ba ràng buộc. Điều này sẽ hoạt động trong một thời gian thích hợp cho các kích thước nhỏ (chắc chắn cho kích thước = 3 và có thể 4) nhưng thuật toán phải đủ chung để hoạt động cho mọi kích thước.

Thuật toán tốt nhất chiến thắng.


PS2: Tôi đã thay đổi từ golf-code sang thử thách code để phản ánh tốt hơn sự khó khăn của vấn đề và khuyến khích các giải pháp thông minh hơn, vượt qua những giải đấu câm nhưng tốt. Nhưng vì rõ ràng "thuật toán tốt nhất" không rõ ràng, hãy để tôi thử xác định nó đúng.

Cho đủ thời gian và bỏ qua các yếu tố không đổi (bao gồm cả CPU và tốc độ trong máy), hoặc tương đương, xem xét hành vi không triệu chứng của chúng, giải pháp nào sẽ hội tụ đến kết quả chính xác nhanh nhất?


11
Đó không phải là thực sự là một thực sự khó khăn vấn đề ? Bạn chỉ đang yêu cầu cách ngắn nhất để tạo ra một hàm để tạo ra các số {1, 1, 288, 6e21}, hoặc bằng cách nào đó mở rộng điều này thành n> 3?
thuật toán

Giải pháp chính xác là một vấn đề cực kỳ khó khăn, nhưng một phép tính gần đúng có thể được tính toán với một số mẫu ngẫu nhiên và một vài giây thời gian CPU hiện đại. Tất nhiên các giải pháp thông minh hơn được chào đón!
Tobia

2
@Tobia phương pháp này đã được sử dụng để tìm số lượng vị trí khối lập phương của rubik yêu cầu N di chuyển để giải quyết kociemba.org/cube.htmlm để có thể lấy xấp xỉ theo cách này. Tuy nhiên, nếu tôi viết một chương trình làm cho mọi hàng được giải quyết và sau đó kiểm tra xem các cột và hình vuông có được giải quyết hay không, nó sẽ có (9!) ^ 9 = 1E50 khả năng để bruteforce, trong đó chỉ có 6E21 là lần truy cập (cho mỗi câu hỏi .) Trung bình sẽ cần 1.6E28 lần thử cho mỗi lần truy cập. Điều đó khá chậm. Bây giờ, nếu tôi có thể đảm bảo cả hàng và cols đều chính xác và chỉ kiểm tra các hình vuông, tôi sẽ đến một nơi nào đó. Ah! Tôi có một ý tưởng ...
Level River St

@steveverrill Thấy chưa? :-)
Tobia

Không có một giải pháp phân tích?
Newbrict

Câu trả lời:


3

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<<ctrong đó 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 bvớ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 để itrở 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 bban đầ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..8và cho biết số cột nào có thể được tìm thấy. do đó b[x][7]==100100001sẽ 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ỏ kvò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();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.