Gere um par de números inteiros a partir de um número não negativo

25

Você deve escrever um programa ou função que use um número inteiro não negativo Ncomo entrada e produza ou retorne dois números inteiros (negativo, zero ou positivo) Xe Y.

Os inteiros são entendidos no sentido matemático, pois existem infinitamente muitos deles.

A função implementada deve ser bijetiva . Isso significa que, para cada um N, deve gerar um X Ypar diferente e cada X Ypar deve ser emitido para alguma entrada, Nou seja, todos os seguintes pares devem ser emitidos para alguns 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 │
    └─────┴─────┴────┴────┴────┘
                 ...

Observe que U Ve V Usão pares diferentes se U!=V.

Detalhes

  • Se o seu idioma não suportar números inteiros arbitrariamente grandes, tudo bem, mas seu algoritmo deve funcionar com um tipo de dados inteiro arbitrariamente grande. Seu código ainda deve suportar valores de entrada por pelo menos 2^31-1.
  • Se você optar por imprimir ou retornar a saída como sequência, não serão permitidos sinais 0ou +sinais à esquerda . Caso contrário, a representação inteira padrão do seu idioma está correta.

Exemplo

Se a tarefa seria fazer uma função bijective tomando um número inteiro não negativo Ne saída de um número inteiro Xuma solução poderia ser a função

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

implementado em algum idioma. Esta função retorna X = 0 -1 1 -2 2...para N = 0 1 2 3 4....

randomra
fonte
Algum dos números inteiros na saída pode ser repetido para uma entrada diferente? por exemplo, 10=>11 12, 9=>10 11isso é inválido porque 11 é repetido?
precisa saber é o seguinte
1
Na medida em que "bijective" é definido "11 12" não é o mesmo que "10 11" e, portanto, válido. Isso ocorre porque uma função bijetiva é definida como uma função "em que cada elemento de um conjunto é emparelhado com exatamente um elemento do outro conjunto, e cada elemento do outro conjunto é emparelhado com exatamente um elemento do primeiro conjunto. Não há elementos não emparelhados ". ( en.wikipedia.org/wiki/Bijection ). Se você fosse para inverter a sua função de "11 12" deve saída 10 e "10 11" deve saída 9.
GiantTree
@BrainSteel Seu exemplo é válido. Somente os pares (ordenados) não podem ser repetidos. GiantTree está correto. Adicionadas mais explicações para a pergunta.
Random15
Tem que ser uma bijeção dentro do intervalo inteiro do idioma especificado ou deve funcionar para todos os números inteiros?
flawr
1
O @LegionMammal tinha uma boa descrição matemática da tarefa: "Você precisa definir uma função bijetiva $ f: N + → Z ^ 2 $. - LegionMammal978." que eu acho que seria algo benéfico na demonstração
Brian J

Respostas:

15

Pitão, 15

u,-HyeGhGjQ2,ZZ

Experimente online.

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

Uma tradução em Python:

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

ou iterativamente:

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

onde binlistconverte um número em uma lista de dígitos como binlist(4) = [1,0,0].

Então, como isso funciona? Ele interpreta os dígitos binários do número como dois números intercalados na base negativa dois, como na minha solução Python .n

O número binário corresponde ao par ( x , y ) = ( b 0 - 2 b 2 + 4 b 4 - 8 b 6 + , b 1 - 2 b 3 + 4 b 5 - 8 b 7 + )

n=...b5b4b3b2b1b0 0
(x,y)=(b0 0-2b2+4b4-8b6+,b1-2b3+4b5-8b7+).

Se ainda não tivéssemos processado o último dígito de n , teríamos todos os índices mais altos em $ 1 $, n = b 5 b 4 b 3 b 2 b 1 correspondente ao par ( x , y ) = ( b 1 - 2 b 3 + 4 b 5 - 8 b 7 + , b 2 - 2 b 4b0 0n

n=...b5b4b3b2b1
(x,y)=(b1-2b3+4b5-8b7+,b2-2b4+4b6-8b8+).

b0 0

(x,y)=(b0 0-2y,x).

(x,y)(b-2y,x)bn(x,y)

xnor
fonte
Observe que o suporte MathJax foi desativado. Convém editar sua explicação para facilitar a leitura.
Alex A.
32

CJam, 24 22 21 bytes

Meu cérebro tem problemas para entender a matemática que outras soluções estão usando. Mas meu cérebro definitivamente entende binário, então aqui está uma soultion baseada em manipulação de bits!

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

Experimente online.

Explicação

Essa abordagem trata a entrada como dois valores binários intercalados, um para cada número de saída. Todos os bits, exceto o menos significativo, codificam uma magnitude, e o bit menos significativo indica se deve ou não tomar o complemento bit a bit dessa magnitude. Nesta implementação, os bits com posição ímpar correspondem ao primeiro número de saída (e os bits com posição par correspondem ao segundo) e um LSB de 0sinais para obter o complemento.

Por exemplo, dada uma entrada de 73, desintercalando sua representação binária de 1001001bproduz 0 1|0(bits com posição ímpar) e 1 0 0|1(bits com posição par). O primeiro valor tem uma magnitude de 01b = 1e deve ser complementado por um valor final de ~1 = -2, e o segundo valor tem uma magnitude de 100b = 4e não deve ser complementado.

Demonstração informal de correção

Fiz um programa de teste que coloca cada entrada de zero a um número especificado pelo usuário menos um em seu local de saída em uma grade 2D. Você pode experimentá-lo online também. Aqui está uma saída deste programa mostrando como o algoritmo é mapeado 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                                                      

O padrão de preenchimento parece um pouco estranho, mas na verdade é bijetivo! Com cada potência sucessiva de 4, ele preenche um quadrado com o dobro do comprimento lateral anterior. Por exemplo, veja como o algoritmo é mapeado 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                  

Isso compõe o quadrado 4x4 no meio do quadrado 8x8 de 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                              

Que compõe o quadrado 8x8 no meio do quadrado 16x16 de 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                                                                        
Runer112
fonte
3
Muito esperto! Você pode salvar dois bytes usando em li4b2fmd2/vez de 0li2b+W%2/W%. Isso fornece os mesmos números inteiros, mas em ordem inversa.
Dennis
@ Dennis Isso também é muito inteligente. Atualizei a resposta para usar esse truque. Obrigado!
precisa saber é o seguinte
12

Python 2, 49

Editar: aprimorado para 49 usando melhor recursão em uma etapa para a base -2.

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

Aqui está uma versão Pyth usando reduce.

Editar: Aprimorado para 52, passando para a base -2 do ternário balanceado .

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))

Isso usa intercalação de dígitos como a solução do Runer112 , mas com ternário balanceado em vez de binário assinado. O Python não possui conversão básica embutida; portanto, o código a implementa recursivamente.

A função auxiliar h(com 3no lugar de 9) pega um número natural e o converte de ternário para ternário balanceado com as substituições de dígitos

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

Assim, por exemplo, 19, que é 201 na base, torna-se (-1) (0) (+ 1) no ternário balanceado, que é (-1) * 3 ^ 2 + (0) * 3 ^ 1 + (+ 1) * 3 ^ 0 = -8.

O ternário balanceado é suficiente para codificar todos os números inteiros e, portanto, fornece um mapeamento de números naturais para números inteiros.

Para mapear para pares de números inteiros, intercalamos os dígitos em n. Para fazer isso, hanalisamos todos os outros dígitos, fazendo n/9o passo recursivo em vez de n/3. Então, para uma coordenada, mudamos ndividindo o piso por 3.

Aqui estão as primeiras 81 saídas, que cobrem a região [-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)

Uma codificação alternativa com quarto de imaginário acabou por mais tempo, embora seja muito bonita.

Python 2, 63

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

Em uma linguagem com manipulação menos desajeitada de conversão complexa, essa provavelmente seria uma abordagem melhor. Se pudéssemos gerar números complexos, poderíamos fazer:

Python 2, 38

f=lambda n:n and n%2+n/2%2*1j-2*f(n/4)
xnor
fonte
1
Sua função base -2 original daria uma resposta Pyth média. L&b-%b2*2y/b4,yQy/Q2tem apenas 20 bytes.
Dennis
4
@ Dennis Acabei de escrever uma solução Pyth de 15 caracteres.
Xnor
Ternário Equilibrado e Trimestre-imaginário. Duas das minhas bases favoritas. Seguido apenas pela Base-e.
Brian Minton
11

Python 2, 98 bytes

Vamos começar com uma abordagem simples:

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)

Ele apenas forma Nunidades espirais retangulares longas em uma grade 2D, começando na origem e retorna as coordenadas do último ponto.

A função é bijetiva, pois:

  • Cada ponto pode ser coberto, dada uma espiral longa o suficiente
  • Cada ponto será cruzado apenas pela espiral uma vez

A espiral se parece com isso (exceto começando em 0 em vez de 1):

Ulam Spiral

grc
fonte
@AlexA. 0**0 == 1em python, por isso é apenas o mesmo queif a == 0: a = b/2
grc
Legal, obrigado por explicar.
Alex A.
@AlexA. Acontece que a=a or b/2é mais curto
grc 11/04
@ GRC 0^0=1em toda a matemática, não apenas python.
Daenyth
1
@Daenyth 0**0é na verdade forma indeterminada em matemática
Sp3000
8

dc, 49

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

Isso começa organizando os números inteiros não negativos em uma grade assim:

..| 
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 ...

Observe que como as posições da grade são preenchidas na diagonal com o aumento de N. Observe que a linha Y = 0 contém a sequência numérica triangular, dada por N = X(X+1)/2. Esta é uma equação quadrática que é resolvida usando a fórmula normal, usando apenas a raiz + ve, para que possamos determinar X a partir de N quando Y = 0. A seguir, alguns embaralhamento aritmético simples para fornecer {X, Y} exclusivos para cada N.

Isso fornece a qualidade bijetiva necessária, mas X e Y são apenas não negativos, mas a pergunta requer todos os números possíveis. Portanto, X e Y são mapeados usando ((t+1)/2)*((t+1)~2*2-1)para fornecer todos os números possíveis.

dcpossui números de precisão arbitrários, portanto, o intervalo de entrada para 2^31-1não é problema. Note-se também que a precisão padrão é 0 dígitos decimais, eo sqrt()e /rodada para baixo, que é o comportamento necessário aqui.

Saída:

$ 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
$
Trauma Digital
fonte
5

Matlab, 54 bytes

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

A chave aqui é que spiralisso cria uma matriz espiral de tamanho arbitrário.

spiral(3)

retorna

ans =

 7     8     9
 6     1     2
 5     4     3

spiral4n2n104n1052.91011n=232.

flawr
fonte
2

Haskell, 78 74 bytes

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

Execução de teste:

*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)

Como funciona: liste todos os pares no primeiro quadrante na seguinte ordem

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

espelhe cada ponto nos outros quadrantes para fazer uma lista de 4 listas de elementos. Concatene tudo em uma única lista e use o nelemento th.

Edit: function não precisa de um nome, reordenou a matemática. expressões.

nimi
fonte
Você pode salvar 4 bytes usando do-notation: Experimente online!
ბიმო
1

Haskell , 50 bytes

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

Experimente online ou tente com o seu inverso!

Ungolfed

ntoN2 n = 0 ! (n + 1)

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

Explicação

(x,y)N22x(2y+1)-1N(!)xlxCounter ). Quando alcançamos o número par, uma divisão inteira calcula y.

Observe que a função real f( ntoN2) incrementa a entrada antes de iniciar o procedimento.

ბიმო
fonte
1

05AB1E , 35 bytes

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

Experimente online! ou como suíte de teste

Explicação

Considerar

f:NN×Nn(x,y),
x2xn+12y+1n+1ff-1(x,y)=2x(2y+1)-1 .

g:N×NZ×Z(m,n)(h(m),h(n)),
h:NZn{n2,n até-n+12,n ímpar.
fghgf:NZ×Z é uma bijection.

gf

>©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)]
Wisław
fonte
uau, votado pela boa explicação. mas certamente 05AB1E deve ser capaz de vencer Pyth?
somente ASCII
gf
0

Mathematica, 46

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

Classifique os vetores de acordo com a norma e, em seguida, pegue o nth.

alefalpha
fonte
0

JavaScript, 166 168 bytes / caracteres

Nova abordagem usando uma espiral retangular como outras estão usando.

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]}

Eu usei essa resposta no Math.SE, traduzi-a para JS e a compactei usando o UglifyJS .

Essa abordagem não usa loops nem cria a espiral de forma alguma.

f:N0 0Z2 .

Update: Saved 2 caracteres por armazenar Mathem b.

t-=1t+=4f(0 0)=f(8)N0 00 0

GiantTree
fonte
1) Repor exatamente a mesma pergunta não ajudará. 2) Cópia outra resposta e, em seguida, usando um minifier para jogar golfe não vai também :)
Optimizer
Pelo menos, segue todas as regras estabelecidas na pergunta e é uma abordagem diferente. Também não estou roubando o trabalho de outra pessoa, mas me refiro a ele sobre como fiz essa resposta.
GiantTree
@Optimizer: 1) Sugeri que o GiantTree deveria repassar, pois ele recebeu 3 votos negativos (por merecer) por sua abordagem original e inválida. 2) O código que ele tirou do Math.SE nem é JavaScript, então ele fez mais do que simplesmente copiá-lo em um minificador.
Dennis
As pessoas do @Dennis podem retirar o voto negativo, você sabe. Além disso, o uso de um minificador para minificar código não é realmente recomendado.
Optimizer
@Optimizer Tentei jogar golfe no código, mas usar um compressor levou a um resultado melhor (menos caracteres), então usei esse.
GiantTree