스도쿠 퍼즐은 몇 개입니까?


10

이것은 스도쿠 솔버 나 스도쿠 체커가 아닙니다.

2D 스도쿠 퍼즐의 "블록"크기 ( 클래식 9x9 보드의 경우 3, 16x16 보드의 경우 4 등) 를 입력으로 주어진 함수 또는 스크립트를 작성 하여 숫자의 근사값을 계산해야합니다. 해당 크기에 대해 존재하는 별개의 퍼즐 (솔루션).

예를 들어, 입력 3이 주어지면 프로그램은 6,670,903,752,021,072,936,960의 숫자를 대략 9x9 Sudoku 퍼즐알려진 숫자 또는 다양한 대칭을 고려할 때 5,472,730,538의 근사값을 인쇄해야합니다. 솔루션에 대칭 계산 여부를 명시해야합니다.

"원하는 정밀도"는 정의되지 않은 상태로 유지됩니다. 프로그램이 주어진 시간 동안 실행 된 다음 결과를 출력하거나 주어진 유효 자릿수까지 계산하거나 영원히 실행되어 더 나은 근사값을 인쇄 할 수 있습니다. 요점은 한정된 시간에 필요한 정밀도로 결과를 계산할 수 있어야한다는 것입니다. (따라서 "42"는 용인되지 않습니다.) 결과의 정밀도를 사용 가능한 기계 수레로 제한하는 것은 허용됩니다.

온라인 리소스에 액세스 할 수 없으며 파일 이름에 소스 코드를 저장하지 않는 등


추신 : 나는 이것이 어려운 문제라는 것을 알고 있습니다 (실수하지 않으면 NP가 완성됩니다). 그러나이 질문은 대략적인 통계 솔루션을 요구합니다. 예를 들어, 하나 (또는 ​​더 나은 두 가지) 제약 조건을 만족시키는 임의의 구성을 시도하고 존재하는 수를 계산 한 다음 세 가지 제약 조건을 모두 만족하는 퍼즐을 얼마나 자주 얻을 수 있는지 확인할 수 있습니다. 이것은 작은 크기 (확실히 크기 = 3 및 가능하면 4)에서는 적절한 시간에 작동하지만 알고리즘은 모든 크기에서 작동하기에 충분히 일반적이어야합니다.

최고의 알고리즘이 이깁니다.


PS2 : 문제의 난이도를 더 잘 반영하고 멍청하지만 골치 아픈 솔루션보다 더 똑똑한 솔루션을 장려하기 위해 코드 골프에서 코드 도전으로 변경했습니다. 그러나 분명히 "최상의 알고리즘"은 명확하지 않으므로 제대로 정의 해 보겠습니다.

충분한 시간이 주어지고 일정한 요소 (CPU 및 정수 속도 포함)를 무시하거나 무의식적 인 행동을 고려할 때 어느 솔루션이 가장 빠른 결과에 가장 적합한 솔루션입니까?


11
이 사실 아닌가요 정말 어려운 문제 ? {1, 1, 288, 6e21} 숫자를 생성하는 함수를 생성하는 가장 짧은 방법을 요청하거나 어떻게하면 이것을 n> 3으로 확장합니까?
algorithmshark

정확한 솔루션은 매우 어려운 문제이지만, 임의의 샘플링과 몇 초의 최신 CPU 시간으로 근사치를 계산할 수 있습니다. 물론 더 똑똑한 솔루션도 환영합니다!
Tobia

2
@Tobia이 접근법은 kociemba.org/cube.htm 을 풀기 위해 N 이동이 필요한 대략적인 루빅스 큐브 위치를 찾는 데 사용 되었으므로 이런 식으로 근사치를 얻을 수 있습니다. 그러나 모든 행을 해결하는 프로그램을 작성한 다음 열과 사각형이 해결되는지 테스트하면 (9!) ^ 9 = 1E50 무차별 대입 가능성이 있습니다. .) 평균 적중 당 1.6E28 회 시도해야합니다. 꽤 느립니다. 이제 행과 열이 모두 정확하고 정사각형 만 검사 할 수 있다면 어딘가에있을 것입니다. 아! 아이디어가 있습니다 ...
Level River St

@steveverrill 참조? :-)
Tobia

분석 솔루션이 없습니까?
Newbrict

답변:


3

C ++

여기에 제시 할 것은 3x3 사례에 대한 예제와 함께 설명 된 알고리즘입니다. 이론적으로 NxN 사례로 확장 될 수 있지만 훨씬 더 강력한 컴퓨터 및 / 또는 독창적 인 조정이 필요합니다. 내가 진행하면서 몇 가지 개선 사항을 언급 할 것입니다.

더 나아 가기 전에 스도쿠 그리드의 대칭, 즉 사소한 방식으로 다른 그리드로 이어지는 변형에 주목하십시오 . 블록 크기 3의 경우 대칭은 다음과 같습니다.

수평 대칭

**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

수직 대칭

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

그리드의 수평 및 수직 반사는 이들의 조합에 의해 달성 될 수 있으므로, 계산 될 필요가 없다는 것에 유의한다. 고려해야 할 공간 대칭이 하나 더 있습니다 2. 이것은 전체 공간 대칭을 제공합니다

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

그런 다음 레이블 변경이라는 또 다른 매우 중요한 대칭이 있습니다.

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

다형성 솔루션의 수가 (1 % 미만) 존재하기 때문에 대칭 고유 솔루션의 수에이 숫자를 곱하여 총 솔루션 수를 간단히 찾을 수 없습니다. 즉, 이러한 특수 솔루션의 경우 해당 작업을 자체에 매핑하는 대칭 작업 또는 동일한 다른 솔루션에 매핑하는 여러 대칭 작업이 있습니다.

솔루션 수를 추정하기 위해 4 단계로 문제에 접근합니다.

1. r[362880][12]숫자 0에서 8까지 가능한 모든 순열을 가진 배열 을 채우십시오 (이것은 프로그래밍이며 C에 있으므로 1에서 9까지는 사용하지 않을 것입니다). 이것은 12가 아닌 9입니다. 이것은 이것을 "행"으로 간주 할 것이라는 점을 염두에두고 있기 때문에 r[9,10,11] == 1<<a | 1<<b | 1<<c9,10,11은 1, 2, 3 스택을 참조하는 3 개의 정수를 더 계산하기 때문입니다 . a, b, c는 해당 행의 각 스택에 존재하는 3 개의 숫자입니다.

2. b3 행 밴드의 가능한 모든 솔루션으로 어레이 를 채 웁니다 . 이것을 합리적으로 작게 유지하려면 맨 위 행이 012,345,678 인 솔루션 만 포함하십시오. 가능한 모든 중간 행을 생성하고 AND를 r[0][10,11,12]사용 하여 무차별 강제 로이 작업 을 수행 r[i][10,11,12]합니다. 양수 값은 동일한 사각형에 두 개의 동일한 숫자가 있고 밴드가 유효하지 않음을 의미합니다. 처음 두 행에 유효한 조합이 있으면 동일한 기술로 세 번째 (아래) 행을 검색합니다.

배열의 크기를 b [2000000] [9]로 측정했지만 프로그램은 1306368 솔루션 만 찾습니다. 나는 얼마나 많은지 알지 못했기 때문에 배열 차원을 그대로 두었습니다. 이것은 실제로 단일 밴드 (wikipedia에서 확인)에 가능한 솔루션의 절반에 불과합니다. 왜냐하면 현재 값에서 3 번째 행만 i위쪽으로 스캔하기 때문 입니다. 솔루션의 나머지 절반은 두 번째와 세 번째 행을 교환하여 사소하게 찾을 수 있습니다.

정보가 배열에 저장되는 방식은 b처음에는 약간 혼란 스럽습니다. 0..8주어진 위치에서 찾은 숫자를 저장하기 위해 각 정수를 사용하는 대신, 각 정수는 숫자 중 하나를 고려 0..8하여 찾을 수있는 열을 나타냅니다. 따라서 b[x][7]==100100001솔루션 x의 경우 숫자 7은 0, 5, 8 열 (오른쪽에서 왼쪽으로)에 있음을 나타냅니다. 표현은 이것을하기에 편리합니다.

위의 두 단계는 설정을 구성하고 약 1 분이 걸립니다 (불필요한 데이터 출력을 제거하면 더 적을 수 있습니다. 아래의 두 단계는 실제 검색입니다).

3 충돌하지 않는 처음 두 밴드에 대한 솔루션을 무작위로 검색합니다 (예 : 주어진 열에 같은 수를 두 번 갖지 않음) 항상 순열 0을 가정하고 밴드 1에 대한 랜덤 솔루션을 선택합니다 . 임의의 순열 결과는 일반적으로 9999 회 미만 (1 천 단계의 첫 단계 적중률)에서 발견되며 1 초의 1 분의 1을 차지합니다. [] 여기서 첫 번째 행은 항상 012,345,678이며 첫 번째 행에서 가능한 일련의 숫자가 가능하도록 레이블을 다시 지정하십시오.

4 3 단계에서 적중이 발견되면 다른 두 밴드와 충돌하지 않는 세 번째 밴드에 대한 해를 검색하십시오. 한 번만 시도하고 싶지 않으면 3 단계의 처리 시간이 낭비됩니다. 반면에 우리는 이것에 많은 노력을 기울이고 싶지 않습니다.

재미를 위해, 어젯밤에 나는 가능한 가장 멍청한 방법을 시도했지만 여전히 흥미 롭습니다 (연령에는 아무것도 없기 때문에 버스트에서 많은 수의 솔루션을 찾았습니다). 작은 해킹으로도 하나의 데이터 포인트를 얻으려면 밤새도록 이것이 유효한 솔루션이 아니라는 것을 알 자마자 (!z)마지막 k루프 를 중단했습니다 (거의 9 배 빠릅니다). 마지막 13013068 정식 솔루션의 모든 362880 레이블 변경을 검색 한 후 전체 그리드에 대한 1186585 솔루션을 찾았습니다. 블록, 총 474054819840 가능성. 그것은 두 번째 단계에서 400000에서 1의 적중률입니다. 스캔이 아닌 임의 검색으로 곧 다시 시도하겠습니다. 단 몇 백만 번의 시도만으로도 합리적인 답변을 제공해야하며 몇 초만 걸립니다.

전체 답변은 (362880 * (1306368 * 2)) ^ 3 * hit rate = 8.5E35 * hit rate 여야합니다. 질문의 숫자를 다시 계산하면 1 / 1.2E14의 적중률이 예상됩니다. 단일 데이터 포인트로 지금까지 얻은 것은 1 / (400000 * 1000)이며 약 백만 배입니다. 이것은 우연한 예외, 프로그램 오류 또는 수학 오류 일 수 있습니다. 몇 가지 테스트를 더 실행할 때까지는 무엇인지 알 수 없습니다.

오늘 밤 여기에 두겠습니다. 텍스트는 약간 까다 롭습니다. 곧 정리하고 더 많은 결과를 추가하고 더 빨리 만드는 방법과 개념을 N = 4로 확장하는 방법에 대한 몇 마디를 추가하겠습니다. 그래도 프로그램에 너무 많은 변화를 줄 것이라고 생각하지는 않습니다. :-)

아 .. 프로그램 :

#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();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.