数独パズルはいくつありますか?


10

これは数独ソルバーでも数独チェッカーでもありません。

あなたの課題は、(3次元である数独パズルの「ブロック」サイズの入力として与えられ、その関数またはスクリプトを書くことである古典的な9x9のボードのために、4を16×16ボード、等)数の近似値を計算しますそのサイズで存在する明確なパズル(ソリューション)の。

たとえば、入力3が与えられた場合、プログラムは、6,670,903,752,021,072,936,960という異なる精度の9x9数独パズルの既知の数、またはさまざまな対称性を考慮に入れると5,472,730,538 の望ましい精度の近似値を出力する必要があります。ソリューションでは、対称性をカウントするか無視するかを指定する必要があります。

「必要な精度」は未定義のままです。プログラムは一定時間実行され、その結果を出力したり、指定された有効桁数まで計算したり、永久に実行されたりして、ますます適切な近似を出力します。重要なのは、有限の時間で、必要な精度で結果を計算できるようにする必要があるということです。(したがって、「42」は許容される回答ではありません。)結果の精度を使用可能なマシンのフロートに制限することは許容されます。

オンラインリソースにアクセスしたり、ソースコードをファイル名に保存したりすることはできません。


PS:私はこれが難しい問題であることを知っています(私が間違っていなければNP完全です)。しかし、この質問は、おおよその統計的解決策を求めているだけです。たとえば、1つ(または2つ以上)の制約を満たすランダムな構成を試して、それらの数を計算し、3つの制約すべてを満たすパズルを取得する頻度を確認できます。これは小さいサイズではまともな時間で動作しますが(確かにsize = 3、場合によっては4の場合)、アルゴリズムは任意のサイズで動作するように十分に汎用的である必要があります。

最良のアルゴリズムが優先されます。


PS2:私は、コードゴルフからコードチャレンジに変更して、問題の難易度をより適切に反映し、馬鹿でよくゴルフされたものよりもスマートなソリューションを奨励しました。しかし、明らかに「最適なアルゴリズム」は不明確なので、適切に定義してみましょう。

十分な時間を与え、一定の要素(CPUとインタプリタの速度を含む)を無視するか、または同等に、それらの漸近的な動作を考慮すると、どのソリューションが正確な結果に最も速く収束するでしょうか?


11
これは本当に 難しい 問題ではありませんか?数値{1、1、288、6e21}を生成する関数を生成するための最短の方法を求めているのですか、それとも何らかの形でこれをn> 3に拡張しているのですか?
algorithmhark

正確な解決策は信じられないほど難しい問題ですが、ランダムサンプリングと最新のCPU時間の数秒で近似を計算できます。もちろん、よりスマートなソリューションは大歓迎です!
トビア2014年

2
@Tobiaこのアプローチは、kociemba.org / cube.htmを解決するためにN回の移動を必要とするルービックキューブの位置のおよその数を見つけるために使用されたため、この方法で近似を取得することができます。ただし、すべての行を解決してから列と四角が解決されるかどうかをテストするプログラムを作成すると、ブルートフォースの可能性は(9!)^ 9 = 1E50になり、そのうち6E21だけがヒットします(質問ごとに) 。)平均で1ヒットあたり1.6E28の試行が必要です。それはかなり遅いです。さて、もし行と列の両方が正しいことを確認し、正方形のみをチェックすることができれば、私はどこかに到達するでしょう。ああ!私はアイデアを持っています...
Level River St

@steveverrillほら?:-)
トビア

分析ソリューションはありませんか?
Newbrict 2014年

回答:


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.

グリッドの水平反射と垂直反射は、これらの組み合わせによって実現できるため、カウントする必要がないことに注意してください。考慮すべきもう1つの空間対称性があります。これは転置であり、これはの係数です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.配列に0〜8のr[362880][12]すべての可能な順列を入力します(これはプログラミングであり、Cで記述されているため、1〜9は使用しません)。鋭敏な場合は、2番目の添え字がわかります。これは12ではなく9です。これは、これを「行」と見なすことを念頭に置いてr[9,10,11] == 1<<a | 1<<b | 1<<c、9、10、11が最初、2番目、3番目のスタックを参照する3つの整数も計算するためです。 a、b、cは、その行の各スタックに存在する3つの数値です。

2. b3行のバンドのすべての可能な解で配列を埋めます。これを適度に小さく保つには、一番上の行が012,345,678であるソリューションのみを含めます。私はこれを力ずくで、可能なすべての中間の行を生成し、とのAND r[0][10,11,12]をとることによってこれを行いますr[i][10,11,12]。正の値は、同じ正方形に2つの同じ数値があり、バンドが無効であることを意味します。最初の2行に有効な組み合わせがある場合は、3番目(下)の行を同じ手法で検索します。

配列のサイズをb [2000000] [9]に設定しましたが、プログラムは1306368の解しか見つけません。いくつあるかわからなかったので、配列の次元はそのままにしておきました。これは実際には、シングルバンド(ウィキペディアで確認済み)の可能なソリューションの半分にすぎません。これは、現在の値から3行目だけをi上向きにスキャンするためです。解の残りの半分は、2行目と3行目を交換することで簡単に見つけることができます。

情報が配列に格納される方法bは、最初は少し混乱します。各整数を使用0..8して特定の位置にある数値を格納する代わりに、ここで各整数は数値の1つを考慮し、0..8それがどの列にあるかを示します。したがってb[x][7]==100100001、解xの場合、列7が列0、5、8に(右から左に)見つかることを示します。この表現の理由は、ラベルを付け直してバンドの残りの可能性を生成する必要があるためです。表現はこれを行うのに便利です。

上記の2つのステップはセットアップを含み、約1分かかります(不要なデータ出力を削除した場合は、おそらく少なくなります。以下の2つのステップは実際の検索です)。

3衝突しない最初の2つのバンドの解をランダムに検索します(つまり、特定の列で同じ数が2回ない)。常に順列0と仮定して、バンド1のランダム解と、ランダムな順列。結果は通常9999回未満の試行で検出され(数千の範囲の最初のステージのヒット率)、1秒の数分の1です。順列によって、2番目のバンドについてはb []から解を取得することを意味します[]ここで、最初の行は常に012,345,678であり、最初の行の番号の可能なシーケンスが可能になるようにラベルを付け直します。

4ステップ3でヒットが見つかったら、他の2つと衝突しない3番目のバンドの解決策を検索します。1回だけ試行するのは望ましくありません。そうしないと、ステップ3の処理時間が無駄になります。一方、これに過度の労力を費やしたくない。

ちょうど楽しみのために、昨夜は可能な限り馬鹿げた方法でそれを行いましたが、それはまだ興味深いものでした(何年もの間何もなかったため、大量のソリューションが一気に見つかりました)。これが有効なソリューションではないことがわかったらすぐに(!z)最後のkループを中止しました(これにより、実行速度が約9倍速くなります)。ブロック、合計474054819840の可能性。これは、第2ステージのヒット率が400000分の1です。スキャンではなくランダム検索ですぐに再試行します。ほんの数秒で、数百万回の試行で妥当な答えが得られるはずです。

全体的な答えは、(362880 *(1306368 * 2))^ 3 *ヒット率= 8.5E35 *ヒット率になるはずです。質問の数値から逆算すると、ヒット率は1 / 1.2E14になると思います。私の単一のデータポイントでこれまでに得たものは1 /(400000 * 1000)で、これは約100万倍のアウトです。これは、偶然の異常、プログラムのエラー、または数学のエラーの可能性があります。あと何回かテストを実行するまで、それがどちらなのかわかりません。

これを今夜ここに置いておきます。テキストは少しスクラップです。すぐに片付けます。うまくいけば、さらに結果を追加します。また、より速くする方法と、概念を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.