Эта задача не об игре Змея.
Представьте, что 2-ая змея сформирована, рисуя горизонтальную линию длины n
. В целочисленных точках вдоль своего тела эта змея может вращать свое тело на 90 градусов. Если мы определим переднюю часть змеи как крайнюю левую для начала, вращение переместит заднюю часть змеи, а передняя часть останется на месте. Делая многократные повороты, он может создавать множество различных форм тела змеи.
правила
- Одна часть тела змеи не может перекрывать другую.
- Должна быть возможность достичь окончательной ориентации без перекрытия между собой частей тела змеи. Две точки, которые касаются, считаются перекрывающимися в этой задаче.
- Я считаю змею и ее оборотную сторону одинаковой формы.
задача
Что касается вращения, перемещения и зеркальной симметрии, какое общее количество различных форм тела змеи можно создать?
Пример поворота части тела змеи. Представьте, n=10
и змея находится в начальной ориентации по прямой линии. Теперь поверните на 4
90 градусов против часовой стрелки. Мы получаем змею , 4
чтобы 10
(хвост змеи) лежал вертикально и змеи от 0
к 4
лежащей горизонтально. Змея теперь имеет один прямой угол в своем теле.
Вот несколько примеров благодаря Мартину Бюттнеру.
Начнем с горизонтальной змеи.
Теперь мы вращаемся из положения 4.
Мы заканчиваем после поворота в этой ориентации.
Теперь давайте рассмотрим эту ориентацию другой змеи.
Теперь мы можем видеть нелегальный ход, когда во время поворота возникнет перекрытие.
Гол
Ваша оценка - самая большая, n
за которую ваш код может решить проблему менее чем за одну минуту на моем компьютере.
Когда вращение происходит, оно перемещает половину змеи вместе с ним. Нам нужно беспокоиться о том, может ли какая-либо из вращающихся частей перекрывать часть змеи во время вращения. Для простоты можно предположить, что змея имеет нулевую ширину. Вы можете вращать только в определенной точке змеи до 90 градусов по часовой стрелке против часовой стрелки. Ведь вы никогда не сможете полностью сложить змею пополам, так как это повлекло бы за собой два поворота в одной и той же точке в одном направлении.
Формы, которые не могут быть сделаны
Простой пример формы, которую невозможно сделать, - это заглавная T
. Более сложная версия
(Спасибо Харальду Ханче-Олсену за этот пример)
В этом примере все соседние горизонтальные линии разделены на 1, как и вертикальные. Таким образом, нет легального движения с этой позиции, и, поскольку проблема обратима, нет возможности добраться туда из начальной позиции.
Языки и библиотеки
Вы можете использовать любой язык со свободно доступным компилятором / интерпретатором и т. Д. для Linux и любых библиотек, которые также свободно доступны для Linux.
Моя машина Время будет работать на моей машине. Это стандартная установка Ubuntu на восьмиъядерный процессор AMD FX-8350. Это также означает, что мне нужно иметь возможность запустить ваш код. Как следствие, используйте только легкодоступное бесплатное программное обеспечение и, пожалуйста, включите подробные инструкции по компиляции и запуску вашего кода.
Ответы:
Python 3 - предварительная оценка: n = 11 (n = 13 с PyPy *)
Поскольку в течение первой недели не было ответов, вот пример на Python для поощрения конкуренции. Я попытался сделать его достаточно читабельным, чтобы можно было легко выявить недостатки и дать идеи для других ответов.
Подход
Код
(теперь с некоторыми doctests и утверждает после моей неправильной первой попытки)
Полученные результаты
На моей машине самая длинная змея, которую можно вычислить менее чем за 1 минуту, имеет длину 11 (или длину 13 с PyPy *). Это, очевидно, лишь предварительная оценка, пока мы не узнаем, какая официальная оценка у машины Лембика.
Для сравнения, вот результаты для первых нескольких значений n:
Пожалуйста, дайте мне знать, если какой-либо из них окажется неправильным.
Если у вас есть пример договоренности, которую нельзя развернуть, вы можете использовать эту функцию,
neighbours(snake)
чтобы найти любые договоренности, достижимые за один шаг, в качестве теста кода.snake
является набором направлений соединения (0 для часовой стрелки, 1 для прямой, 2 для против часовой стрелки). Например (1,1,1) - прямая змея длиной 4 (с 3 суставами).Визуализация
Чтобы визуализировать змею, которую вы имеете в виду, или любую змею, возвращенную
neighbours
вами, вы можете использовать функциюdisplay(snake)
. Он принимает кортеж, как и другие функции, но, поскольку он не используется основной программой и, следовательно, не требует быстрой работы, он также примет строку для вашего удобства.display((1,1,2,0))
эквивалентноdisplay("1120")
Как упоминает Лембик в комментариях, мои результаты идентичны OEIS A037245, который не учитывает пересечения во время вращения. Это связано с тем, что для первых нескольких значений n нет разницы - все фигуры, которые не пересекаются, могут быть получены путем складывания прямой змеи. Корректность кода можно проверить, позвонив
neighbours()
со змеей, которую нельзя развернуть без пересечения. Поскольку у него нет соседей, он будет вносить вклад только в последовательность OEIS, а не в эту. Самый маленький пример я знаю это длина 31 змея Lembik упоминалось, благодаря David K :(1,1,1,1,2,1,2,1,1,1,1,1,1,2,1,1,1,2,1,1,2,2,1,0,1,0,1,1,1,1)
display('111121211111121112112210101111')
дает следующий вывод:Я хотел бы услышать от любого, у кого есть более короткий пример без соседей. Я подозреваю, что самый короткий такой пример будет отмечать наименьшее n, для которого отличаются две последовательности.
* Обратите внимание, что каждое приращение n занимает примерно 3 раза больше, поэтому увеличение с n = 11 до n = 13 требует почти в 10 раз больше времени. Вот почему PyPy позволяет увеличивать n только на 2, даже если он работает значительно быстрее, чем стандартный интерпретатор Python.
источник
C ++ 11 - почти работает :)
Прочитав эту статью , я собрал кусочки мудрости у того парня, который, по-видимому, 25 лет работал над менее сложной проблемой подсчета самоуничтожающихся путей на квадратной решетке.
Сборка исполняемого файла
Скомпилируйте с использованием MinGW под Win7 с g ++ 4.8 для сборок "linux", поэтому переносимость не гарантируется на 100%.
g++ -O3 -std=c++11
Это также работает (вроде) со стандартным проектом MSVC2013
Отменив определение
NDEBUG
, вы получаете следы выполнения алгоритма и сводку найденных конфигураций.Выступления
с хеш-таблицами или без них компилятор Microsoft работает ужасно: сборка g ++ происходит в 3 раза быстрее .
Алгоритм практически не использует памяти.
Поскольку проверка столкновений примерно в O (n), время вычислений должно быть в O (nk n ), где k немного меньше 3.
На моем i3-2100@3.1 ГГц n = 17 занимает около 1:30 (около 2 миллионов) змеи / мин).
Я не закончил оптимизацию, но я не ожидал бы большего, чем прирост в 3 раза, поэтому в принципе я могу надеяться достичь n = 20 за час или n = 24 за день.
Достижение первой известной несгибаемой формы (n = 31) займет от нескольких лет до десятилетия, при условии отсутствия перебоев в электроснабжении.
Подсчет фигур
N размер змея N-1 суставы.
Каждый сустав может быть оставлен прямым или согнутым влево или вправо (3 варианта).
Таким образом, число возможных складываний составляет 3 N-1 .
Столкновения несколько уменьшат это число, поэтому фактическое число близко к 2,7 N-1
Однако многие такие складки приводят к одинаковым формам.
две формы идентичны, если есть вращение или симметрия, которая может преобразовать одну в другую.
Давайте определим сегмент как любую прямую часть сложенного тела.
Например, змея размера 5, сложенная во 2-м суставе, будет иметь 2 сегмента (один длиной 2 единицы, а второй длиной 3 единицы).
Первый сегмент будет назван голова , а последний хвост .
По договоренности мы ориентируем голову змей горизонтально, направив тело вправо (как на первом рисунке ОП).
Мы обозначаем данную фигуру списком длин сегментов со знаком, причем положительные длины указывают на сворачивание справа, а отрицательные - на сворачивание слева.
Начальная длина положительна по соглашению.
Разделение сегментов и изгибов
Если мы рассмотрим только различные способы, которыми змея длины N может быть разбита на сегменты, мы получим перераспределение, идентичное композициям N.
Используя тот же алгоритм, который показан на вики-странице, легко сгенерировать все 2 N-1 возможных раздела змеи.
Каждая перегородка, в свою очередь, генерирует все возможные сгибы, применяя левый или правый изгибы ко всем своим соединениям. Одно такое складывание будет называться конфигурацией .
Все возможные разделы могут быть представлены целым числом из N-1 битов, где каждый бит представляет наличие соединения. Мы назовем это целое число генератором .
Обрезка перегородок
Заметив, что сгибание данного раздела от головы вниз эквивалентно сгибанию симметричного раздела от хвоста вверх, мы можем найти все пары симметричных разделов и устранить одно из двух.
Генератор симметричного раздела - это генератор раздела, записанный в обратном битовом порядке, который легко и дешево обнаружить.
Это исключит почти половину возможных разделов, за исключением разделов с «палиндромными» генераторами, которые остаются неизменными при обращении битов (например, 00100100).
Забота о горизонтальной симметрии
С нашими соглашениями (змея начинает указывать вправо), самый первый изгиб, примененный справа, создаст семейство сгибов, которое будет горизонтальной симметрией от тех, которые отличаются только первым сгибом.
Если мы решим, что первый изгиб всегда будет направо, мы устраняем все горизонтальные симметрии одним большим ударом.
Зачистка палиндромов
Эти два сокращения эффективны, но их недостаточно, чтобы позаботиться об этих надоедливых палиндромах.
Наиболее тщательная проверка в общем случае выглядит следующим образом:
рассмотрим конфигурацию C с палиндромной перегородкой.
Мы могли бы проверить каждую новую конфигурацию против 3 других. Однако, поскольку мы уже генерируем только конфигурации, начиная с правого поворота, у нас есть только одна возможная симметрия для проверки:
Это единственная конфигурация, которую мы можем дублировать.
Устранение дубликатов без хранения
Мой первоначальный подход состоял в том, чтобы хранить все конфигурации в огромной хеш-таблице, чтобы исключить дубликаты путем проверки наличия ранее вычисленной симметричной конфигурации.
Благодаря вышеупомянутой статье стало ясно, что, поскольку разделы и свертки хранятся в виде битовых полей, их можно сравнивать как любое числовое значение.
Таким образом, чтобы исключить одного члена из симметричной пары, вы можете просто сравнить оба элемента и систематически оставить самый маленький (или самый большой, как вам нравится).
Таким образом, тестирование конфигурации на дублирование равнозначно вычислению симметричного раздела, а если оба идентичных, - свертывание. Память не нужна вообще.
Порядок генерации
Очевидно, что проверка столкновений будет наиболее трудоемкой частью, поэтому сокращение этих вычислений значительно экономит время.
Возможное решение состоит в том, чтобы иметь «змею Рэгдолла», которая будет начинаться в плоской конфигурации и постепенно изгибаться, чтобы избежать пересчета всей геометрии змеи для каждой возможной конфигурации.
Выбирая порядок, в котором тестируются конфигурации, так что для каждого общего количества соединений может храниться не более ragdoll, мы можем ограничить количество экземпляров N-1.
Я использую рекурсивное сканирование сакэ с хвоста вниз, добавляя по одному суставу на каждом уровне. Таким образом, новый экземпляр ragdoll создается поверх родительской конфигурации с одним дополнительным изгибом.
Это означает, что изгибы применяются в последовательном порядке, что, по-видимому, достаточно, чтобы избежать самопроизвольных столкновений почти во всех случаях.
Когда обнаруживается самостолкновение, изгибы, которые приводят к нарушающему ходу, применяются во всех возможных порядках, пока не будет найдено законное сворачивание или пока не исчерпаны все комбинации.
Статическая проверка
Прежде чем думать о движущихся деталях, я обнаружил, что эффективнее проверить статическую окончательную форму змеи на самопересечение.
Это делается путем рисования змеи на сетке. Каждая возможная точка изображена с головы вниз. Если есть самопересечение, по крайней мере, пара точек попадет в одно и то же место. Это требует ровно N графиков для любой конфигурации змеи в течение постоянного O (N) времени.
Основным преимуществом этого подхода является то, что один только статический тест просто выберет действительные пути самообращения на квадратной решетке, что позволяет протестировать весь алгоритм, запретив динамическое обнаружение столкновений и убедившись, что мы находим правильное количество таких путей.
Динамическая проверка
Когда змея складывается вокруг одного сустава, каждый повернутый сегмент охватывает область, форма которой совсем не тривиальна.
Очевидно, что вы можете проверять столкновения, проверяя включение во всех таких областях по отдельности. Глобальная проверка была бы более эффективной, но, учитывая сложность областей, о которой я не могу думать (за исключением, может быть, использования графического процессора для рисования всех областей и выполнения глобальной проверки попадания).
Так как статический тест заботится о начальной и конечной позиции каждого сегмента, нам просто нужно проверить пересечения с дугами, проходящими по каждому вращающемуся сегменту .
После интересной дискуссии с trichoplax и небольшим количеством JavaScript, чтобы разобраться, я разработал этот метод:
Чтобы попытаться изложить это в нескольких словах, если вы звоните
(источник: free.fr )
Для любого сегмента, который не содержит I , область развертки ограничена 2 дугами (и 2 сегментами, уже проверенными статической проверкой).
Если я попадаю в сегмент, дуга, пройденная мной, также должна быть принята во внимание.
Это означает, что мы можем проверить каждый неподвижный сегмент на каждом вращающемся сегменте с 2 или 3 пересечениями сегмент с дугой
Я использовал векторную геометрию, чтобы вообще избежать тригонометрических функций.
Векторные операции создают компактный и (относительно) читаемый код.
Пересечение сегмента с дугой требует вектора с плавающей запятой, но логика должна быть защищена от ошибок округления.
Я нашел это элегантное и эффективное решение в неясном сообщении на форуме. Интересно, почему это не так широко освещается.
Это работает?
Ингибирование динамического обнаружения столкновений приводит к правильному самодостаточному количеству путей до n = 19, так что я уверен, что глобальный макет работает.
Динамическое обнаружение столкновений дает согласованные результаты, хотя проверка изгибов в другом порядке отсутствует (на данный момент).
Как следствие, программа подсчитывает змей, которые могут быть согнуты от головы вниз (то есть со сложенными суставами в порядке увеличения расстояния от головы).
источник