Напишите программу для построения двумерной схемы узла на основе структуры узла. Узел - это то, на что он похож: связанная веревка. В математике диаграмма узла показывает, где кусок веревки пересекает или под собой, чтобы сформировать узел. Некоторые примеры диаграмм узлов показаны ниже:
Есть разрыв в линии, где веревка пересекает себя.
Входные данные: входные данные, описывающие узел, являются массивом целых чисел. Узел, где верёвка пересекает себя n раз, может быть представлен как массив из n целых чисел, каждое из которых имеет значение в диапазоне [0, n-1]. Назовем этот массив K .
Чтобы получить массив, описывающий узел, нумеруйте каждый из сегментов от 0 до n-1. Сегмент 0 должен вести к сегменту 1, который должен вести к сегменту 2, который должен вести к сегменту 3 и так далее, пока сегмент n-1 не возвращается назад и не ведет к сегменту 0. Сегмент заканчивается, когда другой сегмент веревки пересекает его ( представлен разрывом в линии на диаграмме). Давайте возьмем самый простой узел - узел трилистника. После нумерации сегментов сегмент 0 заканчивается, когда сегмент 2 пересекает его; сегмент 1 заканчивается, когда сегмент 0 пересекает его; и сегмент 2 заканчивается, когда сегмент 1 пересекает его. Таким образом, массивом, описывающим узел, является [2, 0, 1]. В общем, сегмент x начинается там, где сегмент x-1 mod n остановлен, и заканчивается там, где сегмент K [x] пересекает его.
На рисунке ниже показаны диаграммы узлов с помеченными сегментами и соответствующими массивами, которые описывают узел.
Верхние три диаграммы - это настоящие узлы, в то время как нижние три - это петли веревки, которые пересекают себя, но на самом деле не связаны (но у которых все еще есть соответствующие коды).
Ваша задача - написать функцию, которая принимает массив целых чисел K (вы могли бы назвать его чем-то другим), который описывает узел (или петлю веревки, которая на самом деле не завязана) и который создает соответствующую диаграмму узлов, как описано выше. Примеры. Если вы можете, предоставьте версию вашего кода или объяснение, а также предоставьте примеры выходных данных вашего кода. Один и тот же узел часто может быть представлен несколькими различными способами, но если диаграмма узла, которой удовлетворяет ваша функция, содержит вход в качестве одного из возможных представлений, ваше решение является действительным.
Это код-гольф, поэтому выигрывает самый короткий код в байтах. Линия, представляющая веревку, может иметь толщину 1 пиксель, однако нижние и перемычки должны быть четко различимы (размер разрыва в веревке должен быть больше толщины веревки как минимум на пиксель с любой стороны) ,
Я буду приветствовать ответы, которые полагаются на встроенные возможности теории узлов, но тот, который выбран в конце, не может полагаться на встроенные возможности теории узлов.
Все, что я знаю о своей нотации: я считаю, что есть последовательности значений, которые, кажется, не соответствуют ни одному узлу или узлу. Например, последовательность [2, 3, 4, 0, 1] кажется невозможной для рисования.
Кроме того, предположим, что вы берете пересечение и, начиная с этого пересечения, следуйте по пути веревки в одном направлении и маркируйте каждое непомеченное пересечение, с которым вы сталкиваетесь, последовательно большими интегральными значениями. Для чередующихся узлов существует простой алгоритм преобразования моих обозначений в такую маркировку, а для чередующихся узлов это тривиально преобразовать эту маркировку в код Гаусса:
template<size_t n> array<int, 2*n> LabelAlternatingKnot(array<int, n> end_at)
{
array<int, n> end_of;
for(int i=0;i<n;++i) end_of[end_at[i]] = i;
array<int, 2*n> p;
for(int& i : p) i = -1;
int unique = 0;
for(int i=0;i<n;i++)
{
if(p[2*i] < 0)
{
p[2*i] = unique;
p[2*end_of[i] + 1] = unique;
++unique;
}
if(p[2*i+1] < 0)
{
p[2*i+1] = unique;
p[2*end_at[i]] = unique;
++unique;
}
}
return p;
}
template<size_t n> auto GetGaussCode(array<int, n> end_at)
{
auto crossings = LabelAlternatingKnot(end_at);
for(int& i : crossings) ++i;
for(int i=1;i<2*n;i+=2) crossings[i] = -crossings[i];
return crossings;
}
источник
KnotData
в Mathematica ...: '(Knot
! Пример использования:<< Units`; Convert[Knot, Mile/Hour]
доходность1.1507794480235425 Mile/Hour
. (Я думаю, что это смешно, независимо от того, правда это или нет, но на самом деле это правда.)Ответы:
GNU Prolog,
622634668 байт в кодовой странице 850Обновление : предыдущая версия программы иногда делала пересечения настолько плотными, что они не отображались должным образом, что нарушало спецификацию. Я добавил дополнительный код, чтобы предотвратить это.
Обновление : по-видимому, правила PPCG требуют дополнительного кода, чтобы заставить программу завершить работу и восстановить состояние в точности так, как оно было в начале. Это делает программу несколько длиннее и не добавляет к ней алгоритмического интереса, но в интересах соблюдения правил я изменил ее.
Гольф-программа
Использование GNU Prolog, потому что он имеет синтаксис решателя ограничений, который немного короче, чем переносимый арифметический синтаксис Prolog, сохраняя несколько байтов.
Алгоритм
Это одна из тех проблем, когда трудно понять, с чего начать. Неясно, как определить форму узла из приведенной нотации, потому что она не дает вам знать, нужно ли вам согнуть линию влево или вправо в любом заданном месте (и, как таковой, обозначения могут быть неоднозначными). Мое решение состояло в том, чтобы эффективно использовать старый резерв игры в гольф: написать невероятно неэффективную программу, которая генерирует все возможные результаты, а затем проверяет, соответствуют ли они входным данным. (Используемый здесь алгоритм немного более эффективен, так как Prolog может выбрасывать случайные тупики, но у него недостаточно информации, чтобы улучшить вычислительную сложность.)
Выход через терминал арт. В GNU Prolog, похоже, требуется однобайтовый набор символов, совместимый с ASCII, но ему все равно, какой из них используется (поскольку он обрабатывает символы с старшим битом, заданным как непрозрачные). В результате я использовал IBM850, который широко поддерживается и содержит нужные нам символы рисования линий.
Выход
Программа ищет все возможные изображения узлов в порядке размера их ограничительной рамки, а затем завершает работу, когда находит первое. Вот как выглядит вывод
m([0]).
:Это заняло 290,528 секунд для запуска на моем компьютере; Программа не очень эффективна. Я оставил его включенным в течение двух часов
m([0,1])
и в итоге получил следующее:Неуправляемая версия с комментариями
Подсветка синтаксиса в Stack Exchange, похоже, имеет неправильный символ комментария для Пролога, поэтому вместо
%
комментариев (которые фактически использует Пролог) в этом объяснении используются% #
комментарии (которые, конечно, эквивалентны из-за того, что начинаются с%
, но выделяются правильно).источник