Вступление
Предположим, что вам передана случайная перестановка n
объектов. Перестановка запечатана в коробке, так что вы не знаете, какая из n!
возможных. Если вам удалось применить перестановку к n
отдельным объектам, вы могли бы сразу определить ее идентичность. Однако вам разрешено применять перестановку только к n
двоичным векторам длины , что означает, что вам придется применять ее несколько раз, чтобы распознать ее. Очевидно, что применение его к n
векторам только с одним 1
выполняет свою работу, но если вы умны, вы можете сделать это с помощью log(n)
приложений. Код для этого метода будет длиннее, хотя ...
Это экспериментальная задача, в которой ваш результат представляет собой комбинацию длины кода и сложности запроса , то есть количества обращений к вспомогательной процедуре. Спецификация немного длинная, так что терпите меня.
Задание
Ваша задача - написать именованную функцию (или ближайший эквивалент), f
которая принимает в качестве входных данных положительное целое число n
и перестановку p
первых n
целых чисел, используя индексацию на основе 0 или 1. Его вывод - перестановка p
. Однако вам не разрешен p
прямой доступ к перестановке . Единственное, что вы можете сделать с ним, это применить его к любому вектору n
битов. Для этой цели вы должны использовать вспомогательную функцию, P
которая принимает перестановку p
и вектор битов v
и возвращает переставленный вектор, в чьей p[i]
координате которого содержится бит v[i]
. Например:
P([1,2,3,4,0], [1,1,0,0,0]) == [0,1,1,0,0]
Вы можете заменить «биты» любыми двумя различными значениями, такими как 3
и -4
, или 'a'
и 'b'
, и они не должны быть фиксированными, так что вы можете вызывать P
оба [-4,3,3,-4]
и [2,2,2,1]
в одном вызове f
. Определение P
не засчитывается в ваш счет.
счет
Сложность запроса вашего решения на данный входе является числом вызовов он делает для вспомогательной функции P
. Чтобы сделать эту меру однозначной, ваше решение должно быть детерминированным. Вы можете использовать псевдослучайно сгенерированные числа, но затем вы должны также исправить начальное начальное число для генератора.
В этом репозитории вы найдете файл с именем, permutations.txt
который содержит 505 перестановок, по 5 каждой длины от 50 до 150 включительно, с использованием индексации на основе 0 (увеличивайте каждое число в случае на основе 1). Каждая перестановка находится на отдельной строке, а ее номера разделены пробелами. Ваша оценка - количество байтов f
+ средняя сложность запроса на этих входных данных . Самый низкий балл побеждает.
Дополнительные правила
Код с пояснениями предпочтителен, а стандартные лазейки запрещены. В частности, отдельные биты неразличимы (поэтому вы не можете задавать вектор Integer
объектов P
и сравнивать их идентичности), и функция P
всегда возвращает новый вектор, а не переупорядочивает свои входные данные. Вы можете свободно изменять имена f
и P
, и порядок, в котором они принимают свои аргументы.
Если вы первый, кто отвечает на вашем языке программирования, настоятельно рекомендуется включить тестовый набор, в том числе реализацию функции, P
которая также подсчитывает количество вызовов, которые были вызваны. В качестве примера, вот вам жгут для Python 3.
def f(n,p):
pass # Your submission goes here
num_calls = 0
def P(permutation, bit_vector):
global num_calls
num_calls += 1
permuted_vector = [0]*len(bit_vector)
for i in range(len(bit_vector)):
permuted_vector[permutation[i]] = bit_vector[i]
return permuted_vector
num_lines = 0
file_stream = open("permutations.txt")
for line in file_stream:
num_lines += 1
perm = [int(n) for n in line.split()]
guess = f(len(perm), perm)
if guess != perm:
print("Wrong output\n %s\n given for input\n %s"%(str(guess), str(perm)))
break
else:
print("Done. Average query complexity: %g"%(num_calls/num_lines,))
file_stream.close()
На некоторых языках невозможно написать такой жгут. В частности, Haskell не позволяет чистой функции P
записывать количество вызовов. По этой причине вам разрешается повторно реализовать ваше решение таким образом, чтобы оно также рассчитывало сложность его запроса, и использовать его в проводке.
abaaabababaa
и другое-4 3 3 3 -4 3
- это вектор битов.Ответы:
J
44,069322,0693 =3715 + 7,06931Если мы не можем назвать
P
наi. n
, мы можем по крайней мере вызоваP
на каждый битi. n
отдельно. Количество вызововP
является>. 2 ^. n
(⌈log 2 n ⌉). Я считаю, что это оптимально.Вот реализация функции,
P
которая использует вектор перестановкиp
и сохраняет количество вызовов вPinv
.Вот тестирование, которое получает перестановку и возвращает количество вызовов
p
:И вот как вы можете использовать его в файле
permutations.txt
:объяснение
Краткое объяснение уже приведено выше, но вот более подробное. Во-первых,
f
с вставленными пробелами и в качестве явной функции:Читать:
где у - формальный параметр для f. в J параметры функции (функции) называются x и y, если глагол двоичный (имеет два параметра), и y, если он монадический (имеет один параметр).
Вместо того, чтобы ссылаться
P
наi. n
(т.е.0 1 2 ... (n - 1)
), мы вызываемP
на каждой битовой позиции чисел вi. n
. Поскольку все перестановки переставляются одинаково, мы можем собрать переставленные биты в числа, чтобы получить вектор перестановки:i. y
Целые числа от0
доy - 1
.#: y
-y
представлен в базе 2. Это распространяется на векторы чисел естественным образом. Например,#: i. 16
дает:#. y
-y
интерпретируется как базовое число 2 Примечательно, что это обратное к#:
;y ~: #. #:
всегда держит.|: y
-y
транспонирован.u&.v y
-u
подv
, то естьvinv u v y
гдеvinv
обратноеv
. Заметить, что|:
это его собственная обратная сторона.P y
- функция,P
примененная к каждому векторуy
по определению.источник
Pyth 32 + 7,06931 = 37,06931
Я нашел следующий алгоритм полностью независимым. Но это более или менее то же самое, что и очень короткое J-решение FUZxxl (насколько я понимаю).
Сначала определение функции
P
, которая переставляет битовый массив в соответствии с неизвестной перестановкой.А затем код, который определяет перестановку.
Это определяет функцию
g
, которая принимает два аргумента. Вы можете позвонить поg5[4 2 1 3 0
. Вот онлайн демонстрация . Никогда не использовал так много (5) вложенных карт.Кстати, я на самом деле не сделал тестовый комплект. Также функция не
P
считает, сколько раз я ее вызываю. Я потратил много времени на то, чтобы понять алгоритм. Но если вы прочитаете мое объяснение, то совершенно очевидно, что он используетint(log2(n-1)) + 1
звонки (= ceil(log2(n))
). Иsum(int(log2(n-1)) + 1 for n in range(50, 151)) / 101.0 = 7.069306930693069
.Объяснение:
На самом деле мне было довольно трудно найти этот алгоритм. Мне вообще было не ясно, как этого добиться
log(n)
. Поэтому я начал проводить эксперименты с маленькимиn
.Первое примечание: битовый массив собирает ту же информацию, что и битовый массив дополнения. Поэтому все битовые массивы в моем решении имеют максимально
n/2
активный бит.n = 3:
Поскольку мы можем использовать только битовый массив с 1 активным битом, оптимальное решение зависит от двух вызовов. Например
P([1, 0, 0])
иP([0, 1, 0])
. Результаты сообщают нам первое и второе число перестановок, косвенно мы получаем третий.n = 4:
Здесь это немного интереснее. Теперь мы можем использовать два вида битовых массивов. Те с 1 активным битом и те с 2 активными битами. Если мы используем битовый массив с одним активным битом, мы собираем информацию только об одном номере перестановки и возвращаемся к нему
n = 3
, что приводит к1 + 2 = 3
вызовамP
. Интересно то, что мы можем сделать то же самое только с двумя вызовами, если мы используем битовые массивы с двумя активными битами. НапримерP([1, 1, 0, 0])
иP([1, 0, 1, 0])
.Допустим, мы получаем результаты
[1, 0, 0, 1]
и[0, 0, 1, 1]
. Мы видим, что бит 4 активен в обоих выходных массивах. Единственным битом, который был активен в обоих входных массивах, был бит номер 1, поэтому очевидно, что перестановка начинается с4
. Теперь легко видеть, что бит 2 был перемещен в бит 1 (первый вывод), а бит 3 был перемещен в бит 3 (второй вывод). Поэтому перестановка должна быть[4, 1, 3, 2]
.n = 7:
Теперь что-то большее. Я покажу вызовы
P
сразу. Это то, что я придумал после небольшого размышления и экспериментов. (Обратите внимание, что это не те, которые я использую в своем коде.)Если в первых двух выходных массивах (а не в третьем) бит 2 активен, мы знаем, что перестановка перемещает бит 1 в бит 2, поскольку бит один является единственным битом, который активен в первых двух входных массивах.
Важно то, что (интерпретируя входные данные как матрицу) каждый из столбцов уникален. Это запомнилось мне на кодах Хэмминга , где то же самое достигается. Они просто принимают числа от 1 до 7 и используют свое битовое представление в качестве столбцов. Я буду использовать числа от 0 до 6. Теперь хорошая часть, мы можем снова интерпретировать выходные данные (снова столбцы) как числа. Они говорят нам результат применения перестановки
[0, 1, 2, 3, 4, 5, 6]
.Так что нам осталось только отследить числа. Бит 0 заканчивался битом 5, бит 1 заканчивался битом 0, бит 2 заканчивался битом 6, так что перестановка была
[5, 0, 6, 1, 3, 4, 2]
.И код для функции перестановки:
источник
*sltQ]0
наm0sltQ
, вы можете иметь 6 вложенных карт одинаковой длины.f
хотя другие имена разрешены. Задание засчитывается в ваш счет.g
вместо чтения из STDIN.Mathematica, 63 + 100 = 163
Я использую перестановки на одной основе, поскольку именно так работает индексация в Mathematica.
Сначала тестовый жгут. Это функция запроса
p
(определяемые пользователем имена не должны быть в верхнем регистре в Mathematica):И подготовка ввода вместе с тестовой петлей:
И, наконец, мое фактическое представление, которое пока использует наивный алгоритм:
Или с отступом:
источник