数独の作成と推定

学生時代から数独が好きでした。 学校に行く途中で時間を空けるのに役立ちました(そして今、私は仕事に行く途中で遊んでいます)。 すぐに私の祖母は数独を手に入れることができましたが、問題は彼女が電子機器で遊ぶことができないことでした。 そのため、数独を印刷するというアイデアが思い浮かびました。



数独ジェネレーターの作成方法と、評価上異なる評価可能なオプションの数について説明します。









執筆時点ではマウスに最も近かったため、すべてをPascalに実装します。 ただし、すべてを他の言語で実装できます。



▍方法1。 アルファベットの置換。



最初に検討する方法は、元の数独の例の一部の数値を他の数値に置き換えることです。 初期バージョンでは、次の表を使用します。



テーブル写真








この方法の本質は、各数字を異なる数字と一致させ、それによって数独全体を全体として変更することです。



これを行うには、1〜9のインデックスを持つ1次元の行列を使用し、インデックスに従って最初からすべてのセルを埋めます。 次に、2つのランダムなセルを取得し、それらの値を互いに変更します。 この操作を奇数回実行すると、マトリックスを埋める初期バージョンを取得できないことに注意してください(証拠は読者に提供されます)。 疑いのある人のために、プログラムの最後に、100万個の数独行列が生成され、元の数独行列との一致が評価されます。



説明図








パスカルコード
タイプrec = record //置換アルファベット

num:整数の配列[1..9]。

コンストラクタCreate();

var i:整数;

始める

for i:= 1から9

num [i]:= i;

終わり;

procedure rand(); //アルファベットの2つの数字のランダムな交換

var i、j、k:整数;

始める

i:=ランダム(1.9);

j:= i;

while(i = j)do j:= random(1,9);

k:= num [i];

num [i]:= num [j];

num [j]:= k;

終わり;

procedure wr(); //画面にアルファベットを表示

var i:整数;

始める

for i:= 1から9

書き込み(num [i] + '');

writeln;

終わり;

終わり;



カウントすると、9になります! オプション。これは362,880通りの組み合わせです。



second 2番目の方法。 マトリックス順列。



これは、数年前にまだHabréにあった方法です。 詳細はこちらをご覧ください



そして、ここに簡単な絞り込みがあります:



図に示すように、トリプルで行/列を交換できます














奇数の順列が元のバージョンとの違いを保証することも注目に値します。



したがって、最初に数独マトリックスで行交換と列交換を実装します。 また、コードを簡素化するために、数字をアルファベット順に置き換える機能を挿入します(前述の方法)



パスカルコード
タイプ数独=レコード//数独テーブル

num:整数の配列[1..9]の配列[1..9]。

constructor Create(); // 1〜9の行を循環的にシフトして、テーブルが数独ルールに一致するようにする

始める

num [1] [1]:= 1; num [1] [2]:= 2; num [1] [3]:= 3; num [1] [4]:= 4; num [1] [5]:= 5; num [1] [6]:= 6; num [1] [7]:= 7; num [1] [8]:= 8; num [1] [9]:= 9;

num [2] [1]:= 4; num [2] [2]:= 5; num [2] [3]:= 6; num [2] [4]:= 7; num [2] [5]:= 8; num [2] [6]:= 9; num [2] [7]:= 1; num [2] [8]:= 2; num [2] [9]:= 3;

num [3] [1]:= 7; num [3] [2]:= 8; num [3] [3]:= 9; num [3] [4]:= 1; num [3] [5]:= 2; num [3] [6]:= 3; num [3] [7]:= 4; num [3] [8]:= 5; num [3] [9]:= 6;

num [4] [1]:= 2; num [4] [2]:= 3; num [4] [3]:= 4; num [4] [4]:= 5; num [4] [5]:= 6; num [4] [6]:= 7; num [4] [7]:= 8; num [4] [8]:= 9; num [4] [9]:= 1;

num [5] [1]:= 5; num [5] [2]:= 6; num [5] [3]:= 7; num [5] [4]:= 8; num [5] [5]:= 9; num [5] [6]:= 1; num [5] [7]:= 2; num [5] [8]:= 3; num [5] [9]:= 4;

num [6] [1]:= 8; num [6] [2]:= 9; num [6] [3]:= 1; num [6] [4]:= 2; num [6] [5]:= 3; num [6] [6]:= 4; num [6] [7]:= 5; num [6] [8]:= 6; num [6] [9]:= 7;

num [7] [1]:= 3; num [7] [2]:= 4; num [7] [3]:= 5; num [7] [4]:= 6; num [7] [5]:= 7; num [7] [6]:= 8; num [7] [7]:= 9; num [7] [8]:= 1; num [7] [9]:= 2;

num [8] [1]:= 6; num [8] [2]:= 7; num [8] [3]:= 8; num [8] [4]:= 9; num [8] [5]:= 1; num [8] [6]:= 2; num [8] [7]:= 3; num [8] [8]:= 4; num [8] [9]:= 5;

num [9] [1]:= 9; num [9] [2]:= 1; num [9] [3]:= 2; num [9] [4]:= 3; num [9] [5]:= 4; num [9] [6]:= 5; num [9] [7]:= 6; num [9] [8]:= 7; num [9] [9]:= 8;

終わり;

procedure change(alf:rec); //アルファベットの置換

var i、j:整数;

始める

for i:= 1から9

j:= 1から9まで

num [i] [j]:= alf.num [(num [i] [j])];

終わり;

procedure swip_row(); //行を交換

var i、j、k、r:整数;

始める

i:=ランダム(1.9);

j:= i;

while(j = i)do j:= random(3 *((i-1 + 3)div 3-1)+1,3 *((i-1 + 3)div 3-1)+3);

rの場合:= 1〜9 do

始める

k:= num [i] [r];

num [i] [r]:= num [j] [r];

num [j] [r]:= k;

終わり;

終わり;

procedure swip_line(); //列を交換します

var i、j、k、r:整数;

始める

i:=ランダム(1.9);

j:= i;

while(j = i)do j:= random(3 *((i-1 + 3)div 3-1)+1,3 *((i-1 + 3)div 3-1)+3);

rの場合:= 1〜9 do

始める

k:= num [r] [i];

num [r] [i]:= num [r] [j];

num [r] [j]:= k;

終わり;

終わり;

procedure wr(); //表示

var i、j:整数;

始める

for i:= 1から9

始める

j:= 1から9まで

write(num [i] [j]、 '');

writeln;

終わり;

writeln;

終わり;

終わり;



計算した結果、列/行のトリプルを1つだけ置換する場合、各トリプルでオプションの数を6倍増やすことができます(列ごとに6 * 6 * 6、行ごとに同じ数、合計46 656オプション)。



また、文字ごとに2つの数独を比較し、一致する/一致しない場合にtrue / falseを返す関数を個別に作成します。



パスカルコード
function compl(s1、s2:sudoku):boolean; // 2つの数独テーブルを比較

var i、j:整数; フラグ:ブール値;

始める

フラグ:= true;

for i:= 1から9

j:= 1から9まで

if(s1.num [i] [j] <> s2.num [i] [j])then flag:= false;

compl:=フラグ;

終わり;



var

a:rec;

nw、だった:数独。

i:整数;

t、n:整数;

cor:整数;

始める

//アルファベットと元のバージョンを置き換えた後の数独一致の確率の計算

cor:= 0;

tの場合:= 1から1,000,000 do

始める

nw:=新しい数独();

だった:=新しい数独();

a:= new rec();



//総論。 奇数の順列では、最初の状況は不可能です

// 6つの順列では、すべてのトリプルの列/行を混在させることができます

for i:= 1から7 do nw.swip_row(); // 6 * 3オプション

for i:= 1から7 do nw.swip_line(); // 6 * 3オプション

// 8ペアの順列の場合、9桁の任意の組み合わせを取得できます。

for i:= 1から9 do a.rand(); // 9! オプション

//オプションの推定数117 573 120



nw.change(a);

if(compl(nw、was))then cor:= cor + 1;

終わり;

writeln((cor / 1000000)* 100、 '%');

nw.wr();

終わり。



calculations計算の終わりに



結果の2つの数値を乗算できる理由を説明する価値があります。 文字列を置換するとき、数字を置換するときと同じオプションは得られません。そうでない場合、異なるアルファベットの置換が得られます(文字列の最後の置換を考えると、矛盾を見つけることができます)。



したがって、このアルゴリズムを使用すると、最大362,880 * 46,656または16,930,529,280のバリアントを作成できます。



この記事では、数独テーブルを使用したすべての変換(列/行のトリプルの置き換え、転置など)については説明していません。これは、数独のバリアントの数がさらに多いことを証明しています。



All Articles