Вступление
Вы биолог, изучающий закономерности движения бактерий. У вашей исследовательской группы их куча в чашке Петри, а вы записываете их активность. К сожалению, вы серьезно недофинансированы и не можете позволить себе видеокамеру, поэтому вы просто фотографируете блюдо через равные промежутки времени. Ваша задача - составить программу, которая отслеживает движения микробов по этим картинкам.
вход
Ваши входные данные представляют собой два двумерных массива символов в любом приемлемом формате, представляющих последовательные изображения чашки Петри. В обоих массивах символ .
представляет собой пустое пространство и O
представляет собой зародыш (вы можете выбрать любые два различных символа, если хотите). Кроме того, массив «после» получается из массива «до» путем перемещения некоторых микробов на один шаг в одном из четырех основных направлений; в частности, массивы имеют одинаковую форму. Микробы движутся одновременно, поэтому один из них может переместиться в пространство, в котором уже содержится другой микроб, если он уходит с дороги. Гарантируется, что границы массива «before» содержат только пустые пробелы и что есть хотя бы один зародыш. Таким образом, следующее является допустимой парой входных данных:
Before After
...... ......
.O..O. ....O.
.OO.O. .OO.O.
...... ..O...
Выход
Ваш вывод представляет собой один двумерный массив символов в том же формате, что и входные данные. Он получается из массива «before» путем замены тех микробов, которые переместились, на один из >^<v
, в зависимости от направления движения (вы также можете использовать любые 4 различных символа здесь). Возможных выходов может быть несколько, но вы должны указать только один из них. В приведенном выше примере одним из возможных правильных выходных данных является
......
.v..O.
.>v.O.
......
В выходных данных допускается ненужное перемещение, и микробы могут поменяться местами, поэтому действует также следующее:
......
.v..v.
.>v.^.
......
Правила и оценки
Вы можете написать полную программу или функцию. Побеждает меньшее количество байтов, и стандартные лазейки запрещены.
Мне интересны относительно эффективные алгоритмы, но я не хочу полностью запрещать грубое принуждение. По этой причине есть бонус -75% за решение последнего теста в течение 10 минут на современном процессоре (я не могу тестировать большинство решений, поэтому я просто доверю вам здесь). Отказ от ответственности: я знаю, что существует быстрый алгоритм (поиск «проблема непересекающихся путей»), но я сам не реализовал его.
Дополнительные тестовые случаи
Before
......
.O..O.
..OO..
......
After
......
..O...
...OO.
..O...
Possible output
......
.>..v.
..vO..
......
Before
.......
.OOOOO.
.O..OO.
.OO..O.
.OOOOO.
.......
After
.......
..OOOOO
.O...O.
.O...O.
.OOOOOO
....O..
Possible output
.......
.>>>>>.
.O..>v.
.Ov..v.
.O>>v>.
.......
Before
..........
.OOO..OOO.
.OOOOOOOO.
.OOO..OOO.
..........
After
..O.......
.OOO..O.O.
..OOOOOOOO
.O.O..OOO.
.......O..
Possible output
..........
.>^O..O>v.
.^O>>>vO>.
.O>^..>vO.
..........
Before
............
.OO..OOOOOO.
.OO......OO.
...OOOOOO...
.O.OOOOOO.O.
...OOOOOO...
.OOOOOOOOOO.
............
After
..........O.
.OO..OOOOO..
.O...O...O..
.O.OOOOOOO..
.O.OOOOOO..O
...OO..OO...
....OOOOOOOO
.OOO........
Possible output
............
.OO..v<<<<^.
.v<......^<.
...OOO>>>...
.O.OOO^OO.>.
...OOv^OO...
.vvvO>>>>>>.
............
Before
................
.OOOOOO.OOOOOOO.
..OO..OOOOOOOOO.
.OOO..OOOO..OOO.
..OOOOOOOO..OOO.
.OOOOOOOOOOOOOO.
................
After
................
..OOOOO.OOOOOOOO
..OO..OOOOOOOOO.
..OO..OOOO..OOOO
..OOOOOOOO..OOO.
..OOOOOOOOOOOOOO
................
Possible output
................
.>>>>>v.>>>>>>>.
..OO..>>^>>>>>v.
.>>v..OOO^..OO>.
..O>>>>>>^..OOO.
.>>>>>>>>>>>>>>.
................
Before
..............................
.OOO.O.O.....O.....O.O.O..O...
..OOO.O...O..OO..O..O.O.......
.....O......O..O.....O....O...
.O.OOOOO......O...O..O....O...
.OO..O..OO.O..OO..O..O....O...
..O.O.O......OO.OO..O..OO.....
..O....O..O.OO...OOO.OOO...O..
.....O..OO......O..O...OO.OO..
........O..O........OO.O.O....
..O.....OO.....OO.OO.......O..
.O.....O.O..OO.OO....O......O.
..O..OOOO..O....OO..........O.
.O..O...O.O....O..O....O...OO.
....O...OO..O.......O.O..OO...
........O.O....O.O....O.......
.OO.......O.OO..O.......O..O..
....O....O.O.O...OOO..O.O.OO..
.OO..OO...O.O.O.O.O...OO...O..
..............................
After
..............................
.OOOOO.......OO.....O..O......
...OO..O...O...O....OO....O...
....O.O......O..OO...OO...O...
.OO.OOOO......OO..O..O........
O.O.OO..O..O..O..OO...O...OO..
.OO.....O....OO.O..O.OO.O.....
......O.....O.....OOO.OO...O..
....O..OOOO..O..O..O.O.O.OO...
..O......O.O........O...O.O...
.O.....OOO.....OO.OO...O...O..
.......OOO..O.O.O...........O.
.O...O.....O...OOOO..O.O....O.
.O..O.O..O.....O......O....OO.
....O..O..O.O......O.....O....
........OOO....O......O..O....
.OO......O..OO..OOO.....O..O..
..O.O....OO..O...OO...O...OO..
.O..OO....O..O...O.O.O.OO.....
..............O............O..
Possible output
..............................
.OOO.O.v.....>.....>.v.O..v...
..>>^.v...>..^>..v..O.v.......
.....<......>..>.....O....O...
.O.<O><O......O...O..O....v...
.<O..O..v<.O..O^..O..>....>...
..<.^.v......OO.O^..>..<O.....
..^....v..v.Ov...>>^.<OO...O..
.....<..OO......O..O...Ov.v<..
........>..O........O^.v.^....
..^.....Ov.....OO.OO.......O..
.^.....^.^..O>.vO....v......O.
..<..Ov^^..O....><..........O.
.O..O...>.v....O..^....^...OO.
....O...<v..O.......<.^..v<...
........O.O....O.v....O.......
.OO.......<.Ov..O.......O..O..
....O....O.<.^...O^v..O.v.OO..
.O^..<<...O.>.v.>.^...<O...v..
..............................
источник
>^<v
соответствует движению ровно на один шаг в соответствующем направлении.Ответы:
Октава,
494496 байтов - бонус 372 байта = 124 байтаВ этом ответе еще предстоит много поиграть в гольф, но я хотел получить объяснение без загадок.
Я видел это как проблему удовлетворения ограничений, поэтому я выбрал свою любимую эвристику локального поиска, Min-конфликты . Идея заключается в том, чтобы с учетом начального размещения каждого зародыша в достижимом месте назначения выбрать случайный зародыш, который занимает ту же ячейку назначения, что и один или несколько других зародышей, и переместить его в действительную ячейку, в которой уже есть минимум других зародышей. Повторите по мере необходимости, пока размещение не соответствует цели.
Интересно, что этот алгоритм не гарантированно завершится (если цель недостижима, он будет продолжаться, например, бесконечно), но если он завершится, он гарантированно даст правильное решение.
Вот код:
Выход для последнего теста:
Среднее затраченное время составляло менее
9 секунд1 секунда * на 5-летнем Core i5, который претендовал на бонус.Я пытаюсь заставить это работать на ideone, но у меня, как мне кажется, возникают проблемы с тем, как он обрабатывает вложенные функции. (Вот ссылка на нерабочий ideone для справки: http://ideone.com/mQSwgZ )Код на ideone теперь работает. Мне пришлось принудительно настроить все переменные на глобальный, что не требовало их локального запуска.
* В моей версии без заглядывания было замечено, что один из шагов был неэффективным, поэтому я попытался проверить, могу ли я ускорить выполнение, и для 2 добавленных байтов время выполнения теперь меньше секунды. Код и пример выходных данных были обновлены, а входные данные для ideone были заменены на последний контрольный пример.
источник
Python, 1171 байт - бонус 878,25 байт = 292,75 байт
Идеальная ссылка: http://ideone.com/0Ylmwq
В среднем занимает от 1 до 8 секунд на последнем тестовом примере, что дает право на бонус.
Это моя первая подача кода в гольф, так что, вероятно, это не самая лучшая игра в гольф. Тем не менее, это был интересный вызов, и мне это очень понравилось. @Beaker заслуживает упоминания за напоминание о том, что эвристический поиск - это вещь. До того, как он опубликовал свое решение и вдохновил меня на повторную проверку, мой перебор был слишком долгим, чтобы претендовать на бонус в последнем тестовом примере (это было порядка 69! Итераций, что является 99-значным числом). .).
Я не хотел прямо копировать решение Beaker, поэтому я решил использовать имитацию отжига для своей эвристики поиска. Эта проблема кажется медленнее минимального конфликта (вероятно, потому что это алгоритм оптимизации, а не алгоритм удовлетворения ограничений), но он все еще находится в пределах 10-минутной отметки. Он также имел преимущество в том, что был довольно маленьким, с точки зрения кода. Я потратил на преобразование проблемы гораздо больше байтов, чем на ее решение.
объяснение
Мое решение, вероятно, довольно неэффективно, но у меня возникли проблемы с концепцией, как решить проблему как есть, и поэтому мне пришлось трансформировать ее в другую проблему, которую мне было легче понять. Я понял, что для каждой ячейки в сетке есть четыре варианта:
После разложения данных на эти классы я смог еще больше трансформировать проблему. Для меня сразу стало очевидным, что мне нужно было найти способ доставки микробов из набора «до, но не после» в клетку из набора «после, но не до». Кроме того, микробы могут перемещаться только в одно пространство, поэтому единственный способ для них воздействовать на более отдаленные клетки - это «проталкивать» непрерывный путь микробов в эту клетку. Это означало, что проблема заключалась в поиске X непересекающихся вершин на графе, где каждая ячейка с зародышем была вершиной в указанном графе, а ребра представляли соседние ячейки.
Я решил эту проблему, сначала построив график, описанный выше. Затем я перечислил все возможные пути из каждой ячейки в «До» и каждой ячейки в «После», а затем случайным образом назначил каждой ячейке в «До» один из возможных путей. Наконец, я использовал имитационный отжиг для полуслучайной мутации потенциального решения, пока в конце концов не нашел решение, не имеющее конфликтов ни для одного из путей.
Аннотированная версия
источник