Бібективна функція ℤ → ℤⁿ


23

Тривіально можливо створити бієктивну функцію від (набір усіх цілих чисел) до (наприклад, функція ідентичності).ZZZ

Крім того , можна створити биективное функцію з в (безліч всіх пар 2 цілих чисел, а декартовій твір з і ). Наприклад, ми можемо взяти решітку, що представляє цілі точки на 2D площині, намалювати спіраль від 0 назовні, а потім кодувати пари цілих чисел як відстань уздовж спіралі, коли вона перетинає цю точку.Z 2 Z ZZZ2ZZ

Спіраль

(Функція, яка робить це з натуральними числами, називається функцією сполучення .)

Насправді існує сімейство цих біективних функцій:

fk(x):ZZk

Змагання

Визначте сімейство функцій (де - додатне ціле число) з властивістю, яке бієктивно відображає цілі числа на -пари цілих чисел.k f k ( x ) kfk(x)kfk(x)k

Надіслані вами дані, враховуючи входи і , мають повернути .x f k ( x )kxfk(x)

Це , тому найкоротша відповідна відповідь (вимірюється в байтах) виграє.

Технічні умови

  • Будь-яка сім'я може використовуватися до тих пір, поки вона відповідає наведеним вище критеріям.fk(x)
  • Вам рекомендується додати опис того, як працює ваше сімейство функцій, а також фрагмент для обчислення зворотної функції (це не входить до вашого числа байтів).
  • Добре, якщо обернена функція є непорушною, доки ви можете довести, що ця функція бієктивна.
  • Ви можете використовувати будь-яке відповідне представлення для підписаних цілих чисел та списків підписаних цілих чисел для вашої мови, але ви повинні дозволити введення вашої функції не обмежуватись.
  • Вам потрібно лише підтримувати значення до 127.к

Чи добре брати рядкові версії kі xзамість цілих чисел?
JungHwan Мін

@JungHwanMin Рядки, що представляють вхідні числа, добре.
Esolanging Fruit

Відповіді:


19

Аліса , 14 12 байт

/O
\i@/t&Yd&

Спробуйте в Інтернеті!

Зворотна функція (не для гольфу):

/o     Q
\i@/~~\ /dt&Z

Спробуйте в Інтернеті!

Пояснення

Аліса має вбудовану біекцію між та 2 , яку можна обчислити Y(розпакувати) та її зворотну Z (пакет). Ось уривок із документів, що пояснюють біекцію:

Деталі біекції, ймовірно, не мають значення для більшості випадків використання. Основний момент полягає в тому, що він дозволяє користувачу кодувати два цілих числа в одне і витягувати цілі цілі числа знову згодом. Застосовуючи команду pack багаторазово, цілі списки або дерева цілих чисел можуть зберігатися в одному номері (хоча не особливо ефективним способом пам'яті). Відображення, обчислене операцією пакету, є бієктивною функцією 2 → ℤ (тобто відображенням один на один). По-перше, цілі числа {..., -2, -1, 0, 1, 2, ...} відображаються до натуральних чисел (включаючи нуль), як {..., 3, 1, 0, 2, 4 , ...}(інакше кажучи, від’ємні цілі числа відображаються на непарні натурали, а негативні цілі числа - у натуральні). Потім два натуральних числа відображаються на одне за допомогою функції спарювання Кантора , яка записує натурали по діагоналях першого квадранта цілої сітки. В Зокрема, {(0,0), (1,0), (0,1), (2,0), (1,1), (0,2), (3,0), ...} є відображено на {0, 1, 2, 3, 4, 5, 6, ...} . Отримане натуральне число потім відображається назад до цілих чисел, використовуючи обернену попередню біекцію. Команда unpack обчислює саме зворотну частину цього відображення.

Як уже згадувалося вище, ми можемо використовувати цю операцію розпакування для відображення від до k . Застосувавши його до початкового цілого числа, ми можемо знову розпакувати друге ціле число результату, що дає нам список із трьох цілих чисел. Отже, k-1 додатки Yдають нам k цілі числа.

Ми можемо обчислити зворотне, запакувавши список з Zкінця.

Отже сама програма має таку структуру:

/O
\i@/...d&

Це лише основний шаблон для програми, яка читає змінну кількість десяткових цілих чисел як вхідних даних і друкує змінне число як результат. Тож власне код справді просто:

t   Decrement k.
&   Repeat the next command k-1 times.
Y   Unpack.

Я хотів би сказати одне - "чому б Аліса мала вбудований для бікей ℤ → ction 2 , це не територія мови для гольфу"? Як і у більшості дивних вбудованих програм Аліси, головна причина полягає в принципі проектування Аліси, що кожна команда має два значення: одне для режиму кардиналів (цілих чисел) і одне для звичайного (рядкового) режиму, і ці два значення повинні бути якось пов'язані з поданням У режимі кардинала та ординала відчуття, що вони є дзеркальними всесвітами, де речі наче однакові, але й різні. І досить часто у мене була команда для одного з двох режимів, які я хотів додати, а потім доводилося з'ясовувати, з якою іншою командою спарити її.

У разі Yі Zрежиму Порядковий прийшов першим: Я хотів би мати функцію чергування двох рядків (ZIP) і відокремити їх знову (розпакувати). Якість цього, яке я хотів зафіксувати в режимі Кардинала, полягала в тому, щоб сформувати одне ціле число з двох і мати змогу витягти цілі цілі знову пізніше, що робить такий біекція природним вибором.

Я також подумав, що це насправді буде дуже корисно за межами гольфу, оскільки це дозволяє зберігати цілий список або навіть дерево цілих чисел в одній одиниці пам'яті (елемент стека, клітинка стрічки або комірка сітки).


Чудове пояснення, як завжди
Луїс Мендо

Виявлення Yі Zв документації Еліс насправді то , що спонукало мене опублікувати цю проблему (я думав про це деякий час, але це нагадало мені).
Esolanging Fruit

11

Пітон, 96 93 байт

def f(k,x):
 c=[0]*k;i=0
 while x:v=(x+1)%3-1;x=x//3+(v<0);c[i%k]+=v*3**(i//k);i+=1
 return c

Це в принципі працює, перетворюючи вхідне число xв збалансований потрійний , а потім розподіляючи трити (потрійні цифри) найменш значущими спочатку між різними координатами в кругообіг. Так, k=2наприклад, кожен парний трит би вніс свій внесок у xкоординату, а кожен непарно розташований трит сприяв би yкоординаті. Бо k=3ви б сприяли першому, четвертому та сьомому тритам (і т. Д.) x, А другий, п'ятий і восьмий сприяють y, а третій, шостий і дев'ятий сприяють z.

Наприклад, з k=2, давайте подивимось x=35. У збалансованому потрійному значенні 35є 110T(використовуючи позначення статті у Вікіпедії, де Tзображено -1цифру). Ділення трити вгору дає 1T(перший і третій трити, рахуючи справа) для xкоординати і 10(другий і четвертий трити) для yкоординати. Перетворивши кожну координату назад в десяткову, отримаємо 2, 3.

Звичайно, я фактично не перетворюю ціле число на збалансований потрійний одразу в код для гольфу. Я просто обчислюю по одному триті за раз (у vзмінній) і додаю його значення безпосередньо до відповідної координати.

Ось зворотна неперевершена функція, яка бере список координат і повертає число:

def inverse_f(coords):
    x = 0
    i = 0
    while any(coords):
        v = (coords[i%3]+1) % 3 - 1
        coords[i%3] = coords[i%3] // 3 + (v==-1)
        x += v * 3**i
        i += 1
    return x

Моя fфункція, мабуть, примітна своєю роботою. Він використовує лише O(k)пам'ять і потребує O(k) + O(log(x))часу, щоб знайти результати, тому він може працювати з дуже великими вхідними значеннями. Спробуйте, f(10000, 10**10000)наприклад, і ви отримаєте відповідь майже миттєво (додавання додаткового нуля до експонента, xце 10**100000означає, що це займає 30 секунд або близько того на моєму старому ПК). Зворотна функція не така швидка, головним чином тому, що важко сказати, коли вона виконана (вона сканує всі координати після кожної зміни, тому це займає щось на зразок O(k*log(x))часу). Можливо, це може бути оптимізовано, щоб бути швидшим, але це, мабуть, досить швидко для нормальних параметрів.


Ви можете видалити пробіли (нові рядки) всередині циклу while
містер Xcoder

Дякую, я помилково подумав, що між циклом та використанням ;ланцюжків операторів на одному рядку був певний конфлікт .
Blckknght

9

Лушпиння , 10 байт

§~!oΠR€Θݱ

Спробуйте в Інтернеті!

Зворотна функція також становить 10 байт.

§o!ȯ€ΠRΘݱ

Спробуйте в Інтернеті!

Пояснення

Напрямок вперед:

§~!oΠR€Θݱ  Implicit inputs, say k=3 and x=-48
        ݱ  The infinite list [1,-1,2,-2,3,-3,4,-4,..
       Θ    Prepend 0: [0,1,-1,2,-2,3,-3,4,-4,..
 ~    €     Index of x in this sequence: 97
§    R      Repeat the sequence k times: [[0,1,-1,..],[0,1,-1,..],[0,1,-1,..]]
   oΠ       Cartesian product: [[0,0,0],[1,0,0],[0,1,0],[1,1,0],[-1,0,0],[0,0,1],..
  !         Index into this list using the index computed from x: [-6,1,0]

Зворотний напрямок:

§o!ȯ€ΠRΘݱ  Implicit inputs, say k=3 and y=[-6,1,0]
     ΠRΘݱ  As above, k-wise Cartesian product of [0,1,-1,2,-2,..
   ȯ€       Index of y in this sequence: 97
§o!         Index into the sequence [0,1,-1,2,-2,.. : -48

Вбудований декартовий продукт Πдобре поводиться для нескінченних списків, перераховуючи кожну k- туту рівно один раз.


[[0,1,-1,..],[[0,1,-1,..],[[0,1,-1,..]]ця частина повинна бути [[0,1,-1,..],[0,1,-1,..],[0,1,-1,..]]?
Erik the Outgolfer

@EriktheOutgolfer Ум так, виправлено зараз.
Згарб

Це прекрасно. Як програміст J, чи знаєте ви, чи є хороший спосіб перетворити подібне рішення з ледачого списку в J, яке не підтримує їх? ^:^:_Рішення типу зазвичай закінчуються набагато громіздкішими ...
Йона

@Jonah Я не впевнений. Ви можете спробувати обчислити масив усіх k -tuples із записами i: xта сортувати його за сумою абсолютних значень, а потім індексувати на це. Ідея полягає в тому, що ці масиви є префіксами одного "нескінченного масиву", який містить усі k -турі.
Згарб

7

Мова Вольфрама (Mathematica) , 61 байт

SortBy[Range[-(x=2Abs@#+Boole[#>=0]),x]~Tuples~#2,#.#&][[x]]&

Спробуйте в Інтернеті!

(Бере ціле число, а потім довжину кортежу як вхідне.)

Зворотне:

If[OddQ[#],#-1,-#]/2&@Tr@Position[SortBy[Range[-(x=Ceiling@Norm@#),x]~Tuples~Length@#,#.#&],#]&

Спробуйте в Інтернеті!

Як це працює

Ідея проста: ми перетворюємо цілий вхід на натуральне ціле (шляхом відображення 0,1,2,3, ... до 1,3,5,7, ... і -1, -2, -3, ... до 2,4,6, ...), а потім індексувати на всі k -пари, відсортовані по відстані від початку, а потім за розбиттям за замовчуванням Mathematica.

Але ми не можемо використовувати нескінченний список, тому коли ми шукаємо n- й k- пар, ми генеруємо лише k -пари цілих чисел у діапазоні {- n , ..., n }. Цього гарантовано достатньо, тому що n- й найменший k -парна за нормою має норму менше n , і всі кортежі норми n або менше включаються до цього переліку.

Для зворотного, ми просто генеруємо досить довгий список k -парів, знаходимо положення даного k -tuple в цьому списку, а потім обертаємо операцію "скласти в натуральне число".


2
Біг із входами [15, 5]розбив мій ПК ...
JungHwan Min

2
Це станеться. В принципі, алгоритм працює для чого завгодно, але у вашому випадку він працює, генеруючи всі 5-кортежі з діапазону {-31, .., 31}, а потім приймаючи 31-й, тож він досить затягує пам'ять.
Міша Лавров

3

J, 7 байт

#.,|:#:

J-код зробити це бентежно просто

Дуже проста функція сполучення (або функція тюпінгу) полягає у простому переплетенні цифр двійкового розширення кожного з чисел. Так, наприклад (47, 79), буде сполучено як таке:

1_0_0_1_1_1_1
 1_0_1_1_1_1
-------------
1100011111111

або, 6399. Очевидно, що ми можемо тривільно узагальнити будь-який n-кортеж.

Розберемо, як це діє дієслово за дієсловом.

#:є анти-базовим двома, при використанні монадично він повертає двійкове розширення числа. #: 47 79дає результат:

0 1 0 1 1 1 1
1 0 0 1 1 1 1

|:- оператор транспонування, який просто обертає масив. Обертання результату #: 47 79дає:

0 1
1 0
0 0
1 1
1 1
1 1
1 1

Коли використовується монадично, ,це оператор рейлів, він створює 1-мірний список із таблиці:

0 1 1 0 0 0 1 1 1 1 1 1 1 1

Нарешті, #.перетворює бінарне розширення назад, даючи нам результат 6339.

Це рішення буде працювати для будь-якого рядка цілих чисел.


7
Як це працює для від’ємних чисел?
Ніл

2

Perl 6 , 148 байт

my@s=map ->\n{|grep {n==abs any |$_},(-n..n X -n..n)},^Inf;my&f={$_==1??+*!!do {my&g=f $_-1;my@v=map {.[0],|g .[1]},@s;->\n{@v[n>=0??2*n!!-1-2*n]}}}

Спробуйте в Інтернеті!

Безголівки:

sub rect($n) {
    grep ->[$x,$y] { abs($x|$y) == $n }, (-$n..$n X -$n..$n);
}

my @spiral = map { |rect($_) }, ^Inf;

sub f($k) {
    if ($k == 1) {
        -> $_ { $_ }
    } else {
        my &g = f($k-1);
        my @v = map -> [$x, $y] { $x, |g($y) }, @spiral;
        -> $_ { $_ >= 0 ?? @v[2*$_] !! @v[-1-2*$_] }
    }
}

Пояснення:

  • rect($n)- це хелперна функція, яка генерує координати інтегральних точок на краю прямокутника від координат (-$n,$n)до ($n, $n).

  • @spiral - ледачий, нескінченний список цілих точок на краях прямокутників збільшення розміру, починаючи з 0.

  • f($k)повертає функцію, яка є біекцією з цілих чисел до $k-чисел цілих чисел.

Якщо $kє 1, fповертає тотожне відображення -> $_ { $_ }.

В іншому випадку &g- це рекурсивно отримане відображення від цілих чисел до $k-1-чисел цілих чисел.

Тоді ми @spiralвиходимо з початку, і в кожній точці $kутворюємо -член, приймаючи координату X і сплющений результат виклику gз координатою Y. Це ліниво сформоване відображення зберігається в масиві @v.

@vмістить усі- $kпарні параметри, починаючи з індексу 0, тому для розширення індексації до від'ємних цілих чисел ми просто відображаємо позитивні входи на парні числа, а негативні - на непарні числа. Повертається функція (закриття), яка шукає елементи @vцього способу.


2

JavaScript, 155 байт

f=k=>x=>(t=x<0?1+2*~x:2*x,h=y=>(g=(v,p=[])=>1/p[k-1]?v||t--?0:p.map(v=>v&1?~(v/2):v/2):[...Array(1+v)].map((_,i)=>g(v-i,[...p,i])).find(u=>u))(y)||h(y+1))(0)

Досконала версія:

k => x => {
  // Map input to non-negative integer
  if (x > 0) t = 2 * x; else t = 2 * -x - 1;
  // we try to generate all triples with sum of v
  g = (v, p = []) => {
    if (p.length === k) {
      if (v) return null;
      if (t--) return null;
      // if this is the t-th one we generate then we got it
      return p;
    }
    for (var i = 0; i <= v; i++) {
      var r = g(v-i, [...p, i]);
      if (r) return r;
    }
  }
  // try sum from 0 to infinity
  h = x => g(x) || h(x + 1);
  // map tuple of non-negative integers back
  return h(0).map(v => {
    if (v % 2) return -(v + 1) / 2
    else return v / 2;
  });
}
  • По-перше, ми зіставляємо по цілому цілі числа з усіма негативними цілими числами:
    • якщо n> 0, то результат = n * 2
    • в іншому випадку результат = -n * 2 - 1
  • По-друге, ми даємо порядку всі кортежі з невід’ємними цілими числами k:
    • обчислити суму всіх елементів, менший на перше місце
    • якщо сума дорівнює, порівняйте зліва направо, меншою стає перша
    • В результаті ми отримали карту всіх невід’ємних цілих чисел до кортежів з k невід’ємними цілими числами
  • Нарешті, картографуйте невід'ємні цілі числа в кортежі, задані на другому кроці, до всіх цілих чисел із подібною формулою на першому кроці

Я думаю, що x<0?~x-x:x+xзаощаджує 2 байти.
Ніл

2

Мова Вольфрама (Mathematica) , 107 байт

(-1)^#⌈#/2⌉&@Nest[{w=⌊(√(8#+1)-1)/2⌋;x=#-w(w+1)/2,w-x}~Join~{##2}&@@#&,{2Abs@#-Boole[#<0]},#2-1]&

Спробуйте в Інтернеті!

Зворотній, 60 байт

(-1)^#⌈#/2⌉&@Fold[+##(1+##)/2+#&,2Abs@#-Boole[#<0]&/@#]&

Спробуйте в Інтернеті!

Пояснення:

Z -> N0 via f(n) = 2n if n>=0 and -2n-1 if n<0

N0 -> N0 ^ 2 через зворотну функцію сполучення

N0 -> N0 ^ k Неодноразово застосовуємо вищезазначене до лівого лівого числа, поки не отримаємо довжину k

N0 ^ k -> Z ^ k via f(n) = (-1)^n * ceil(n/2), стильний


Математика, 101 байт

(-1)^#⌈#/2⌉&@Nest[{a=#~IntegerExponent~2+1,#/2^a+1/2}~Join~{##2}&@@#&,{2Abs@#+Boole[#<=0]},#2-1]&

Аналогічно вище (використовує N замість N0), але використовує зворотну біекцію f: N ^ 2 -> N via f(a, b) = 2^(a - 1)(2b - 1)


Ви маєте на увазі ... для цього немає вбудованої математики (коли в Аліси є)? Мені бракує слів.
JayCe

1

JavaScript, 112 байт

k=>x=>(r=Array(k).fill(''),[...`${x<0?2*~x+1:2*x}`].map((c,i,s)=>r[(s.length-i)%k]+=c),r.map(v=>v&1?~(v/2):v/2))
  1. перетворити на негативні
  2. (n * k + i) та цифра до i-го числа
  3. конвертувати назад

@HermanLauenstein не потрібно повертати назад?
tsh

Я думаю, що x<0?~x-x:x+xзаощаджує 2 байти.
Ніл

Використання -5 байт [...BT${x<0?~x-x:x+x}BT].reverse().map((c,i)=>r[i%k]+=c),(кредит на @Neil для x<0?~x-x:x+x). .reverse()використовується замість того, (s.length-i)що дозволяє уникнути необхідності додаткового параметра sдо першого .map. Не потрібно повертати назад, оскільки тимчасовий масив не використовується знову. (Я не перевіряв її, але, мабуть, це має спрацювати)
Герман L

Ще один байти можуть бути збережені шляхом заміни .fill('')з .fill(0), так як провідний нуль не має ніякого значення (принаймні , ні до тестуванні в Safari)
Herman L

@HermanLauenstein Ви пробували .fill`` ? Це може зберегти ще пару байтів.
Ніл


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.