与邻居交换位


26

任务描述

给定一个整数,将其第(2k-1)个和第2k个最低有效位交换为所有k> 0的整数。这是OEIS中的序列A057300

(假定该数字具有“无限多个”前导零。实际上,这仅意味着在奇数长度的数字前添加一个0位。)

在此处输入图片说明

这是,所以最短的代码(以字节为单位)获胜。

测试用例

0 -> 0
1 -> 2
9 -> 6
85 -> 170
220 -> 236
1827 -> 2835
47525 -> 30298

5
我们是否可以假设该数字适合位移位之类的整数?
xnor

1
@xnor:我认为这是默认/社区共识(否则在C等语言中的答案将永远是错误的)?好确定!:)
林恩

@Lynn:C需要unsigned char array_of_bytes[1024]按照您期望的方式工作(即,是具有1024 * CHAR_BIT项的位域)。我想大多数支持任意长度输入的答案都应该CHAR_BIT是偶数,因为在字节之间移动位很麻烦。因此,您绝对可以提出一个要求,以支持k某个恒定大小,例如256或对AES合理的某种大小,而没有256位整数类型的语言将必须使用循环。这可能使SIMD向量值得考虑用于x86 asm答案:P
Peter Cordes

2
我交换@Geobits与Minibits
优化

Answers:


9

果冻10 9 7 个字节

b4d2UFḄ

在线尝试!验证所有测试用例

怎么运行的

b4d2UFḄ  Main link. Argument: n (integer)

b4       Convert n to base 4.
  d2     Divmod each quaternary digit x by 2, yielding [x : 2, x % 2].
    U    Upend; reverse each pair, yielding [x % 2, x : 2].
     F   Flatten the 2D list of binary digits.
      Ḅ  Convert from binary to integer.

20

Python 2,26字节

lambda n:2*n-3*(4**n/3&n/2)

一点技巧!

n...ghfedcba二进制形式。然后,我们可以将其拆分为其他位

n   = o + 2*e
n   = ...hgfedcba
o   = ...0g0e0c0a
2*e = ...h0f0d0b0

然后,位切换结果s可以表示为s=2*o+e

s   = 2*o + e
s   = ...ghefcdab
2*o = ...g0e0c0a0
e   = ...0h0f0d0b

我们宁愿只计算e和之一o,所以我们表达o=n-2*e和替代

s=2*(n-2*e)+e = 2*n-3*e

所以,现在它仍然表现e在以下方面n。该数字采用二进制M=4**n/3形式...10101010101,用作奇数位的掩码。指数n确保M足够长。以逐位andn/2并且该值给出e所期望。

n/2     = ...hgfedcb
M       = ...1010101
n/2 & M = ...h0f0d0b = e

相反e,我们可以用表示o e=(n-o)/2,它给出了s=(n+o*3)/2,这要感谢xsot的优化,从而节省了一个字节。

lambda n:n+(n&4**n/3)*3>>1

很好的解释,还有很好的技巧,只需减去即可使用掩码一次n。但是,我更喜欢相反的“奇数”与“偶数”位命名约定。LSB是位0,它是偶数(即使它是第一位)。在SIMD编程中,混洗经常选择带有索引的元素,索引从0开始计数,因此通常将低元素视为偶数元素。例如[ 3 2 1 0 ]
Peter Cordes

我使用您的表达式添加了一个C答案。字节节省与Digital Trauma的C答案的所有功劳归功于此。
彼得·科德斯

2
26:lambda n:n+(n&4**n/3)*3>>1
xsot

@PeterCordes您的C代码适用于gcc 4.8.3和默认设置。对于输入2f(x){return x+((x&~0U/3)*3)>>1;}返回1
丹尼斯

@Dennis:是的,在C中为我工作,我不好。我实际上是在calc(aka apcalc)中测试表达式,实际上不是C。我想我会没事的,因为我不需要截断到32bit或二进制补码。我认为表达式看起来不正确,所以我愿意相信我的错误测试。但是无论如何,我需要更好的REPL来开发比特黑客。有什么建议么?(理想情况下是Linux命令行,如bc -lcalc,但实际上是暴露int32_t/ uint32_t或某种东西,而不是扩展精度。)
Peter Cordes

10

C函数,38

位纠缠:

f(x){return(x&~0U/3*2)/2+(x&~0U/3)*2;}

伊迪恩


还是为了好玩:

C递归函数,43

根据OEIS公式a(4n+k) = 4a(n) + a(k), 0 <= k <= 3

f(x){return x>3?4*f(x/4)+f(x%4):x%3?3-x:x;}

要么

f(x){return x>3?4*f(x/4)+f(x%4):x%2*2+x/2;}

伊迪恩


1
xnor对表达式的巧妙转换使它在C语言中降至32字节。我将其作为单独的答案发布,因为这是一种截然不同的方法。
彼得·科德斯

8

CJam,16 14 13字节

ri4b4e!2=f=4b

在这里测试。

说明

ri  e# Read input and convert to integer.
4b  e# Get base-4 digits.
4e! e# Push all permutations of [0 1 2 3].
2=  e# Select the third one which happens to be [0 2 1 3].
f=  e# For each base-4 digit select the value at that position in the previous
    e# list, which swaps 1s and 2s.
4b  e# Convert back from base 4.

排列的技巧非常好!
路易斯·门多

7

Pyth,9个字节

iXjQ4S2)4

Pyth编译器中尝试一下。

怎么运行的

  jQ4      Convert the input (Q) to base 4.
 X   S2)   Translate [1, 2] to [2, 1].
i       4  COnvert from base 4 to integer.

5

JavaScript(ES6),32个 30字节

(n,m=0x55555555)=>n*2&~m|n/2&m

由于JavaScript整数的限制,最多只能使用1073741823。38 36字节最多可处理4294967295:

(n,m=0x55555555)=>(n*2&~m|n/2&m)>>>0

编辑:由于@ user81655,节省了2个字节。

51个字节最多可处理4503599627370495:

n=>parseInt(n.toString(4).replace(/1|2/g,n=>3-n),4)

难道n<<1n*2
user81655

@ user81655而且我也可以使用n/2!我不知道为什么我以前没有想到这些。
尼尔

我从未见过>>>……那是什么?
泰特斯(Titus),

@Titus就像>>>但是它执行无符号转换。>>>0基本上转换为32位无符号整数。
尼尔(Neil)

5

x86 asm函数:14个字节的机器代码

uint64_t版本:24个字节

x86-64 SysV调用约定(x在中edi),但是相同的机器代码也将在32位模式下工作。(lea将将解码为lea eax, [edi + eax*2]结果相同)。

0000000000000040 <onemask_even>:
  40:   89 f8                   mov    eax,edi
  42:   25 55 55 55 55          and    eax,0x55555555
  47:   29 c7                   sub    edi,eax
  49:   d1 ef                   shr    edi,1
  4b:   8d 04 47                lea    eax,[rdi+rax*2]
  4e:   c3                      ret    
4f: <end>

0x4f - 0x40 = 14个字节

这是使用xnor极好的一次掩膜思想的编译器输出的相反方法。(和相反的术语:低位是位0,它是偶数,不是奇数。)

unsigned onemask_even(unsigned x) {
  unsigned emask = ~0U/3;
  unsigned e = (x & emask);
  return e*2 + ((x - e) >> 1);
}

我发现编译器没有任何改进。我可能将其写为mov eax, 0x555.../ and eax, edi,但是长度相同。


对于64位整数,相同的功能占用24个字节(请参见godbolt链接)。我看不到任何比10字节短的方式movabs rax, 0x55...来在寄存器中生成掩码。(x86的div指令比较笨拙,因此将全数除以3的无符号除法将无济于事。)

我确实提出了一个循环来在rax中生成掩码,但是它是10个字节(与的长度完全相同mov imm64)。

# since 0x55 has its low bit set, shifting it out the top of RAX will set CF
0000000000000000 <swap_bitpairs64>:
   0:   31 c0                   xor    eax,eax      ; old garbage in rax could end the loop early
0000000000000002 <swap_bitpairs64.loop>:
   2:   48 c1 e0 08             shl    rax,0x8
   6:   b0 55                   mov    al,0x55      ; set the low byte
   8:   73 f8                   jnc    2 <swap_bitpairs64.loop>  ; loop until CF is set
000000000000000a <swap_bitpairs64.rest_of_function_as_normal>:
 # 10 bytes, same as   mov  rax, 0x5555555555555555
 # rax = 0x5555...
   a:   48 21 f8                and    rax,rdi
   ...

如果我们知道其中的现有字节均未rax设置低位,则可以跳过xor,这将是8个字节长。

该答案的先前版本使用loopinsn 进行了10个字节的循环,但是0xFFFFFFFFFFFFFF08由于我只设置了,因此迭代的运行时间最差cl


5

绿洲,17字节(无竞争)

n4÷axxn4÷xxe+3120

在线尝试!

Oasis是Adnan设计的一种语言,专门研究序列。

目前,该语言可以执行递归和封闭形式。

我们使用以下公式: a(4n+k) = 4a(n) + a(k), 0 <= k <= 3

指定基本情况很简单:3120结尾处的仅仅表示a(0)=0, a(1)=2, a(2)=1, a(3)=3

n4÷axxn4÷xxe+3120
                0  a(0) = 0
               2   a(1) = 2
              1    a(2) = 1
             3     a(3) = 3

n                  push n (input)
 4÷                integer-divide by 4
   a               a(n/4)
    xx             double twice; multiply by 4
                   now we have 4a(n/4)
      n            push n (input)
       4÷xx        integer-divide by 4 and then multiply by 4
                   since there is no modulo currently, n%4
                   is built as n-(n/4*4)
           e       we should have done a(n-(n/4*4)), but this
                   is a shortcut for a(n-x) where x is the top
                   of stack. Therefore, we now have a(n-n/4*4)
                   which is a(n%4).
            +      add.

4

MATL,10字节

BP2eP1ePXB

在线尝试!

修改版本以生成序列的第一项(OEIS A057300)。

说明

B     % Take input implicitly. Convert to binary array
P     % Flip
2e    % Convert to two-row 2D array, padding with a trailing zero if needed. 
      % Because of the previous flip, this really corresponds to a leading zero
P     % Flip each column. This corresponds to swapping the bits
1e    % Reshape into a row
P     % Flip, to undo the initial flipping
XB    % Convert from binary array to number. Display implicitly

3

zsh,28个字节

<<<$[`tr 12 21<<<$[[#4]$1]`]

将输入作为命令行参数,在STDOUT上输出。

它不兼容Bash,因为它使用特定于zsh的基本转换语法。

                       $1     input (first command line argument)
                 $[      ]    arithmetic expansion
                   [#4]       output in base 4
              <<<             pass the result of this to...
      tr                      the `tr' command
         12 21                and replace 1s with 2s, 2s with 1s
     `                    `   evaluate the result...
   $[                      ]  in another arithmetic expansion, to convert back
                                to base 10
<<<                           output the result on STDOUT

3

视网膜,70字节

。+
$ *
+`(1 +)\ 1
$ 1x
x1
1个
^
X
r`(。)(。)
$ 2 $ 1
1个
X@
+`@ x
X@@
X*(@*)
$ .1
0元

测试套件。(稍作修改。)

好吧,只是为了好玩:7个字节

T`12`21

将base-4作为输入,并将输出作为base-4。

在线尝试!


4
我很矛盾 我想对你的答案的下半部分投票,但对上半部分的投票否决。
丹尼斯

1
@丹尼斯现在我把那些交换了。
Leaky Nun


3

C,32 30 29字节

f(x){return(x&~0U/3)*3+x>>1;}                // 30 bit version, see below

// less golfed:
f(x){return ((x & 0x55555555)*3 + x) >>1;}   //  >> is lower precedence than +

从xsot对xnor的Python answer的评论中复制的算法。不要掩盖这两种方式,而是掩盖一种方式并结合起来。

它将编译为与我测试的上一个版本相同的asm,并且可以正常工作(对于x最高为0x3FFFFFFF,对于x以上的x,只要未设置bit 30,请参见下文)。在机器代码中,它的长度与我现有的asm答案的长度相同。


上面的版本总是清除结果的高位。最佳安全版本是32个字节:

g(x){return 2*x-3*(x/2U&~0U/3);}   // safe 32bit version, works for all x

Python版本没有这个问题,因为python在需要时使用任意精度的整数类型,而不是将其截断为固定的上限。


@丹尼斯:啊,是的,谢谢。我测试进行了最后一次更改,却错过了asm输出的差异。难怪我以为它看起来不对劲。我忘记了那>>太低的优先级了。我经常打高尔夫球以至于不能确切地记住规则是什么,因为在危险情况下建议编译器的警告会允许我节省真实代码。:P
彼得·科德斯

2
您可以通过重新排列表达式中的术语来删除该空间。
xsot

2

使用Javascript(ES6),113个 109字节

多亏了Upgoat,节省了4个字节

n=>+('0b'+/(..)+$/.exec('0'+n.toString`2`)[0].split``.reduce((p,c)=>p.length-1?[p.join(c)]:[p[0],c],[''])[0],2)

怎么运行的

n=>+('0b'+                              //parse as binary literal
    /(..)+$/.exec('0'+n.toString`2`)[0] //convert to binary string with an even number of digits
        .split``                        //convert to array
        .reduce((p,c)=>p.length-1?[p.join(c)]:[p[0],c],[''])
                                        //swap all numbers
)

使用+('0b"+binary_string_here)而不是`parseInt(...,2)
Downgoat

1

J,20个字节

4#.0 2 1 3{~4#.^:_1]

用法

>> f =: 4#.0 2 1 3{~4#.^:_1]
>> f 85
<< 170

>>STDIN和<<STDOUT 在哪里。

不打高尔夫球

to_base   =: 4 #.^:_1 ]
transpose =: 0 2 1 3 {~ to_base
from_base =: 4 #. transpose

三把叉子。

边注

在官方解释器中,^:_1可以替换为inv,节省1个字节。

但是,没有在线解释器实现此目的。



1

INTERCAL,60个字节

DOWRITEIN.1PLEASE.1<-!1~#21845'$.1~#43690DOREADOUT.1DOGIVEUP

在线尝试!

适用于16位整数,对于INTERCAL,I / O以最自然的格式进行:输入是用几种自然语言或构造语言之一拼出的一系列十进制数字,而输出则是“屠宰罗马数字”。

这是那些难得的挑战之一,实际上可以直观地使用INTERCAL的二进制运算符,因为重新定位位就是它们的全部。Select(~)从其第一个参数中获取与第二个参数中的一个相对应的位,并用零将其填充到右侧,而mingle($)对其参数中的这些位进行交织,以使第一个参数中的位更有效。因此,直接的解决方案是选择较低有效的交替位(.1~#21845),选择较高有效的交替位(.1~#43690),然后以相反的顺序将它们交织在一起。幸运的是,对于字节数,尽管INTERCAL的运算符没有定义的优先级(因为该语言的目标是没有先例),但事实证明TIO上的C-INTERCAL不需要为此特定表达式进行太多分组,因为'.可以缩写!

支持32位整数:

INTERCAL,67个字节

DOWRITEIN:1PLEASE:1<-':1~#0$#65535'$:1~#65535$#0DOREADOUT:1DOGIVEUP

在线尝试!

INTERCAL不允许使用32位文字,这实际上使它更易于阅读,因为这意味着选择交替位的魔术常数必须通过将两个16位文字混合在一起而构成,其中一个全为零,另一个为零所有的。(实际上,即使有32位文字,它也将更短。#0$#65535相距两个字节#1431655765,另一个字节也是如此。)这对于INTERCAL来说,整个过程的通信自然不自然。

一种笨拙地使用操作数重载的替代方法:

INTERCAL,71个字节

DO:1<-:1/.2$.3PLEASEWRITEIN:2DO:1<-:2PLEASE:2<-.3$.2DOREADOUT:2DOGIVEUP

在线尝试!

这清除了通过声明选择完全:1.2夹杂着.3,设置:1到输入端,然后输出.3与混到.2。由于:1被重载为.2$.3,因此DO :1 <- :2.2和分配值,.3从而:1获取的值:2,这将导致.2包含来自的较高有效交替位,:2.3包含较低的有效交替位。这将是两个32位解决方案由四个字节的短,如果PLEASE WRITE IN :1可以取代PLEASE WRITE IN :2 DO :1 <- :2重载的:1,但CALCULATING事实证明,使用重载是必要的。我也觉得可能有比通过来启动程序更短的重载方法DO:1<-:1/.2$.3,但是由于这是INTERCAL,所以我也觉得不可能。


0

Mathematica,44个字节

Fold[3#+##&,#~IntegerDigits~4/.{1->2,2->1}]&

与我的CJam答案相同的方法:转换为base-4,交换1和2,再转换回。它还使用alephalpha的技巧来替换FromDigits一个Fold保存一个字节的操作。


0

实际上是16个字节

4@¡"21""12"(t4@¿

在线尝试!

说明:

4@¡"21""12"(t4@¿
4@¡               base 4 representation of n
   "21""12"(t     translate (swap 1s and 2s)
             4@¿  base 4 to decimal

0

J,22个字节

([:,_2|.\,&0)&.(|.@#:)

基于数组操作而不是基于4的技巧的替代方法。

用法

   f =: ([:,_2|.\,&0)&.(|.@#:)
   (,.f"0) 0 1 9 85 220 1827 47525
    0     0
    1     2
    9     6
   85   170
  220   236
 1827  2835
47525 30298

说明

([:,_2|.\,&0)&.(|.@#:)  Input: n
                   #:   Get the value as a list of base 2 digits
                |.@     Reverse it
(           )&.         Apply to the list of base 2 digits
         ,&0            Append a zero to the end of the list
    _2  \               Split the list into nonoverlapping sublists of size 2
      |.                Reverse each sublist
 [:,                    Flatten the list of sublists into a list
             &.(    )   Apply the inverse of (reversed base 2 digits)
                        to convert back to a number and return it

0

REXX,88个字节

n=x2b(d2x(arg(1)))
o=0
do while n>''
  parse var n a+1 b+1 n
  o=o||b||a
  end
say x2d(b2x(o))
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.