1つの数字に着目して、3×3のブロックごとに配置できる可能性を考えていくと
9*6*3*6*4*2*3*2*1 = 46656通りある。
まずは事前にそれをすべて書き出すことにする。
(過去にやったのでそのまま流用)
touch-sp.hatenablog.com
036147258 036147285 036147528 036147582 ・ ・ ・
46656個の文字列(9文字)から1文字目、2文字目、3文字目・・・とすべて重複しないようにランダムに9個抽出することが今回のストラテジーである。
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace ConsoleApp1 { class Program { static void Main(string[] args) { List<string> mylist = new List<string>(); using (System.IO.StreamReader file = new System.IO.StreamReader("resource.txt")) { string line = ""; // test.txtを1行ずつ読み込んでいき、末端(何もない行)までwhile文で繰り返す while ((line = file.ReadLine()) != null) { mylist.Add(line); } } string[] b = new string[9]; int[] kazu = new int[81]; int[] start_position = { 0, 3, 6, 27, 30, 33, 54, 57, 60 }; int[] next_position = { 0, 1, 2, 9, 10, 11, 18, 19, 20 }; List<string> result = new List<string>(); Stopwatch sw = new Stopwatch(); sw.Start(); for (int mycount = 0; mycount < 40000; mycount++) { string[] a = new string[9]; System.Random r = new System.Random(); int rnd = r.Next(46656); a[0] = mylist[rnd]; List<string> mylist2 = mylist.Where(c => hantei(a[0],c)).ToList(); System.Random r2 = new System.Random(); rnd = r2.Next(mylist2.Count()); a[1] = mylist2[rnd]; List<string> mylist3 = mylist2.Where(c => hantei(a[1], c)).ToList(); System.Random r3 = new System.Random(); rnd = r3.Next(mylist3.Count()); a[2] = mylist3[rnd]; List<string> mylist4 = mylist3.Where(c => hantei(a[2], c)).ToList(); b = Try(a, mylist4); for (int i = 0; i < 9; i++) { for (int ii = 0; ii < 9; ii++) { int position = start_position[ii] + next_position[(int)char.GetNumericValue(b[i][ii])]; kazu[position] = i + 1; } } string sdData = string.Join("", kazu); result.Add(sdData); } System.IO.File.WriteAllLines(@"sudoku_answer.txt", result); sw.Stop(); Console.WriteLine(sw.Elapsed.ToString()); Console.WriteLine("finish..."); Console.ReadLine(); } static bool hantei(string x, string y) { for(int i = 0; i < 9; i++) { if (x[i] == y[i]) { return false; } } return true; } static string[] Try(string[] a, List<string> mylist) { //0(空欄)を探す→存在しない場合には-1を返す int blank = Array.IndexOf(a, null); if (blank != -1) { var ary = Enumerable.Range(0, mylist.Count()).ToArray(); int[] ary2 = ary.OrderBy(t => Guid.NewGuid()).ToArray(); for (int i = 0; i < mylist.Count(); i++) { if (isOK(blank, mylist[ary2[i]], a)) { a[blank] = mylist[ary2[i]]; Try(a, mylist); int blank2 = Array.IndexOf(a, null); if (blank2 == -1) { break; } a[blank] = null; } } } return a; } static bool isOK(int x, string L, string[] a) { for(int i = 0; i < x; i++) { for (int indx = 0; indx < 9; indx++) { if (L[indx] == a[i][indx]) { return false; } } } return true; } } }
- まず一つ目をランダムに選択
- その後選択できるリストを絞り込む
- 二つ目をランダムに選択
- 再度選択できるリストを絞り込む
- 3つ目をランダムに選択
- 再再度選択できるリストを絞り込む
- 残りの6つはバックトラック法で選択
- 最初に戻り同様のことを繰り返す
ただし、通常のバックトラック法ではすべての解を見つけてしまうため、ここでは1つの解が見つかった時点で多重ループを抜けるように工夫してある。
以下の環境で40000個作るのに14分かかった。速いのか遅いのか?
Windows 10 Pro Visual Studio 2017 Intel(R) Core(TM) i7-4600U CPU @2.10GHz 2.69GHz RAM:8.0GB