Générer une paire d'entiers à partir d'un entier non négatif


25

Vous devez écrire un programme ou une fonction qui prend un entier non négatif Nen entrée et génère ou renvoie deux entiers (négatif, zéro ou positif) Xet Y.

Les entiers sont signifiés au sens mathématique car ils sont infiniment nombreux.

La fonction implémentée doit être bijective . Cela signifie que pour chaque Nil doit sortir une X Ypaire différente et chaque X Ypaire doit être sortie pour une entrée, Nc'est-à-dire que toutes les paires suivantes doivent être sorties pour certaines 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 │
    └─────┴─────┴────┴────┴────┘
                 ...

Notez que U Vet V Usont des paires différentes si U!=V.

Détails

  • Si votre langue ne prend pas en charge les entiers arbitrairement grands, c'est bien, mais votre algorithme devrait fonctionner avec un type de données entier arbitrairement grand. Votre code doit toujours prendre en charge les valeurs d'entrée pendant au moins 2^31-1.
  • Si vous choisissez d'imprimer ou de renvoyer la sortie sous forme de chaîne, aucun signe de 0début ni +signe n'est autorisé. Sinon, la représentation entière standard de votre langue est correcte.

Exemple

Si la tâche consistait à créer une fonction bijective prenant un entier non négatif Net à générer un entier, Xune solution pourrait être la fonction

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

mis en œuvre dans une langue. Cette fonction revient X = 0 -1 1 -2 2...pour N = 0 1 2 3 4....


L'un des entiers de la sortie peut-il être répété pour différentes entrées? Par exemple, 10=>11 12, 9=>10 11est-ce invalide parce que 11 est répété?
BrainSteel

1
Dans la mesure où "bijectif" est défini "11 12" n'est pas la même chose que "10 11" et donc valable. En effet, une fonction bijective est définie comme une fonction "où chaque élément d'un ensemble est associé à exactement un élément de l'autre ensemble, et chaque élément de l'autre ensemble est associé à exactement un élément du premier ensemble. Il n'y a pas éléments non appariés. "( en.wikipedia.org/wiki/Bijection ). Si vous deviez inverser votre fonction, "11 12" devrait produire 10 et "10 11" devrait produire 9.
GiantTree

@BrainSteel Votre exemple est valide. Seules les paires (ordonnées) ne peuvent pas être répétées. GiantTree est correct. Ajout d'une explication supplémentaire à la question.
randomra

Doit-il s'agir d'une bijection dans la plage entière du langage donné ou doit-il fonctionner pour tous les nombres entiers?
flawr

1
@LegionMammal avait une bonne description mathématique de la tâche: "Vous devez définir une fonction bijective $ f: N + → Z ^ 2 $. - LegionMammal978." que je pense serait bénéfique quelque part dans la déclaration
Brian J

Réponses:


15

Pyth, 15

u,-HyeGhGjQ2,ZZ

Essayez-le en ligne.

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

Une traduction Python:

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

ou itérativement:

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

binlistconvertit un nombre en une liste de chiffres comme binlist(4) = [1,0,0].

Donc comment ça fonctionne? Il interprète les chiffres binaires du nombre comme deux nombres entrelacés en base négative deux comme dans ma solution Python .n

Le nombre binaire correspond à la paire ( 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
(X,y)=(b0-2b2+4b4-8b6+,b1-2b3+4b5-8b7+).

Si nous n'avions pas encore traité le dernier chiffre de n , nous aurions tous les indices supérieurs de $ 1 $, n = b 5 b 4 b 3 b 2 b 1 correspondant à la paire ( x , y ) = ( b 1 - 2 b 3 + 4 b 5 - 8 b 7 + , b 2 - 2 b 4b0n

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

b0

(X,y)=(b0-2y,X).

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


Notez que la prise en charge de MathJax a été désactivée. Vous voudrez peut-être envisager de modifier votre explication pour la lisibilité.
Alex A.

32

CJam, 24 22 21 octets

Mon cerveau a du mal à comprendre les mathématiques que les autres solutions utilisent. Mais mon cerveau comprend définitivement le binaire, alors voici une soultion basée sur la manipulation de bits!

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

Essayez-le en ligne.

Explication

Cette approche traite l'entrée comme deux valeurs binaires entrelacées, une pour chaque numéro de sortie. Tous, sauf le bit le moins significatif de chacun, codent une amplitude, et le bit le moins significatif indique s'il faut ou non prendre le complément binaire de cette amplitude. Dans cette implémentation, les bits impairs correspondent au premier numéro de sortie (et les bits pairs correspondent au second) et un LSB de 0signaux pour prendre le complément.

Par exemple, étant donné une entrée de 73, sans entrelacer sa représentation binaire des 1001001bproduits 0 1|0(bits impairs) et 1 0 0|1(bits pairs). La première valeur a une magnitude de 01b = 1et doit être complétée pour une valeur finale de ~1 = -2, et la seconde valeur a une magnitude de 100b = 4et ne doit pas être complétée.

Démonstration informelle de l'exactitude

J'ai créé un programme de test qui place chaque entrée de zéro à un nombre spécifié par l'utilisateur moins un à son emplacement de sortie sur une grille 2D. Vous pouvez également l' essayer en ligne . Voici une sortie de ce programme montrant comment l'algorithme est mappé 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                                                      

Le motif de remplissage semble un peu bizarre, mais il est en fait bijectif! Avec chaque puissance successive de 4, il remplit un carré avec le double de la longueur du côté précédent. Par exemple, voici comment l'algorithme mappe 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                  

Cela constitue le carré 4x4 au milieu du carré 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                              

Ce qui constitue le carré 8x8 au milieu du carré 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                                                                        

3
Très intelligent! Vous pouvez enregistrer deux octets en utilisant li4b2fmd2/au lieu de 0li2b+W%2/W%. Cela donne les mêmes nombres entiers, mais dans l'ordre inverse.
Dennis

@Dennis C'est aussi très intelligent. J'ai mis à jour la réponse pour utiliser cette astuce. Merci!
Runer112

12

Python 2, 49

Edit: Amélioré à 49 en utilisant une meilleure récursion en une étape pour la base -2.

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

Voici une version Pyth utilisant reduce.

Edit: Amélioré à 52 en passant à la base -2 du ternaire équilibré .

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

Cela utilise l'entrelacement des chiffres comme la solution de Runer112 , mais avec un ternaire équilibré plutôt que binaire signé. Python n'a pas de conversion de base intégrée, donc le code l'implémente récursivement.

La fonction d'assistance h(avec 3à la place de 9) prend un nombre naturel et le convertit du ternaire en ternaire équilibré avec les substitutions de chiffres

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

Ainsi, par exemple, 19, qui est 201 en base, devient (-1) (0) (+ 1) en ternaire équilibré, qui est (-1) * 3 ^ 2 + (0) * 3 ^ 1 + (+ 1) * 3 ^ 0 = -8.

Un ternaire équilibré suffit pour coder chaque entier, et donne ainsi une correspondance entre les nombres naturels et les entiers.

Pour mapper sur des paires d'entiers, nous entrelacement les chiffres dans n. Pour ce faire, nous avons hexaminé tous les autres chiffres en faisant n/9comme étape récursive plutôt que n/3. Ensuite, pour une coordonnée, nous décalons nen divisant le sol par 3.

Voici les 81 premières sorties, qui couvrent la région [-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)

Un codage alternatif avec quart-imaginaire s'est avéré plus long, bien qu'il soit très joli.

Python 2, 63

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

Dans une langue avec une gestion moins maladroite des conversions complexes, ce serait probablement une meilleure approche. Si nous pouvions sortir des nombres complexes, nous pourrions faire:

Python 2, 38

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

1
Votre fonction de base -2 d'origine ferait une réponse Pyth moyenne. L&b-%b2*2y/b4,yQy/Q2ne fait que 20 octets.
Dennis

4
@Dennis Je viens d'écrire une solution Pyth de 15 caractères.
xnor

Ternaire équilibré et quart-imaginaire. Deux de mes bases préférées. Suivi uniquement par Base-e.
Brian Minton

11

Python 2, 98 octets

Commençons par une approche simple:

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)

Il forme simplement une spirale rectangulaire Nsur une grille 2D, en partant de l'origine, et renvoie les coordonnées du dernier point.

La fonction est bijective puisque:

  • Chaque point peut être couvert, étant donné une spirale suffisamment longue
  • Chaque point ne sera intersecté par la spirale qu'une seule fois

La spirale ressemble à ceci (sauf à partir de 0 plutôt que 1):

Ulam Spiral


@AlexA. 0**0 == 1en python, c'est donc la même chose queif a == 0: a = b/2
grc

Cool, merci pour l'explication.
Alex A.

@AlexA. s'avère a=a or b/2plus court
grc

@grc 0^0=1en toutes mathématiques, pas seulement en python.
Daenyth

1
@Daenyth 0**0est en fait une forme indéterminée en mathématiques
Sp3000

8

dc, 49

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

Cela commence par disposer les entiers non négatifs sur une grille ainsi:

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

Notez que la façon dont les positions de la grille sont remplies en diagonale avec l'augmentation de N. Remarquez que la ligne Y = 0 contient la séquence de nombres triangulaires, donnée par N = X(X+1)/2. Il s'agit d'une équation quadratique qui est résolue en utilisant la formule normale, en utilisant uniquement la racine + ve, afin que nous puissions déterminer X à partir de N lorsque Y = 0. Vient ensuite un simple mélange arithmétique pour donner un {X, Y} unique pour chaque N.

Cela fournit la qualité bijective requise, mais X et Y ne sont que non négatifs, mais la question nécessite tous les entiers possibles. Donc X et Y sont mappés en utilisant ((t+1)/2)*((t+1)~2*2-1)pour donner tous les entiers possibles.

dca des nombres de précision arbitraires, donc la plage d'entrée à 2^31-1ne pose aucun problème. Notez également que la précision par défaut est 0 chiffres décimaux, et sqrt()et /vers le bas autour de ce qui est le comportement nécessaire ici.

Sortie:

$ 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 octets

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

La clé ici est spiral cela crée une matrice en spirale d'une taille arbitraire.

spiral(3)

résultats

ans =

 7     8     9
 6     1     2
 5     4     3

spiral4n2ndix4ndix52.9dix11n=232


2

Haskell, 78 74 octets

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

Essai:

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

Fonctionnement: répertoriez toutes les paires du premier quadrant dans l'ordre suivant

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

miroir chaque point dans les autres quadrants pour faire une liste de 4 listes d'éléments. Concatène tout en une seule liste et prends le ne élément.

Edit: la fonction n'a pas besoin de nom, les mathématiques ont été réorganisées. expressions.


Vous pouvez économiser 4 octets en utilisant do-notation: Essayez-le en ligne!
ბიმო

1

Haskell , 50 octets

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

Essayez-le en ligne ou essayez-le avec son inverse!

Non golfé

ntoN2 n = 0 ! (n + 1)

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

Explication

(X,y)N22X(2y+1)-1N(!)XlxCountery .

Notez que la fonction réelle f( ntoN2) incrémente l'entrée avant de commencer la procédure.


1

05AB1E , 35 octets

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

Essayez-le en ligne! ou comme suite de tests

Explication

Considérer

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 même-n+12,n impair.
FghgF:NZ×Z

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

wow, a voté pour la belle explication. mais sûrement 05AB1E devrait être capable de battre Pyth?
ASCII uniquement le

gF

0

Mathematica, 46

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

Trier les vecteurs selon leur norme, puis prendre le nth.


0

JavaScript, 166 168 octets / caractères

Nouvelle approche utilisant une spirale rectangulaire comme les autres utilisent.

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

J'ai utilisé cette réponse sur Math.SE, je l'ai traduite en JS et je l' ai compressée en utilisant UglifyJS .

Cette approche n'utilise aucune boucle et ne crée aucune spirale.

F:N0Z2

Mise à jour: enregistré 2 caractères en stockant Mathdans b.

t-=1t+=4F(0)=F(8)N00


1) Republier exactement la même question n'aidera pas vraiment. 2) Copier une autre réponse puis utiliser un minifieur pour jouer au golf ne le sera pas trop :)
Optimizer

Au moins, il suit toutes les règles énoncées dans la question et c'est une approche différente. Je ne vole pas non plus le travail d'un autre, mais je me réfère à la façon dont j'ai fait cette réponse.
GiantTree

@Optimizer: 1) J'ai suggéré que GiantTree devrait republier car il a obtenu 3 downvotes (méritants) pour son approche originale et invalide. 2) Le code qu'il a pris de Math.SE n'est même pas JavaScript, il a donc fait plus que simplement le copier dans un minifieur.
Dennis

Les gens de @Dennis peuvent retirer leur vote négatif, vous savez. De plus, l'utilisation d'un minifieur pour minifier le code n'est pas vraiment encouragée imo.
Optimizer

@Optimizer J'ai essayé de jouer au code, mais l'utilisation d'un compresseur a conduit à un meilleur résultat (moins de caractères), j'ai donc utilisé celui-ci à la place.
GiantTree
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.