Реконструюйте перестановку


16

Вступ

Припустимо, вам передають випадкову перестановку 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був би вектором бітів.
FUZxxl

@FUZxxl Так, якщо окремі елементи не відрізняються.
Згарб

Вони є числами в підході до реалізації, який я маю.
FUZxxl

@FUZxxl Я відредагував специфікацію.
Згарб

Відповіді:


11

J, 44.0693 22.0693 = 37 15 + 7.06931

Якщо ми не можемо назвати Pна i. n, ми можемо по крайней мере виклику Pна кожен біт i. nокремо. Кількість викликів Pстановить >. 2 ^. n(⌈log 2 n ⌉). Я вважаю, що це оптимально.

f=:P&.|:&.#:@i.

Ось реалізація функції, Pяка використовує вектор перестановки pі зберігає кількість викликів у Pinv.

P =: 3 : 0"1
 Pinv =: Pinv + 1
 assert 3 > # ~. y    NB. make sure y is binary
 p { y
)

Ось тестовий джгут, який отримує перестановку і повертає кількість викликів p:

harness =: 3 : 0
 Pinv =: 0
 p =: y
 assert y = f # y     NB. make sure f computed the right permutation
 Pinv
)

Ось як ви можете використовувати його у файлі permutations.txt:

NB. average P invocation count
(+/ % #) harness@".;._2 fread 'permutations.txt'

Пояснення

Коротке пояснення вже подано вище, але ось більш детальне. По-перше, fіз вставленими пробілами та як явна функція:

f =: P&.|:&.#:@i.
f =: 3 : 'P&.|:&.#: i. y'

Прочитайте:

Нехай f - P при транспозиції під базовим-2 представленням перших y цілих чисел.

де y - формальний параметр до f. в J параметри a (функції) називаються x і y, якщо дієслово є діадичним (має два параметри) і y, якщо воно є монадичним (має один параметр).

Замість виклику Pна i. n(тобто 0 1 2 ... (n - 1)), ми викликаємо Pкожне бітове положення чисел у i. n. Оскільки всі перестановки переставляють однаково, ми можемо зібрати перестановлені біти на числа, щоб отримати вектор перестановки:

  • i. y- цілі числа від 0до y - 1.
  • #: y- yпредставлено в базі 2. Це поширюється на вектори чисел природним чином. Наприклад, #: i. 16урожайність:

    0 0 0 0
    0 0 0 1
    0 0 1 0
    0 0 1 1
    0 1 0 0
    0 1 0 1
    0 1 1 0
    0 1 1 1
    1 0 0 0
    1 0 0 1
    1 0 1 0
    1 0 1 1
    1 1 0 0
    1 1 0 1
    1 1 1 0
    1 1 1 1
    
  • #. y- yінтерпретується як базове 2 число. Помітно, це обернено #:; y ~: #. #:завжди вдається.

  • |: y- yперенесено.
  • u&.v y- uпід v, vinv u v yтам, де vinvзнаходиться зворотний бік v. Зауважте, що |:це своя зворотна.

  • P y- функція, Pзастосована до кожного вектора yза визначенням.


3

Pyth 32 + 7.06931 = 37.06931

Наступний алгоритм я виявив абсолютно незалежним. Але це більш-менш те саме, що дуже коротке J-рішення FUZxxl (наскільки я це розумію).

Спочатку визначення функції P, яка перетворює бітовий масив відповідно до невідомої перестановки.

D%GHJHVJ XJ@HN@GN)RJ

А потім код, який визначає перестановку.

Mmxmi_mbk2Cm%dHCm+_jk2*sltG]0GdG

Це визначає функцію 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. Цікава частина полягає в тому, що ми можемо зробити те ж саме, лише за допомогою 2 викликів, якщо ми використовуємо бітові масиви з 2 активними бітами. Напр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. Вони колись, з якими я придумав, подумавши і експериментуючи. (Зверніть увагу, це не ті, які я використовую у своєму коді.)

P([1, 1, 1, 0, 0, 0, 0])
P([1, 0, 0, 1, 1, 0, 0])
P([0, 0, 1, 1, 0, 1, 0])

Якщо в перших двох вихідних масивах (а не в третьому) біт 2 активний, ми знаємо, що перестановка переміщується біт 1 на біт 2, оскільки біт один є єдиним бітом, який активний у перших двох вхідних масивах.

Важливим є те, що (інтерпретуючи входи як матрицю) кожен стовпчик унікальний. Це запам’яталося мені за кодами Хеммінга , де те саме відбувається. Вони просто беруть числа від 1 до 7 і використовують їх бітове представлення у вигляді стовпців. Я буду використовувати цифри 0 до 6. Тепер приємна частина, ми можемо інтерпретувати результати (знову ж стовпці) як цифри знову. Вони повідомляють нам про результат перестановки, застосованої до [0, 1, 2, 3, 4, 5, 6].

   0  1  2  3  4  5  6      1  3  6  4  5  0  2
P([0, 1, 0, 1, 0, 1, 0]) = [1, 1, 0, 0, 1, 0, 0]
P([0, 0, 1, 1, 0, 0, 1]) = [0, 1, 1, 0, 0, 0, 1]
P([0, 0, 0, 0, 1, 1, 1]) = [0, 0, 1, 1, 1, 0, 0]

Тож нам залишається лише відстежити числа. Біт 0 закінчився бітом 5, біт 1 закінчився бітом 0, біт 2 закінчився бітом 6, ... Отже, перестановка була [5, 0, 6, 1, 3, 4, 2].

Mmxmi_mbk2Cm%dHCm+_jk2*sltG]0GdG
M                                 define a function g(G, H), that will return
                                  the result of the following computation:
                                  G is n, and H is the permutation. 
                m            G     map each k in [0, 1, ..., Q-1] to:
                  _                   their inverse
                   jk2                binary representation (list of 1s and 0s)
                 +                    extended with 
                      *sltG]0         int(log2(Q - 1)) zeros
               C                   transpose matrix # rows that are longer 
                                                   # than others are shortened
           m%dH                    map each row (former column) d of 
                                   the matrix to the function P (here %)
          C                        transpose back
   m                              map each row k to:                         
    i    2                           the decimal number of the 
     _mbk                            inverse list(k) # C returns tuple :-(
Let's call the result X.  
 m                             G   map each d in [0, 1, ..., Q - 1] to:
  x         X                 d       the index of d in X

І код функції перестановки:

D%GHJHVJ XJ@HN@GN)RJ
D%GH                     def %(G, H):  # the function is called %
    JH                     J = copy(H)
      VJ         )        for N in [0, 1, ..., len(J) - 1]: 
         XJ@HN@GN            J[H[N]] = G[N]           
                  RJ      return J

1
Якщо замінити *sltQ]0з m0sltQ, ви могли б мати 6 вкладених карт на одній і тій же довжини.
isaacg

Вам слід, відповідно до виклику, призначити свій код, який вирішує виклик функції, ідеально названій, fхоча інші імена дозволені. Завдання зараховується до вашого рахунку.
FUZxxl

@FUZxxl оновив мій код. Тепер визначається функція gзамість читання з STDIN.
Якубе

2

Математика, 63 + 100 = 163

Я використовую перестановки на основі однієї, оскільки так працює індексація в Mathematica.

По-перше, тестовий джгут. Це функція запиту p(визначені користувачем імена не повинні бути великими літерами в Mathematica):

p[perm_, vec_] := (
   i += 1;
   vec[[Ordering@perm]]
   );

І підготовка вводу разом із тестовим циклом:

permutations = 
  ToExpression@StringSplit@# + 1 & /@ 
   StringSplit[Import[
     "https://raw.githubusercontent.com/iatorm/permutations/master/permutations.txt"
   ], "\n"];
total = 0;
(
    i = 0;
    result = f@#;
    If[# != result, 
      Print["Wrong result for ", #, ". Returned ," result ", instead."]
    ];
    total += i;
    ) & /@ permutations;
N[total/Length@permutations]

І нарешті, моє фактичне подання, яке наразі використовує алгоритм наївності:

f=(v=0q;v[[#]]=1;Position[q~p~v,1][[1,1]])&/@Range@Length[q=#]&

Або з відступом:

f = (
     v = 0 q;
     v[[#]] = 1;
     Position[q~p~v, 1][[1, 1]]
) & /@ Range@Length[q = #] &
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.