Это не решатель судоку и не проверка судоку.
Ваша задача состоит в том, чтобы написать функцию или сценарий, который, учитывая в качестве входных данных размер блока «2D» головоломки Судоку (3 для классической доски 9x9 , 4 для доски 16x16 и т. Д.), Будет вычислять приблизительное число различных головоломок (решений), которые существуют для этого размера.
Например, с учетом ввода 3 ваша программа должна напечатать с требуемой точностью приблизительное число 6 670 903 752 021 072 936 960, которое является известным числом различных головоломок 9х9 Судоку , или 5 472 730 538 с учетом различных симметрий. Ваше решение должно указывать, учитываются ли симметрии или игнорируются.
«Требуемая точность» остается неопределенной: ваша программа может работать в течение определенного времени, а затем выводить свой результат или вычислять его до заданного числа значащих цифр, или даже работать вечно, печатая все более и более приближенные значения. Дело в том, что должна быть возможность заставить его вычислять результат с любой необходимой точностью за конечное время. (Таким образом, «42» не является приемлемым ответом.) Допускается ограничение точности вашего результата доступными числами с плавающей запятой машины.
Нет доступа к онлайн-ресурсам, нет сохранения исходного кода в имени файла и т. Д.
PS: я знаю, что это трудная проблема (если я не ошибаюсь, если вы не ошиблись), этот вопрос требует только приблизительного статистического решения. Например, вы можете попробовать случайные конфигурации, которые удовлетворяют одному (или лучше двум) ограничениям, вычислить, сколько из них существует, а затем проверить, как часто вы получаете головоломку, которая удовлетворяет всем трем ограничениям. Это будет работать в приличное время для небольших размеров (конечно, для размера = 3 и, возможно, 4), но алгоритм должен быть достаточно универсальным, чтобы работать для любого размера.
Лучший алгоритм побеждает.
PS2: я перешел с кода-гольфа на код-вызов, чтобы лучше отражать сложность проблемы и поощрять более разумные решения, а не тупые, но хорошо сыгранные в гольф. Но поскольку, по-видимому, «лучший алгоритм» неясен, позвольте мне попытаться определить его правильно.
Учитывая достаточное количество времени и игнорируя постоянные факторы (в том числе процессор и скорость интерпретатора) или, что то же самое, учитывая их асимптотическое поведение, какое решение приведет к точному результату быстрее всего?
источник
Ответы:
C ++
Здесь я приведу алгоритм, иллюстрированный примером для случая 3x3. Теоретически его можно распространить на случай NxN, но для этого потребуется гораздо более мощный компьютер и / или некоторые хитрые настройки. Я упомяну некоторые улучшения по мере прохождения.
Прежде чем идти дальше, давайте отметим симметрии сетки Судоку, то есть преобразования, которые приводят к другой сетке тривиальным образом. Для размера блока 3 симметрии следующие:
Горизонтальная симметрия
Вертикальная симметрия
Обратите внимание, что горизонтальные и вертикальные отражения сетки могут быть достигнуты с помощью их комбинации, поэтому их не нужно считать. Существует еще одна пространственная симметрия, которая должна быть рассмотрена, это транспонирование, которое является фактором
2
. Это дает общую пространственную симметриюТогда есть другая, очень важная симметрия, называемая релабелингом.
Общее количество решений не может быть найдено просто путем умножения числа уникальных по симметрии решений на это число, поскольку существует число (менее 1%) автоморфных решений. Это означает, что для этих специальных решений существует операция симметрии, которая отображает их на себя, или множественные операции симметрии, которые сопоставляют их с тем же другим решением.
Чтобы оценить количество решений, я подхожу к проблеме в 4 этапа:
1. Заполните массив
r[362880][12]
всеми возможными перестановками чисел от 0 до 8. (Это программирование, и это в C, поэтому мы не будем использовать от 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. Я делаю это с помощью грубой силы, генерируя все возможные средние строки и Андингr[0][10,11,12]
сr[i][10,11,12]
. Любое положительное значение означает, что в одном квадрате два одинаковых числа, а полоса неверна. Когда есть правильная комбинация для первых двух строк, я ищу 3-ю (нижнюю) строку тем же методом.Я измерил массив как b [2000000] [9], но программа находит только 1306368 решений. Я не знал, сколько их было, поэтому я оставил размер массива таким образом. На самом деле это только половина возможных решений для одной полосы (проверено в википедии), потому что я сканирую только 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 раз быстрее). Он нашел 1186585 решений для полной сетки после поиска всех 362880 релейных разрядов всех 1306368 канонических решений для последнего блок, всего 474054819840 возможностей. Это показатель успеха 1 на 400000 для второго этапа. Я скоро попробую снова со случайным поиском, а не сканированием. Он должен дать разумный ответ всего за несколько миллионов попыток, что должно занять всего несколько секунд.Общий ответ должен быть (362880 * (1306368 * 2)) ^ 3 * коэффициент попадания = 8.5E35 * коэффициент попадания. Возвращаясь к подсчету числа, о котором идет речь, я ожидаю, что показатель попаданий составит 1 / 1.2E14. То, что я до сих пор получил с моей единственной точкой данных, это 1 / (400000 * 1000), что примерно в миллион раз меньше. Это может быть случайная аномалия, ошибка в моей программе или ошибка в моей математике. Я не буду знать, что это такое, пока не проведу еще несколько тестов.
Я оставлю это здесь на сегодня. Текст немного неаккуратный, я скоро приведу его в порядок и, надеюсь, добавлю еще несколько результатов и, возможно, несколько слов о том, как сделать это быстрее и как расширить концепцию до N = 4. Я не думаю, что внесу в свою программу слишком много изменений :-)
Ах .. программа:
источник