period
Строк является кратчайшим ненулевым сдвигом так , что строка соответствует самому себе, игнорируя любые детали , которые сверхмандаты. Так, например, abcabcab
есть период 3
. По соглашению мы говорим, что если такого сдвига нет, то строка имеет период, равный ее длине. Итак, период abcde
есть 5
и период a
есть 1
.
В более формальных терминах период строки S
является минимальным, i > 0
так что S[1,n-i] == S[i+1,n]
(индексация из 1
).
Для данной строки S степени двух длин мы вычислим период всех ее префиксов степени двух длин. Например, рассмотрим S = abcabcab
. Периоды, которые мы рассчитаем:
'a', 1
'ab', 2
'abca', 3
'abcabcab', 3
Фактически, мы просто выведем массив периодов [1, 2, 3, 3]
.
Для данной положительной степени двойки n
рассмотрим все возможные двоичные строки S
. Напомним, что бинарная строка - это просто строка 1
s и 0
s, так что именно 2^n
таких строк (то есть 2
в степени n
). Для каждого мы можем вычислить этот массив периодов.
Задача состоит в том, чтобы написать код, который принимает
n
(степень два) в качестве входных данных и вычисляет, сколько различных таких массивов существует.
Ответы для n = 1, 2, 4, 8, 16, 32, 64, 128
:
1, 2, 6, 32, 320, 6025, 216854, 15128807
Полный набор различных массивов периодов для n = 4
:
1, 1, 1
1, 1, 3
1, 1, 4
1, 2, 2
1, 2, 3
1, 2, 4
Гол
Я буду запускать ваш код на моем компьютере с Ubuntu в течение 10 минут. Ваша оценка самая большаяn
для которой ваш код заканчивается в это время. В случае ничьей, ответ, который завершает объединение n
самых больших быстрых побед. В случае, если в течение 1 секунды есть связь по времени, первый опубликованный ответ выигрывает.
Языки и библиотеки
Вы можете использовать любой доступный язык и библиотеки, которые вам нравятся. Пожалуйста, включите полное объяснение того, как запустить / скомпилировать ваш код в Linux, если это возможно.
Ваш код должен фактически вычислять ответы, а не, например, просто выводить предварительно вычисленные значения.
Ведущие записи
- Питер Тейлор: 2 минуты 21 секунда для n = 128 в C #
- 9 секунд для n = 32 в Rust от isaacg
n
, вы бы приняли это? Неясно, где находится граница между жестким кодированием и фактическими вычислениями.abcab
. Все кроме последних 3 букв естьabcab
. Они совпадают, и удаление меньшего количества букв не совпадает.Ответы:
C #, n = 128 примерно в 2:40
Расширение до n = 256 потребовало бы переключения
BigInteger
на маски, что, вероятно, слишком сильно снижает производительность, чтобы n = 128 мог пройти без новых идей, не говоря уже о n = 256.Под Linux скомпилируйте
mono-csc
и выполните сmono
.Основное объяснение
Я не собираюсь делать построчное рассечение, просто обзор концепций.
Как правило, я рад перебрать порядка 2 50 элементов в комбинаторной программе грубой силы. Поэтому, чтобы получить n = 128, необходимо использовать подход, который не анализирует каждую цепочку битов. Таким образом, вместо того чтобы работать вперед от цепочек битов к последовательностям периодов, я работаю задом наперед: есть ли последовательность периодов, есть ли цепочка битов, которая ее реализует? Для n = 2 x существует простая верхняя граница 2 x (x + 1) / 2 последовательностей периодов (против 2 2 x цепочек битов).
Некоторые аргументы используют лемму периодичности строки :
Wlog Я предполагаю, что все рассматриваемые цепочки битов начинаются с
0
.Учитывая последовательность периодов, где есть период префикса длины 2 i ( всегда), есть несколько простых наблюдений о возможных значениях :
[p1 p2 ... pk]
pi
p0 = 1
pk+1
pk+1 ≥ pk
так как период строкиS
также является периодом любого префиксаS
.pk+1 = pk
всегда возможное расширение: просто повторите одну и ту же примитивную строку для вдвое большего количества символов.2k < pk+1 ≤ 2k+1
всегда возможное расширение. Достаточно показать это, потому что апериодическая строка длины может быть расширена до апериодической строки длиныpk+1 = 2k+1
L
L+1
, добавляя любую букву, которая не является ее первой буквой.Возьмем строку
Sx
длиной 2 k , период которой равен, и рассмотрим строку длиной 2 k + 1 . Ясно , что есть в период 2 к +1. Предположим, его период меньше.pk
SxyS
SxyS
q
Тогда по лемме о периодичности также есть период , и, поскольку наибольший делитель меньше или равен своим аргументам и является наименьшим периодом, мы требуем, чтобы коэффициент составлял 2 k +1. Так как его частное не может быть 2, мы имеем .
2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)
gcd(2k+1, q)
SxyS
q
q
q ≤ (2k+1)/3
Теперь, поскольку это период, это должен быть период . Но период является . У нас есть два случая:
q ≤ 2k
SxyS
Sx
Sx
pk
gcd(pk, q) = pk
или эквивалентно делится точно на .pk
q
pk + q > 2k + gcd(pk, q)
так что лемма периодичности не форсирует меньший период.Рассмотрим сначала второй случай. , противореча определению как период . Поэтому мы вынуждены сделать вывод, что это фактор .
pk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2q
pk
Sx
pk
q
Но поскольку
q
это периодSx
и является периодом , префикс длины - это просто копии префикса длины , поэтому мы видим, что это также период .pk
Sx
q
q/pk
pk
pk
SxyS
Следовательно, период
SxyS
равен или 2 k +1. Но у нас есть два варианта ! Максимум один выбор даст период , поэтому по крайней мере один даст период 2 k +1. QED.pk
y
y
pk
Лемма периодичности позволяет отбросить некоторые из оставшихся возможных расширений.
Любое расширение, которое не прошло тест быстрого принятия или быстрого отклонения, нуждается в конструктивной проверке.
Построение цепочки битов с заданной последовательностью периодов является по существу проблемой удовлетворительности, но она имеет много структур. Существуют простые ограничения равенства, подразумеваемые каждым периодом префикса, поэтому я использую структуру данных объединенного набора для объединения битов в независимые кластеры. Этого было достаточно, чтобы решить n = 64, но для n = 128 нужно было идти дальше. Я использую две полезные аргументы:
2k - pk
M
равен, а префикс длины имеет период, тогда префикс длины должен заканчиваться на . Это наиболее эффективно именно в тех случаях, когда в противном случае было бы большинство независимых кластеров, что удобно.01M-1
L > M
L
L
1M
M
равен, а префикс длины имеет период с и заканчивается, то фактически он должен заканчиваться на . Это наиболее эффективно в противоположной крайности, когда последовательность периодов начинается с множества единиц.0M
L > M
L - d
d < M
0d
10d
Если мы не получим непосредственное противоречие, заставив кластер с первым битом (предположительно равным нулю) равным единице, то мы перебираем силу (с некоторыми микрооптимизациями) над возможными значениями для не принудительных кластеров. Обратите внимание, что в порядке убывания числа единиц, потому что, если
i
th-й бит равен единице, период не может быть,i
и мы хотим избежать периодов, которые короче тех, которые уже применяются кластеризацией. Понижение повышает шансы найти действительное назначение на ранней стадии.источник
Ржавчина, 32, 10 с
11 с29на моем ноутбукеВызовите его с битовым размером в качестве аргумента командной строки.
Умные методы: представлять битовые строки непосредственно в виде чисел, использовать битовую переписку для проверки циклов. Выполняйте поиск только в первой половине цепочек битов, начинающихся с 0, поскольку массив периодов цепочки битов и ее обратной последовательности (с заменой 0 на 1 с) идентичны. Если все возможности для окончательной позиции уже произошли, я не ищу ее.
Более умные вещи:
Чтобы дедуплицировать каждый блок (строки, в которых первая половина битов одинакова), я использую битовый вектор, который намного быстрее, чем хэш-набор, поскольку длины последнего цикла не требуют хеширования.
Кроме того, я пропускаю первые шаги проверки цикла, потому что я знаю, что последний цикл не может быть короче второго до последнего цикла.
После большого количества профилирования я могу теперь сказать, что почти все время используется продуктивно, поэтому, я думаю, потребуется усовершенствование алгоритма, чтобы улучшить его отсюда. Я также переключился на 32-разрядные целые числа, чтобы сэкономить немного больше времени.
Положить
bit-vec = "0.4.4"
в свой Cargo.tomlЕсли вы хотите запустить это, клонируйте это: github.com/isaacg1/cycle,
Cargo build --release
чтобы построить, а затемCargo run --release 32
запустить.источник
time
дает ему 27 пользовательских секунд на моем ноутбуке.Ржавчина , 16
Попробуйте онлайн!
Компиляция:
rustc -O <name>.rs
Строка реализована в виде вектора Bool.
next
Функция перебирать комбинации;find_period
Берет кусочек Bool и возвращает период;compute_count_array
Делает работу для каждой «власти два» подпоследовательности каждой комбинации BOOLS.Теоретически, переполнение не ожидается, пока не
2^n
превысит максимальное значение u64, т.е.n > 64
. Этот предел может быть превышен дорогостоящим тестом s = [true, true, ..., true].Плохая новость: она возвращает 317 для n = 16, но я не знаю почему. Я также не знаю, удастся ли это сделать за десять минут при n = 32, поскольку
Vec<bool>
он не оптимизирован для такого рода вычислений.РЕДАКТИРОВАТЬ
Мне удалось снять ограничение 64 для
n
. Теперь он не будет аварийноn
завершать работу, пока не станет больше целого числа max usize.Я нашел, почему предыдущий код вернул 317 для
n=32
. Я считал наборы периодов, а не массивы периодов. Было три массива с одинаковыми элементами:Теперь это работает. Это все еще медленно, но это работает.
источник
С - 16
Это терпит неудачу на значениях больше чем 16 унций переполнения.
Я понятия не имею, как быстро это работает, потому что на Chromebook работает на repl.it.
Просто реализует вопрос во время чтения, просматривает все битовые строки, вычисляет массивы периодов и проверяет, были ли они уже подсчитаны.
Просто скомпилируйте его с помощью gcc и т. Д.
источник
16
тогда , когда код был изменен таким образом , что дваmalloc
s былиmalloc(...int*))
и ,...**
соответственно ,16
напечатанный ,Answer: 320
как и ожидалось, однако32
напечатанAnswer: 0
(и довольно быстро).n = 8
но после того, как результат напечатан, что означает, что стек поврежден. Возможно, вы где-то пишете за пределами выделенных блоков памяти.Haskell
Компилировать с
ghc -O2
. Попробуйте онлайн!Работает менее чем за 0,1 секунды на моем 6-летнем ноутбуке
n=16
.n=32
занимает9992 мин, так что я фактор 9 или 10 от. Я пытался кэшировать периоды в справочной таблице, поэтому мне не нужно пересчитывать их снова и снова, но это быстро исчерпывает память на моей машине 4 ГБ.источник
Python 2 (PyPy), 16
источник
osboxes@osboxes:~/python$ python ascii_user.py 16 None
[C ++], 32, 4 минуты
источник