Генерация последовательностей Сколема


10

Сколем последовательности

Последовательность Сколема - это последовательность 2nчисел, где каждое число iмежду 1и nвстречается ровно дважды, а расстояние между двумя вхождениями iсоставляет ровно iшаги. Вот несколько примеров последовательностей Сколема:

1 1
1 1 4 2 3 2 4 3
16 13 15 12 14 4 7 3 11 4 3 9 10 7 13 12 16 15 14 11 9 8 10 2 6 2 5 1 1 8 6 5

Следующие последовательности не являются последовательностями Сколема:

1 2 1 2      (The distance between the 1's is 2, not 1)
3 1 1 3      (The number 2 is missing)
1 1 2 1 1 2  (There are four 1's)

Задача

Напишите программу, функцию или выражение для подсчета количества всех последовательностей Сколема заданной длины. Более конкретно, ваш ввод - целое число n, а вывод - количество последовательностей Сколема длины 2n. Эта последовательность имеет запись OEIS . Для n = 0, вы можете вернуть либо 0или 1. В первые несколько значений, начиная с 0, являются

0, 1, 0, 0, 6, 10, 0, 0, 504, 2656, 0, 0, 455936, 3040560, 0, 0, 1400156768

Правила и оценки

Это код гольф. Формат вывода слабый в пределах разумного.


Просто любопытно, а что 0, 1, 0, 0, 6...в твоем вопросе? Это фрагмент кода, если так, то что это за язык?
PhiNotPi

2
Почему первый элемент в вашем выводе 0? Если вы собираетесь признать 0допустимым ввод, то вывод должен быть 1.
Питер Тейлор

1
Некоторые (включая мой код) считают, что пустых последовательностей нет. Если 1 заставляет вас чувствовать себя лучше, верните его.
Boothby

2
AFAIK в каждом контексте вы предполагаете, что есть одна и только одна пустая последовательность / нулевой объект / пустой набор и т. Д. / Function-to / from-the-empty-set / empty graph / что угодно еще.
Бакуриу

1
@boothby, ты только что назвал Кнута дураком?
Питер Тейлор

Ответы:


8

GolfScript, 48 46 символов

:b,1,{)2\?){{.2$&!{.2$|@@}*.+.4b?<}do;;}+%}@/,

Более быстрая версия ( попробуйте онлайн ) - работает достаточно быстро, например, n=8занимает около двух секунд. И выбранный подход требует очень мало персонажей.

Эта версия также работает с битовыми масками. Он строит массив возможных результатов от 1 и выше, то есть для n=3:

1: 000011        000110 001100 011000 110000
2: 010111 101011 101110        011101 110101 111010

Хотя некоторые результаты (например, 000011) имеют два возможных продолжения, другие (например, 001100) не имеют ни одного и удаляются из массива результатов.

Пояснение к коду:

:b           # save the input into variable b for later use
,            # make the list 0..b-1 (the outer loop)
1,           # puts the list [0] on top of the stack - initially the only possible
             # combination
{)           # {...}@/ does the outer loop counting from i=1 to b
  2\?)       # computes the smalles possible bit mask m=2^i+1 with two bits set 
             # and distance of those equal to i (i.e. i=1: 11, i=2: 101, ...)
  {          # the next loop starts with this bitmask (prepended to code via
             # concatination {...}+
             # the loop itself iterates the top of the stack, i.e. at this point 
             # the result array                 
             # stack here contains item of result array (e.g. 00000011)
             # and bitmask (e.g. 00000101)
    {        # the inner-most loop tries all masks with the current item in the result set
      .2$&!  # do item and result set share not single bit? then - {...}*
      {
        .2$| # then generate the new entry by or-ing those two
        @@   # push it down on the stack (i.e. put working items to top)
      }*
      .+     # shift the bit mask left by one
      .4b?<  # if still in the range loop further
    }do;;    # removes the remainders of the loop (i.e. item processed and mask)
  }+%        # stack now contains the new result array
}@/
,            # length of result array, i.e. the number of Skolem sequences

Принятие быстрее связанных решений.
Boothby

6

J выражение, 47 символов

 +/*/"1((=&{:+.2-#@])#;.2)\"1~.(i.!+:y)A.,~>:i.y

Пример:

    y=:5
    +/*/"1((=&{:+.2-#@])#;.2)\"1~.(i.!+:y)A.,~>:i.y
10

Занимает около 30 секунд y=:5на моей машине.

алгоритм работает настолько медленно, насколько это возможно:

  • ~.(i.!+:y)A.,~>:i.yгенерирует каждую перестановку 1 2 .. y 1 2 .. yи удаляет повторяющиеся записи
  • ((=&{:+.2-#@])#;.2)\"1 вычисляет:
    • (...)\"1 для каждого префикса каждой строки:
      • #;.2 считает элементы перед каждым вхождением последнего элемента
      • #@] подсчитывает количество отсчетов (то есть количество вхождений последнего элемента)
      • =&{: определяет "равенство" "из" последнего элемента "списка счетчиков и исходного списка.
      • +.это логическое ИЛИ. =&{:+.2-#@]читает "либо последние элементы [в списке счетчиков и исходном списке] равны, либо в списке счетчиков есть только один элемент, а не два".
  • */"1 умножает (логическое И) на строки таблицы условий, определяя, какие перестановки являются последовательностями Сколема.
  • +/ суммирует единицы и нули вместе.

6

GolfScript (46 символов)

:&1,\,{0,2@)?)2&*{2${1$^}%@+\2*}*;+}/{4&?(=},,

Это выражение, которое принимает данные в стеке. Чтобы превратить его в полноценную программу, которая принимает ввод в stdin, добавьте~

Это довольно неэффективно - большая часть экономии, которую я сэкономил при игре в гольф по сравнению с 56 безгольфовыми символами, заключалась в расширении диапазона петель способами, которые не дают неправильных результатов, но делают подсчет отходов.

Подход - побитовая маскировка декартовых произведений. Например, (используя двоичный код для масок) для n=4негольфированного кода вычислите xor каждого элемента в декартовом произведении [00000011 00000110 ... 11000000] x [00000101 00001010 ... 10100000] x ... x [00010001 ... 10001000]. Любой результат с 8 битами может быть достигнут только с помощью неперекрывающихся масок.

Чтобы оптимизировать размер, а не скорость, код накапливает частичные продукты ( S1 u S1xS2 u S1xS2xS3 ...) и превращает каждый продукт в 2nэлементы, а не только в те, 2n-1-iкоторые действительно могут внести вклад в правильную последовательность.

скорость

Гольф-версия работает n=5на моем компьютере в течение 10 секунд и более 5 минут n=6. Оригинальная версия без игры n=5в гольф вычисляется менее чем за секунду и n=6примерно за 1 минуту. С простым фильтром промежуточных результатов, он может вычислить n=8за 30 секунд. Я набрал в нем 66 символов (в качестве программы - 65 символов в качестве выражения), сохранив как можно более ограниченные циклы и отфильтровывая промежуточные столкновения:

~:&1,\,{0,\).2\?)2&*@-{.{[\].~^.@~+<{;}*}+3$%@+\2*}*;\;}/{4&?(=},,

Черт. Как раз тогда, когда я подумал, что мое решение на 48 чар J было достаточно хорошим для публикации.
Джон Дворак

Черт. Наш галстук из 47 персонажей длился недолго. +1
Джон Дворак

5

GolfScript, 49 символов

~:/..+?:d(,{d+/base(;:w;/,{.w?)w>1$?=},,/=},,/1=+

Ожидает число nна STDIN. Это код-гольф - не пытайтесь использовать код nбольше 5.


Ой, не больше 5?
Boothby

@boothby Это была первая прямая попытка. Нам часто приходится выбирать скорость решения в зависимости от размера, а код-гольф - это размер. Вот почему я также добавил быструю версию - которая изначально была намного длиннее, а теперь еще короче.
Говард

0

Мудрец, 70

Это немного короче моего оригинала.

sum(1for i in DLXCPP([(i-1,j,i+j)for i in[1..n]for j in[n..3*n-i-1]]))

Как это работает:

Учитывая матрицу 0/1, точная проблема покрытия для этой матрицы состоит в том, чтобы найти подмножество строк, которые суммируют (как целые числа) с вектором всех единиц. Например,

11001
10100
01001
00011
00010

есть решение

10100
01001
00010

Мой любимый подход к задачам - привести их к точной проблеме прикрытия. Сколем последовательности эффективно способствуют этому. Я делаю точную проблему покрытия, где решения находятся в биекции с последовательностями Сколема длины 2n. Например, строка задачи для n=6является

  a   |  b  
001000|001001000000 # S[b] = S[b+a+1] = a

где 1 в положении a < nозначает, что символ aиспользуется. Остальные позиции соответствуют фактическим местоположениям в последовательности. Точное покрытие соответствует каждому символу, используемому ровно один раз, и каждое местоположение заполняется ровно один раз. По построению, любой символ kв локации находится на kрасстоянии от своего партнера.

В Sage DLXCPPэто реализация «танцующих звеньев» - она ​​решает точную проблему покрытия исключительно изящным способом. Это один из моих любимых алгоритмов, и нахождение в Sage на самом деле делает комбинаторное перечисление радостью.


Вау, танцевальная ссылка. Использование len(list(...))спасет 4 символа.
Рэй

@Ray Мой компьютер просто умрет, если я вычислю len(list(...))для n = 16. И это полностью убило бы время выполнения.
Boothby

Это верно, потому что преобразование генератора в список стоит много памяти.
Рэй
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.