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. (це програмування, і це в С, тому ми не збираємось використовувати від 1 до 9.) Якщо ви проникливі, ви помітите, що другий підпис це 12 не 9. Це тому, що, роблячи це, маючи на увазі, що ми будемо вважати це "рядком", ми також обчислюємо ще три цілі числа, r[9,10,11] == 1<<a | 1<<b | 1<<c
де 9,10,11 посилаються на перший, другий і третій стеки і a, b, c - це три числа, присутні в кожній стеці для цього рядка.
2.Заповніть масив b
усіма можливими рішеннями смуги з 3 рядів. Щоб це було досить мало, включайте лише ті рішення, де верхній ряд - 012,345,678. Я роблю це грубою силою, генеруючи всі можливі середні ряди та ANDing r[0][10,11,12]
з r[i][10,11,12]
. Будь-яке додатне значення означає, що в одному квадраті є два однакових числа, а смуга недійсна. Коли для перших двох рядків є дійсна комбінація, я шукаю 3-й (нижній) рядок тією ж технікою.
Я розмірив масив як b [2000000] [9], але програма знаходить лише 1306368 рішень. Я не знав, скільки їх було, тому я залишив такий вимір масиву. Це насправді лише половина можливих рішень для однієї смуги (перевірено у wikipedia), оскільки я сканую лише 3-й рядок із поточного значення на i
угору. Решту половини рішень можна знайти тривіально шляхом обміну 2-го та 3-го рядів.
Спосіб зберігання інформації в масиві b
спочатку трохи заплутаний. замість використання кожного цілого числа для зберігання чисел, 0..8
знайдених у заданій позиції, тут кожне ціле число розглядає одне з чисел 0..8
і вказує, у яких стовпцях його можна знайти. таким чином, b[x][7]==100100001
було б зазначено, що для рішення x число 7 знаходимо у стовпцях 0,5 та 8 (справа наліво.) Причиною цього подання є те, що нам потрібно генерувати решту можливостей для смуги шляхом повторного відліку, і це представлення робить це зручним для цього.
Два вище кроки містять налаштування і займають близько хвилини (можливо менше, якщо я видалив непотрібний вихід даних. Два кроки нижче - це фактичний пошук.)
3 Шукайте випадковим чином рішення для перших двох діапазонів, які не зіштовхуються (тобто не мають однакового числа двічі в заданому стовпчику. Ми вибираємо випадкове рішення для смуги 1, припускаючи завжди перестановку 0, і випадкове рішення для смуги 2 з випадкова перестановка. Результат зазвичай зустрічається менш ніж за 9999 спроб (швидкість потрапляння першої стадії в тисячному діапазоні) і займає частку секунди. Під перестановкою я маю на увазі, що для другої смуги ми приймаємо рішення з b [] [] де перший рядок завжди 012,345,678 і відновіть його так, щоб була можлива будь-яка можлива послідовність чисел у першому рядку.
4 Коли в кроці 3 знайдено хіт, знайдіть рішення для третьої смуги, яка не зіткнеться з двома іншими. Ми не хочемо робити лише одну спробу, інакше час обробки для кроку 3 буде витрачено даремно. З іншого боку, ми не хочемо докладати до цього непосильних зусиль.
Просто для розваги, вчора ввечері я це зробив найнеобхіднішим способом, але це було все-таки цікаво (тому що він нічого не старів, потім знайшов велику кількість рішень у сплесках.) Це знадобилось цілу ніч, щоб отримати одну точку даних, навіть з маленьким хаком (!z)
Я зробив скасування останнього k
циклу, як тільки ми знаємо, що це неправомірне рішення (завдяки чому воно працює майже в 9 разів швидше.) Після пошуку всіх репрезентацій 362880 всіх канонічних рішень 1306368 було знайдено 1186585 рішень для останнього блок, загалом 474054819840 можливостей. Це вражаючий показник 1 на 400000 для другого етапу. Невдовзі я спробую знову зі випадковим пошуком, а не скануванням. Він повинен дати розумну відповідь всього за кілька мільйонів спроб, що повинно зайняти лише кілька секунд.
Загальна відповідь повинна бути (362880 * (1306368 * 2)) ^ 3 * частота звернень = 8,5E35 * частота звернень. Зворотним підрахунком з числа у запитанні я очікую, що швидкість звернення дорівнює 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();
}