Скільки головоломок Судоку існує?


10

Це не розв'язувач судоку, ані перевірка судоку.

Ваше завдання полягає в тому, щоб написати функцію або сценарій, який, заданий як введення, розмір "блоку" двовимірної головоломки судоку (що становить 3 для класичної дошки 9x9 , 4 для дошки 16x16 тощо), обчислить приблизне число. різних головоломок (рішень), що існують для цього розміру.

Наприклад, задавши вхід 3, ваша програма повинна надрукувати наближення, до потрібної точності, число 6,670,903,752,021,072,936,960, що є відомою кількістю чітких головоломок 9x9 судоку , або 5,472,730,538 при врахуванні різних симетрій. У вашому рішенні повинно бути вказано, чи вважаються симетрики чи ігноруються

"Бажана точність" залишається невизначеною: ваша програма може працювати протягом певного часу, а потім виводити її результат, або обчислювати її до заданої кількості значущих цифр, або навіть запускати назавжди, друкуючи кращі та кращі наближення. Справа в тому, що слід обмежити час обчислення результату до необхідної точності. (Отже, "42" не є прийнятною відповіддю.) Обмеження точності результату на доступних поплавках машини є прийнятним.

Ні доступу до інтернет-ресурсів, ні зберігання вихідного коду у назві файлу тощо.


PS: Я знаю, що це складна проблема (якщо я не помиляюся.), Але це питання вимагає лише приблизного, статистичного рішення. Наприклад, ви можете спробувати випадкові конфігурації, які задовольняють одне (або краще два) обмеження, обчислити, скільки існує, а потім перевірити, наскільки часто ви отримуєте головоломку, яка задовольняє всі три обмеження. Це буде спрацьовувати у пристойний час для невеликих розмірів (звичайно, для розміру = 3 і, можливо, 4), але алгоритм повинен бути загальним для роботи для будь-якого розміру.

Виграє найкращий алгоритм.


PS2: Я перейшов з кодового гольфу на виклик коду, щоб краще відобразити складність проблеми та заохочувати розумніші рішення над німими, але добре гольфними. Але оскільки, мабуть, "найкращий алгоритм" незрозумілий, дозвольте мені спробувати його правильно визначити.

Враховуючи достатньо часу та нехтуючи постійними факторами (включаючи швидкість процесора та інтепретер), або рівнозначно, враховуючи їх асимптотичну поведінку, яке рішення найшвидше сходиться до точного результату?


11
Це насправді не дуже складна проблема ? Ви просто запитуєте про найкоротший спосіб створити функцію для отримання чисел {1, 1, 288, 6e21} або якось поширити це на n> 3?
Алгоритм

Точне рішення є надзвичайно важкою проблемою, але наближення можна обчислити деяким випадковим вибіркою та кількома секундами сучасного процесорного часу. Звичайно, розумніші рішення вітаються!
Тобія

2
@Tobia цей підхід був використаний для пошуку приблизно кількості кубових позицій рубіка, що вимагає N ходів, щоб вирішити kociemba.org/cube.htm, так що можна отримати наближення таким чином. Однак, якщо я напишу програму, яка змушує вирішувати кожен рядок, а потім перевіряти, чи вирішено стовпці та квадрати, у неї буде (9!) ^ 9 = 1E50 можливостей для грубої сили, з яких лише 6E21 - хіти (за запитання .) В середньому знадобиться 1.6E28 спроб за кожен удар. Це досить повільно. Тепер, якби я міг переконатись, що і рядки, і крапки є правильними і перевіряють лише квадрати, я б кудись дістався. Ах! У мене є ідея ...
Level River St

@steveverrill Дивіться? :-)
Tobia

Чи не існує аналітичного рішення?
Новак

Відповіді:


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. (це програмування, і це в С, тому ми не збираємось використовувати від 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();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.