Генерация пары целых чисел из неотрицательных


25

Вы должны написать программу или функцию, которая принимает неотрицательное целое число в Nкачестве входных данных и выводит или возвращает два целых числа (отрицательное, нулевое или положительное) Xи Y.

Целые числа подразумеваются в математическом смысле, поскольку их бесконечно много.

Реализованная функция должна быть биективной . Это означает, что для каждого Nон должен вывести отдельную X Yпару, и каждая X Yпара должна быть выведена для некоторого ввода, Nто есть все следующие пары должны быть выведены для некоторого N:

                 ...
    ┌─────┬─────┬────┬────┬────┐
    │-2 -2│-2 -1│-2 0│-2 1│-2 2│
    ├─────┼─────┼────┼────┼────┤
    │-1 -2│-1 -1│-1 0│-1 1│-1 2│
    ├─────┼─────┼────┼────┼────┤
... │0 -2 │0 -1 │0 0 │0 1 │0 2 │ ...
    ├─────┼─────┼────┼────┼────┤
    │1 -2 │1 -1 │1 0 │1 1 │1 2 │
    ├─────┼─────┼────┼────┼────┤
    │2 -2 │2 -1 │2 0 │2 1 │2 2 │
    └─────┴─────┴────┴────┴────┘
                 ...

Обратите внимание, что U Vи V Uразные пары, если U!=V.

Детали

  • Если ваш язык не поддерживает произвольно большие целые числа, это нормально, но ваш алгоритм должен работать с произвольно большим целочисленным типом данных. Ваш код должен по-прежнему поддерживать входные значения по крайней мере 2^31-1.
  • Если вы решите напечатать или вернуть вывод в виде строки, начальные 0символы или +знаки не допускаются. В противном случае стандартное целочисленное представление вашего языка в порядке.

пример

Если задача состоит в том, чтобы сделать биективную функцию с неотрицательным целым числом Nи вывести одно целое число, Xрешением может быть функция

if (input mod 2 == 0) return N/2 else return -(N+1)/2,

реализовано на каком-то языке. Эта функция возвращает X = 0 -1 1 -2 2...для N = 0 1 2 3 4....


Может ли любое из целых чисел в выходных данных быть повторено для другого входа? Например 10=>11 12, 9=>10 11, это неверно, потому что 11 повторяется?
BrainSteel

1
Что касается «биективный», то он определяется как «11 12», а не как «10 11» и, следовательно, действителен. Это связано с тем, что биективная функция определяется как функция ", где каждый элемент одного набора связан с ровно одним элементом другого набора, а каждый элемент другого набора связан с ровно одним элементом первого набора. непарные элементы. "( en.wikipedia.org/wiki/Bijection ). Если бы вы обратились, ваша функция «11 12» должна вывести 10, а «10 11» должна вывести 9.
GiantTree

@BrainSteel Ваш пример действителен. Только (заказанные) пары не могут быть повторены. GiantTree это правильно. Добавил еще несколько пояснений к вопросу.
Рандомра

Должна ли это быть биекция в целочисленном диапазоне данного языка или она должна работать для всех целых чисел?
flawr

1
У @LegionMammal было хорошее математическое описание задачи: «Вам нужно определить биективную функцию $ f: N + → Z ^ 2 $. - LegionMammal978». что я думаю, что было бы полезно где-то в заявлении
Брайан J

Ответы:


15

Пиф, 15

u,-HyeGhGjQ2,ZZ

Попробуйте онлайн.

u             reduce
                lambda G,H:    [implicit]
  ,-HyeGhG         (H-2*G[-1],G[0])
  jQ2           base(input(),2)
  ,ZZ           (0,0)
              print result     [implicit]

Перевод Python:

g=lambda Z,n:(n-2*Z[1],Z[0])
print reduce(g,binlist(input()),(0,0))

или итеративно:

(x,y)=(0,0)
for b in binlist(input()):
    (x,y)=(b-2*y,x)
print (x,y)

где binlistпреобразует число в список цифр, как binlist(4) = [1,0,0].

Так как же это работает? Он интерпретирует двоичные цифры числа как два перемеженных числа в базе отрицательных двух, как в моем решении Python .N

Двоичное число соответствует паре ( x , y ) = ( b 0 - 2 b 2 + 4 b 4 - 8 b 6 + , b 1 - 2 б 3 + 4 б 5 - 8 б 7 + )

Nзнак равно...б5б4б3б2б1б0
(Икс,Y)знак равно(б0-2б2+4б4-8б6+,б1-2б3+4б5-8б7+),

Если бы мы еще не обработали последнюю цифру из n , все индексы были бы выше на $ 1 $, n = b 5 b 4 b 3 b 2 b 1, соответствующих паре ( x , y ) = ( b 1 - 2 b 3 + 4 b 5 - 8 b 7 + , b 2 - 2 b 4б0N

N'знак равно...б5б4б3б2б1
(Икс',Y')знак равно(б1-2б3+4б5-8б7+,б2-2б4+4б6-8б8+),

б0

(Икс,Y)знак равно(б0-2Y',Икс'),

(Икс,Y)(б-2Y,Икс)бN(Икс,Y)


Обратите внимание, что поддержка MathJax отключена. Возможно, вы захотите отредактировать ваше объяснение для удобочитаемости.
Алекс А.

32

CJam, 24 22 21 байт

Мой мозг не может понять математику, которую используют другие решения. Но мой мозг определенно понимает бинарный код, так что вот вам подсказка, основанная на битовых манипуляциях!

li4b2fmd2/z{)(\2b^}%p

Попробуйте онлайн.

объяснение

Этот подход обрабатывает входные данные как два чередующихся двоичных значения, по одному для каждого выходного числа. Все, кроме младшего значащего бита каждого, кодируют величину, а младший значащий бит сигнализирует, следует ли принимать побитовое дополнение этой величины. В этой реализации биты с нечетным позиционированием соответствуют первому выходному номеру (а биты с нечетным позиционированием соответствуют второму) и младшему разряду 0сигналов для принятия дополнения.

Например, при заданном входе 73, чередование его двоичного представления 1001001bпроизводит 0 1|0(биты с нечетным позиционированием) и 1 0 0|1(биты с четным позиционированием). Первое значение имеет величину 01b = 1и должно быть дополнено до конечного значения ~1 = -2, а второе значение имеет величину 100b = 4и не должно дополняться.

Неформальная демонстрация правильности

Я сделал тестовую программу, которая помещает каждый вход от нуля до указанного пользователем числа минус один в своем месте вывода на 2D-сетке. Вы можете попробовать это онлайн также. Вот выходные данные этой программы, показывающие, как алгоритм отображает 0-99:

      -8 -7 -6 -5 -4 -3 -2 -1  0  1  2  3  4  5  6  7  8

-8                      92 84 86 94                     
-7                      88 80 82 90                     
-6                      76 68 70 78                     
-5                   96 72 64 66 74 98                  
-4                60 52 28 20 22 30 54 62               
-3                56 48 24 16 18 26 50 58               
-2                44 36 12  4  6 14 38 46               
-1                40 32  8  0  2 10 34 42               
 0                41 33  9  1  3 11 35 43               
 1                45 37 13  5  7 15 39 47               
 2                57 49 25 17 19 27 51 59               
 3                61 53 29 21 23 31 55 63               
 4                   97 73 65 67 75 99                  
 5                      77 69 71 79                     
 6                      89 81 83 91                     
 7                      93 85 87 95                     
 8                                                      

Шаблон заливки выглядит немного странно, но на самом деле это биективно! С каждой последовательной степенью 4 он заполняет квадрат с удвоенной длиной предыдущей стороны. Например, вот как алгоритм отображает 0-15:

      -2 -1  0  1  2

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

Это составляет квадрат 4x4 в середине квадрата 8x8 0-63:

      -4 -3 -2 -1  0  1  2  3  4

-4    60 52 28 20 22 30 54 62   
-3    56 48 24 16 18 26 50 58   
-2    44 36 12  4  6 14 38 46   
-1    40 32  8  0  2 10 34 42   
 0    41 33  9  1  3 11 35 43   
 1    45 37 13  5  7 15 39 47   
 2    57 49 25 17 19 27 51 59   
 3    61 53 29 21 23 31 55 63   
 4                              

Что составляет квадрат 8x8 в середине квадрата 16x16 из 0-255:

         -8  -7  -6  -5  -4  -3  -2  -1   0   1   2   3   4   5   6   7   8

 -8     252 244 220 212 124 116  92  84  86  94 118 126 214 222 246 254    
 -7     248 240 216 208 120 112  88  80  82  90 114 122 210 218 242 250    
 -6     236 228 204 196 108 100  76  68  70  78 102 110 198 206 230 238    
 -5     232 224 200 192 104  96  72  64  66  74  98 106 194 202 226 234    
 -4     188 180 156 148  60  52  28  20  22  30  54  62 150 158 182 190    
 -3     184 176 152 144  56  48  24  16  18  26  50  58 146 154 178 186    
 -2     172 164 140 132  44  36  12   4   6  14  38  46 134 142 166 174    
 -1     168 160 136 128  40  32   8   0   2  10  34  42 130 138 162 170    
  0     169 161 137 129  41  33   9   1   3  11  35  43 131 139 163 171    
  1     173 165 141 133  45  37  13   5   7  15  39  47 135 143 167 175    
  2     185 177 153 145  57  49  25  17  19  27  51  59 147 155 179 187    
  3     189 181 157 149  61  53  29  21  23  31  55  63 151 159 183 191    
  4     233 225 201 193 105  97  73  65  67  75  99 107 195 203 227 235    
  5     237 229 205 197 109 101  77  69  71  79 103 111 199 207 231 239    
  6     249 241 217 209 121 113  89  81  83  91 115 123 211 219 243 251    
  7     253 245 221 213 125 117  93  85  87  95 119 127 215 223 247 255    
  8                                                                        

3
Очень умно! Вы можете сохранить два байта, используя li4b2fmd2/вместо 0li2b+W%2/W%. Это дает те же самые целые числа, но в обратном порядке.
Деннис

@ Денис Это тоже очень умно. Я обновил ответ, чтобы использовать этот трюк. Благодарность!
Runer112

12

Python 2, 49

Изменить: Улучшено до 49 с использованием лучшей одношаговой рекурсии для базы -2.

def f(n):x,y=n and f(n/2)or(0,0);return n%2-2*y,x

Вот версия Pyth с использованием reduce.

Изменить: Улучшено до 52 путем переключения на базу -2 из сбалансированной троичной .

Python 2, 52

h=lambda n:n and n%2-2*h(n/4)
lambda n:(h(n),h(n/2))

Python 2, 54

h=lambda n:n and-~n%3-1+3*h(n/9)
lambda n:(h(n),h(n/3))

При этом используется чередование цифр, как в решении Runer112 , но с сбалансированным троичным, а не двоичным знаком . Python не имеет встроенного базового преобразования, поэтому код реализует его рекурсивно.

Вспомогательная функция h3вместо 9) берет натуральное число и преобразует его из троичного в сбалансированный троичный с заменой цифр

0 -> 0 
1 -> +1
2 -> -1

Так, например, 19, который равен 201 в основании, становится (-1) (0) (+ 1) в сбалансированной троичной, что составляет (-1) * 3 ^ 2 + (0) * 3 ^ 1 + (+ 1) * 3 ^ 0 = -8.

Сбалансированный троичный код достаточен для кодирования каждого целого числа, и поэтому дает отображение из натуральных чисел в целые числа.

Чтобы отобразить пары целых чисел, мы чередуем цифры в n. Чтобы сделать это, мы должны hсмотреть на каждую другую цифру, выполняя n/9рекурсивный шаг, а не n/3. Затем, для одной координаты, мы сдвигаемся nна пол-деление на 3.

Вот первые 81 выход, которые охватывают область [-4,4] ^ 2.

0 (0, 0)
1 (1, 0)
2 (-1, 0)
3 (0, 1)
4 (1, 1)
5 (-1, 1)
6 (0, -1)
7 (1, -1)
8 (-1, -1)
9 (3, 0)
10 (4, 0)
11 (2, 0)
12 (3, 1)
13 (4, 1)
14 (2, 1)
15 (3, -1)
16 (4, -1)
17 (2, -1)
18 (-3, 0)
19 (-2, 0)
20 (-4, 0)
21 (-3, 1)
22 (-2, 1)
23 (-4, 1)
24 (-3, -1)
25 (-2, -1)
26 (-4, -1)
27 (0, 3)
28 (1, 3)
29 (-1, 3)
30 (0, 4)
31 (1, 4)
32 (-1, 4)
33 (0, 2)
34 (1, 2)
35 (-1, 2)
36 (3, 3)
37 (4, 3)
38 (2, 3)
39 (3, 4)
40 (4, 4)
41 (2, 4)
42 (3, 2)
43 (4, 2)
44 (2, 2)
45 (-3, 3)
46 (-2, 3)
47 (-4, 3)
48 (-3, 4)
49 (-2, 4)
50 (-4, 4)
51 (-3, 2)
52 (-2, 2)
53 (-4, 2)
54 (0, -3)
55 (1, -3)
56 (-1, -3)
57 (0, -2)
58 (1, -2)
59 (-1, -2)
60 (0, -4)
61 (1, -4)
62 (-1, -4)
63 (3, -3)
64 (4, -3)
65 (2, -3)
66 (3, -2)
67 (4, -2)
68 (2, -2)
69 (3, -4)
70 (4, -4)
71 (2, -4)
72 (-3, -3)
73 (-2, -3)
74 (-4, -3)
75 (-3, -2)
76 (-2, -2)
77 (-4, -2)
78 (-3, -4)
79 (-2, -4)
80 (-4, -4)

Альтернативный код с четверть-мнимым получился длиннее, хотя он очень симпатичный.

Python 2, 63

h=lambda n:n and n%4+2j*h(n/4)
lambda n:(h(n).real,h(n).imag/2)

В языке с менее сложной обработкой сложных преобразований это, вероятно, будет лучшим подходом. Если бы мы могли выводить комплексные числа, мы могли бы сделать:

Python 2, 38

f=lambda n:n and n%2+n/2%2*1j-2*f(n/4)

1
Ваша первоначальная функция базы -2 будет означать средний ответ Pyth. L&b-%b2*2y/b4,yQy/Q2длиной всего 20 байтов.
Деннис

4
@ Денис Я только что написал решение Pyth с 15 символами.
xnor

Сбалансированная троичная и четвертично-мнимая. Две из моих любимых баз. Далее следуют только Base-е.
Брайан Минтон

11

Python 2, 98 байт

Давайте начнем с простого подхода:

def f(N):
 x=a=0;b=2
 while N:x+=1j**b;b+=a<1;a=a or b/2;N-=1;a-=1
 return int(x.real),int(x.imag)

Он просто формирует прямоугольные спиральные Nединицы длиной на двумерной сетке, начиная с начала координат, и возвращает координаты последней точки.

Функция является биективной, поскольку:

  • Каждая точка может быть покрыта, учитывая достаточно длинную спираль
  • Каждая точка будет пересекаться спиралью только один раз

Спираль выглядит примерно так (за исключением того, что начинается с 0, а не с 1):

Улам Спираль


@AlexA. 0**0 == 1в Python, так что это так же, какif a == 0: a = b/2
grc

Круто, спасибо за объяснение.
Алекс А.

@AlexA. оказывается a=a or b/2короче
grc

@grc 0^0=1во всей математике, а не только на питоне.
Дениф

1
@Daenyth 0**0- фактически неопределенная форма в математике
Sp3000

8

DC, 49

[1+2~2*1-*n]sm?dsa8*1+v1-2/dd1+*2/lar-dlmx32P-lmx

Это начинается с размещения неотрицательных целых чисел в сетке таким образом:

..| 
4 | 14
3 |  9 13
2 |  5  8 12
1 |  2  4  7 11
0 |  0  1  3  6 10
Y +-----------------
  X  0  1  2  3  4 ...

Обратите внимание на то, как позиции сетки заполняются по диагонали с увеличением N. Обратите внимание, что строка Y = 0 содержит треугольную последовательность чисел, заданную как N = X(X+1)/2. Это квадратное уравнение, которое решается с помощью нормальной формулы, используя только корень + ve, так что мы можем определить X из N, когда Y = 0. Далее следует простое арифметическое перемешивание, чтобы дать уникальный {X, Y} для каждого N.

Это обеспечивает требуемое биективное качество, но X и Y только неотрицательны, но вопрос требует всех возможных целых чисел. Таким образом, X и Y отображаются с использованием ((t+1)/2)*((t+1)~2*2-1)всех возможных целых чисел.

dcимеет произвольные числа точности, поэтому входной диапазон для 2^31-1не проблема. Также обратите внимание, что точность по умолчанию равна 0 десятичных цифр, sqrt()а /округление и округление - это поведение, которое необходимо здесь.

Выход:

$ for i in {0..10}; do dc biject.dc <<< $i; echo; done
0 0
0 -1
-1 0
0 1
-1 -1
1 0
0 -2
-1 1
1 -1
-2 0
0 2
$

5

Matlab, 54 байта

n=input('')+1;[i,j]=find(spiral(2*n)==n);disp([i,j]-n)

Ключевым моментом здесь является то spiral, что это создает спиральную матрицу произвольного размера.

spiral(3)

возвращается

ans =

 7     8     9
 6     1     2
 5     4     3

spiral4N2N104N1052,91011Nзнак равно232


2

Haskell, 78 74 байта

(concat[[(x,i-x),(x,x-1-i),(-1-x,x-1-i),(-1-x,i-x)]|i<-[0..],x<-[0..i]]!!)

Тестовый забег:

*Main> mapM_ (print . (concat[[(x,i-x),(x,x-1-i),(-1-x,x-1-i),(-1-x,i-x)]|i<-[0..],x<-[0..i]]!!) ) [0..20]
(0,0)
(0,-1)
(-1,-1)
(-1,0)
(0,1)
(0,-2)
(-1,-2)
(-1,1)
(1,0)
(1,-1)
(-2,-1)
(-2,0)
(0,2)
(0,-3)
(-1,-3)
(-1,2)
(1,1)
(1,-2)
(-2,-2)
(-2,1)
(2,0)

Как это работает: перечислите все пары в первом квадранте в следующем порядке

  |
 2| #4
  |
 1| #2  #5
  | 
 0| #1  #3  #6
  +---------------
     0   1   2   3 

зеркалируйте каждую точку в другие квадранты, чтобы составить список из 4 списков элементов. Объединить все в один список и взять nэлемент th.

Редактировать: функция не нуждается в имени, переупорядочена математика. выражения.


Вы можете сохранить 4 байта, используя do-notation: Попробуйте онлайн!
ბიმო

1

Haskell , 50 байтов

(0!).succ
l!n=(last$(!).succ:[(,)|odd n])l$div n 2

Попробуйте онлайн или попробуйте с обратным!

Ungolfed

ntoN2 n = 0 ! (n + 1)

xCounter ! remainingNum
  | odd remainingNum = (xCounter, div remainingNum 2)
  | otherwise        = (xCounter + 1) ! div remainingNum 2

объяснение

(Икс,Y)N22Икс(2Y+1)-1N(!)ИксlxCountery

Обратите внимание, что фактическая функция f( ntoN2) увеличивает вход перед началом процедуры.


1

05AB1E , 35 байт

>©DÝʒo®sÖ}àsÅÉʒ®sÖ}à<2÷‚εDÈi2÷ë>2÷(

Попробуйте онлайн! или как тестовый набор

объяснение

Рассмотреть возможность

е:NN×NN(Икс,Y),
Икс2ИксN+12Y+1N+1ее-1(Икс,Y)знак равно2Икс(2Y+1)-1

г:N×NZ×Z(м,N)(час(м),час(N)),
час:NZN{N2,N четное-N+12,N странный,
егчасге:NZ×Z

ге

>©DÝʒo®sÖ}àsÅÉʒ®sÖ}à<2÷‚εDÈi2÷ë>2÷( # Full program

                                    # Implicit input: Integer n
>©                                  # Compute n+1 and save it to the register
  DÝ                                # Duplicate n+1 and push the list [0,...,n+1]
    ʒo®sÖ}                          # Only keep those numbers x so that 2^x divides n+1
          à                         # Get maximum element in the list.
           sÅÉ                      # Swap so that n+1 is on top and push [1,3,5,...,n+1]
              ʒ®sÖ}                 # Only keep those numbers z which divides n+1
                   à<2÷             # Compute y = (z-1)/2
                       ‚            # Push the pair [x,y]
                        ε           # Apply the function h to x (and y):
                           i        # if...
                         DÈ         # x is even
                            2÷      # then compute x/2
                              ë>2÷( # else compute -(x+1)/2
                                    # Implicit output: [h(x),h(y)]

Вау, проголосовал за хорошее объяснение. но наверняка 05AB1E должен быть в состоянии победить Pyth?
Только для ASCII

ге

0

Математика, 46

SortBy[Tuples[Range[2#]-#,2],Norm][[#]]&[#+1]&

Сортируйте векторы по их норме, а затем по nодному.


0

JavaScript, 166 168 байт / символов

Новый подход с использованием прямоугольной спирали, как и другие.

function f(n){return b=Math,k=b.ceil((b.sqrt(n)-1)/2),t=2*k+1,m=b.pow(t,2),t+=4,m-t>n?(m-=t,m-t>n?(m-=t,m-t>n?[k,k-(m-n-t)]:[-k+(m-n),k]):[-k,-k+(m-n)]):[k-(m-n),-k]}

Я использовал этот ответ на Math.SE, перевел его на JS и сжал его с помощью UglifyJS .

Этот подход не использует петли и не создает спираль в любом случае.

е:N0Z2

Обновление: сохранены 2 символа путем сохранения Mathв b.

t-=1t+=4е(0)знак равное(8)N00


1) Повторный ответ на этот вопрос не поможет. 2) Копировать другой ответ, а затем использовать минификатор для игры в гольф, тоже не удастся :)
Оптимизатор

По крайней мере, он следует всем правилам, изложенным в вопросе, и это другой подход. Также я не краду чужую работу, но ссылаюсь на то, как я сделал этот ответ.
GiantTree

@Optimizer: 1) Я предложил GiantTree сделать репост, так как он получил 3 (заслуживающих) снижения голосов за свой оригинальный, недопустимый подход. 2) Код, который он взял из Math.SE, даже не JavaScript, поэтому он сделал больше, чем просто скопировал его в минификатор.
Деннис

Знаете, люди из Денниса могут отказаться от своего отрицательного голоса. Кроме того, использование минификатора для минимизации кода не очень рекомендуется для IMO.
Оптимизатор

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