从非负数生成一对整数


25

您应该编写一个程序或函数,该程序或函数将非负整数N作为输入并输出或返回两个整数(负,零或正)XY

整数在数学上是指整数,因为它们无限多。

实现的功能必须是双射的。这意味着对于每一个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 2015年

1
就“双射”而言,“ 11 12”与“ 10 11”不同,因此有效。这是因为双射函数定义为“其中一组的每个元素与另一组的一个元素恰好配对,而另一组的每个元素与第一组的一个元素恰好配对的函数。未配对的元素。”(en.wikipedia.org/wiki/Bijection)。如果你要你的反函数“11 12”应该输出10和“10 11”应该输出9
GiantTree

@BrainSteel您的示例有效。仅(有序)对不能重复。巨树是正确的。为该问题添加了更多说明。
randomra 2015年

它必须是给定语言的整数范围内的双射吗,还是应该适用于所有整数?
瑕疵的

1
@LegionMammal对任务有很好的数学描述:“您需要定义一个双射函数$ f:N +→Z ^ 2 $。– LegionMammal978。” 我认为这对声明中的某处将是有益的
Brian J

Answers:


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]

那么,这是如何工作的呢?它将数字的二进制数字解释为基数负2中的两个交错数字,就像在我的Python解决方案中一样ñ

二进制数对应于对x y = b 02 b 2 + 4 b 48 b 6 + b 12 b 3 + 4 b 58 b 7 +

ñ=b5b4b3b2b1个b0
Xÿ=b0-2b2+4b4-8b6+b1个-2b3+4b5-8b7+

如果我们还没有处理的最后一位ñ,我们不得不各项指标均高出$ 1 $,ñ 'b0ñ对应于一对X 'ÿ '= b 12 b 3 + 4 b 58 b 7 + b 22 b 4

ñ=b5b4b3b2b1个
Xÿ=b1个-2b3+4b5-8b7+b2-2b4+4b6-8b8+

一旦根据旧值读取了我们就可以表示新值b0

Xÿ=b0-2ÿX

因此,通过对n的每个连续位b重复执行变换,我们建立了从提取其数字而来的点x y Xÿb-2ÿXbñXÿ


请注意,MathJax支持已被禁用。您可能需要考虑编辑说明以提高可读性。
Alex A.

32

CJam,24 22 21字节

我的大脑难以理解其他解决方案正在使用的数学。但是我的大脑绝对可以理解二进制,因此这里是基于位操作的灵魂!

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

在线尝试。

说明

这种方法将输入视为两个交错的二进制值,每个输出编号一个。除最低有效位外,所有除最低有效位外的所有位均编码一个量级,最低有效位发信号通知是否采用该量级的按位补码。在这种实现方式中,奇数位对应于第一个输出数(偶数位对应于第二个输出数),0信号的LSB表示补码。

例如,给定的输入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                  

这将在以下8x8正方形的中间组成4x4正方形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                              

在以下16x16正方形的中间组成8x8正方形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'2015-4-10

12

Python 2,49

编辑:通过对基数-2使用更好的单步递归将其提高到49。

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

这里有一个Pyth版本使用reduce

编辑:通过从平衡三进制切换到以-2基,提高到52 。

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没有内置的基本转换,因此代码以递归方式实现。

辅助函数h(用3代替9)采用自然数,并用数字替换将其从三元数转换为平衡三元数

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

因此,例如,以19为基数的19在平衡三进制数中变为(-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)

在一种不太复杂的复杂转换处理语言中,这可能是一种更好的方法。如果我们可以输出复数,则可以执行以下操作:

蟒蛇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
@Dennis我刚刚写了一个15个字符的Pyth解决方案。
xnor 2015年

三元和四分之一虚构平衡。我最喜欢的两个基地。其次是Base-e。
Brian Minton

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从原点开始在2d网格上形成一个长的矩形螺旋单位,并返回最后一点的坐标。

该函数是双射的,因为:

  • 只要有足够长的螺旋,就可以覆盖每个点
  • 每个点仅与螺旋线相交一次

螺旋看起来像这样(除了从0而不是1开始):

乌兰螺旋


@AlexA。0**0 == 1在python中,所以与if a == 0: a = b/2
grc 2015年

太好了,谢谢您的解释。
亚历克斯A.15年

@AlexA。结果a=a or b/2更短
grc 2015年

@grc 0^0=1在所有数学中,而不仅仅是python。
丹妮丝2015年

1
@Daenyth 0**0实际上是数学中的不确定形式
Sp3000 2015年

8

直流,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根)求解,因此当Y = 0时,可以从N确定X。接下来是一些简单的算术改组,以便为​​每个N赋予唯一的{X,Y}。

这提供了所需的双射质量,但是X和Y仅是非负数,但是问题要求所有可能的整数。因此,使用映射X和Y ((t+1)/2)*((t+1)~2*2-1)来给出所有可能的整数。

dc具有任意精度的数字,因此输入范围to 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

spiral4ñ2ñ104ñ1052.91011ñ=232


2

哈斯克尔,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个元素列表的列表。将所有内容合并为一个列表,并采用nth元素。

编辑:函数不需要名称,对数学进行了重新排序。表达式。


您可以使用do-notation 保存4个字节:在线尝试!
ბიმო

1

Haskell,50个字节

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

在线尝试尝试逆向尝试!

不打高尔夫球

ntoN2 n = 0 ! (n + 1)

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

说明

Xÿñ22X2ÿ+1个-1个ñ(!)XlxCountery

请注意,实际函数fntoN2)在开始执行该过程之前会增加输入。


1

05AB1E,35个字节

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

在线尝试!或作为测试套件

说明

考虑

Fññ×ññXÿ
X2Xñ+1个2ÿ+1个ñ+1个FF-1个Xÿ=2X2ÿ+1个-1个

Gñ×ñž×žñHHñ
Hñžñ{ñ2ñ 甚至-ñ+1个2ñ 奇
FGHGFñž×ž

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

哇,赞扬了很好的解释。但肯定05AB1E应该能够击败Pyth?
仅ASCII

GF

0

Mathematica,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进行了压缩。

这种方法不使用任何循环,也不以任何方式产生螺旋。

Fñ0ž2

更新: 通过存储保存了2个字符Math在中b

t-=1t+=4F0=F8ñ00


1)重新发布完全相同的问题并不会真正有帮助。2)复制另一个答案,然后使用减震器打高尔夫球也不太可能:)
Optimizer

至少它遵循问题中陈述的所有规则,并且是另一种方法。同样,我不是在偷别人的作品,而是在我做这个答案时参考它。
巨树2015年

@Optimizer:1)我建议GiantTree应该重新发布,因为他为他的原始无效方法获得了3张(应得)赞成票。2)他从Math.SE那里获得的代码甚至都不是JavaScript,因此他所做的不只是将其复制到压缩程序中。
丹尼斯

@丹尼斯人可以收回他们的反对票,你知道的。另外,使用minifier来最小化代码并不是imo的真正建议。
Optimizer

@Optimizer我尝试编写代码,但是使用压缩程序可以得到更好的结果(更少的字符),所以我改用了那个代码。
巨树2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.