找出第二个零


10

挑战

给定一个32位二进制补码格式的整数,请返回二进制表示形式中第二个最低有效零数字的索引,其中索引0表示最低有效位,而索引31表示最高有效位。

如果没有第二个零,则可以返回0,任何负数,任何虚假值或以您的语言有意义的方式报告错误。

如果愿意,可以使用1索引,但是下面的测试用例将使用0索引。

如果愿意,可以使用无符号整数。如果这样做,则必须处理范围内的整数[0, 2^32)。如果使用带符号整数,则必须处理范围内的整数[-2^31, 2^31)。这里的测试用例将使用带符号的整数,但请注意,-x(带符号)为2^32 - x(无符号)。

测试用例

0(0b00)-> 1
1(0b001)-> 2
10(0b1010)-> 2
11(0b01011)-> 4
12(0b1100)-> 1
23(0b010111)-> 5
-1(0b11..11)->无
-2(0b11..10)->无
-4(0b11..00)-> 1
-5(0b11..1011)->无
-9(0b11..10111)->无
2 ^ 31-2(0b0111..1110)-> 31

计分

这是,因此每种语言中最短的答案将获胜!


我们可以改用无符号整数吗?
Leaky Nun

是的,您可以,只要然后处理范围内的整数[0, 2^32)
musicman523

1
我们是以整数还是字符串0b...作为输入?
TheLethalCoder

1
@JonathanAllan我想不是,因为我果冻的答案得到了纠正,2^32-1因为我不该返回33
暴民埃里克(Erik the Outgolfer)'17年

1
@JonathanAllan Erik的答案是正确的。我已经更新了挑战规范,以反映无论选择带符号还是无符号,您都应该处理32位整数
musicman523 '17

Answers:


16

Python 2,45个字节

lambda n:[i for i in range(32)if n|1<<i>n][1]

在线尝试!

使用0索引的无符号数字,并且在没有第二个零时引发错误。

只需创建一个从最低到最高的未设置位索引列表,然后返回第二个条目。


5
欢迎来到PPCG!不错的第一篇文章!
暴民埃里克(Erik the Outgolfer)

谢谢!我仍然非常不熟悉Python中的高尔夫技巧,因此我很高兴这段代码没有立即被使用。
阿诺德·帕尔默

太好了:)n = 2147483647呢?
mdahmoune

@mdahmoune 2 ** 31-1应该会出错,因为其32位二进制表示为0b01111111111111111111111111111111111,它没有第二个0。除非我丢失了某些内容……
Arnold Palmer

6

JavaScript(ES6),34个字节

返回从0开始的索引,或者-1找不到第二个零。

n=>31-Math.clz32((n=~n^~n&-~n)&-n)

测试用例

替代表达

n=>31-Math.clz32((n=~n^++n&-n)&-n)

递归版本,42字节

返回从0开始的索引,或者false找不到第二个零。

f=(n,p=k=0)=>n&1||!k++?p<32&&f(n>>1,p+1):p

怎么样?

f=(n,p=k=0)=>                               // given n, p, k
             n&1||                          // if the least significant bit of n is set
                  !k++?                     // or this is the 1st zero (k was not set):
                       p<31&&               //   return false if p is >= 31
                             f(n>>1,p+1)    //   or do a recursive call with n>>1 / p+1
                                        :p  // else: return p

测试用例

Neil建议的替代版本,41字节

返回从0开始的索引,如果找不到第二个零,则抛出过多的递归错误。

f=(n,c=1)=>n%2?1+f(~-n/2,c):c&&1+f(n/2,0)

41字节的递归版本:f=(n,c=1)=>n%2?1+f(~-n/2,c):c&&1+f(n/2,0)
Neil

5

果冻,7个字节

|‘‘&~l2

在线尝试!

如果没有第二个零,它将输出不在[1,31]范围内的内容。这包括32 33(-inf+nanj)。我想这很有道理。

它计算log(((x|(x+1))+1)&~x)/log(2)


1
-inf+nanj我认为这甚至不可能存在
Luis Mendo

(-inf+nanj)对于2147483647具有31个1s的二进制表示形式的输入,它不会输出,因此在32位带符号的表示法中没有第二个零(这就是为什么它比我的和Erik的答案短得多的原因)。
乔纳森·艾伦

其实,当是产生(-inf+nanj)
乔纳森·艾伦

...啊,我想解决了,您使用的是签名选项?
乔纳森·艾伦

4

Java,... 194191186 字节

static int f(int n){char[] c=Integer.toBinaryString(n).toCharArray();int j=0,o=2^32-2,i=c.length,l=i-1;if(n<0|n>o)return 0;for(;j<2&i>0;j+=c[--i]==48?1:0);if(j==2)return l-i;return 0;}

-159字节,用于使用较小的变量名并除去空白
,在获取更短的变量之后,还要感谢@KevinCruijssen提示
-18字节,更多的空格,函数名
-3字节,感谢@KevinCruijssen,如果条件的话则缩短
-5字节,感谢@Arnold Palmer,@ KevinCruijssen,缩短了循环

不打高尔夫球

public static int getPosSecondZero2(int number){
    int overflow = 2^32-2;
    if(number < 0 || number > overflow){
        return 0;
    }    
    String binaryString = Integer.toBinaryString(number);   
    char[] binaryCharArray = binaryString.toCharArray();    
    int count = 0;
    int idx = binaryCharArray.length;
    int length = binaryCharArray.length -1;
    while(count < 2 && idx>0){
        idx--;
        if(binaryCharArray[idx] == '0'){
            count++;
        }   
    }
    if(count == 2)
        return length-idx;
    return 0;
}

欢迎来到PPCG!您可以打高尔夫的很多事情:static 可以被删除;if(n<0||n>o){return 0;}可以是if(n<0|n>o)return 0;|而不是||没有括号);bs,,bsa等都可以是单个字符(在代码高尔夫球中请勿使用多字节变量/方法名称);您可以组合int,如下所示:int o=2^32-2,c=0,i=x.length,l=i-1;。高尔夫还有更多的东西。在Java高尔夫技巧,并在所有的语言技巧高尔夫可能是有趣的阅读。再次欢迎您,并祝您逗留愉快!:)
Kevin Cruijssen '17

我认为您仍然可以在变量声明中删除几个空格。欢迎!:)
musicman523

@ musicman523,谢谢,是的。现在是194 :)
0x45

1
if(c[i]=='0'){j++;}仍然可以打if(c[i]==48)j++;-3字节:)编辑:或者更好的是:while(j<2&&i>0){i--;if(c[i]=='0'){j++;}}可以for(;j<2&i>0;j+=c[i--]==48?1:0);为-8字节。
凯文·克鲁伊森

1
@ 0x45我相信如果您将@KevinCruijssen的代码更改为for(;j<2&i>0;j+=c[--i]==48?1:0);它应该可以工作。错误是i由于字符串的长度引起的,因此,最初,您尝试索引超出数组边界的位置。如果您执行减量操作(如更新的代码段所示),那么第一次使用它时,它将c[c.length-1]像原始代码一样进行访问。
阿诺德·帕尔默


3

IA-32机器代码,14 13字节

十六进制转储:

F7 D1 0F BC C1 0F B3 C1 0F BC C9 91 C3

拆卸清单:

0:  f7 d1                   not    ecx
2:  0f bc c1                bsf    eax,ecx
5:  0f b3 c1                btr    ecx,eax
8:  0f bc c1                bsf    ecx,ecx
b:  91                      xchg   eax, ecx
c:  c3                      ret

在中接收输入ecx; 输出在中al。错误返回0。

首先,它反转输入,因此它可以使用位扫描指令查找设置的位。它寻找最低有效置位,将其复位,再次寻找最低有效置位,并返回结果。

如果位扫描指令找不到任何设置的位,则英特尔文档说输出未定义。但是,实际上,在这种情况下,所有处理器都不会更改目标寄存器(如Cody Gray所指出,AMD文档将这种行为描述为强制性的)。

因此,有以下几种情况:

  1. 无零位(二进制111 ... 1):ecx由设置为0 not并保持为0
  2. 一个零位:ecx被设置为0,btr并在之后保持为0bsf
  3. 两个零位:ecx设置为适当的值 bsf

只有Intel的文档说未定义对0的位扫描。AMD的文档明确记录了目的地未更改。如果要避免这种行为,通常可以添加REP前缀来获取LZCNT或TZCNT,但这会增加字节数,这对于代码高尔夫自然是不可取的。
科迪·格雷

1
您实际上在这里做了太多工作。挑战不要求您区分没有零位的情况和只有1个零位的情况。在两种情况下,它都允许您返回0(或任何负值)。所以,虽然1字节SALC+ DEC非常聪明的,你可以只使用无论是在剃去一个字节ECX作为第二的BSF指令。唯一需要的是一个1字节XCHG的结果,EAX以便可以将其返回。换句话说,not ecx; bsf eax, ecx; btr ecx, eax; bsf ecx, ecx; xchg eax, ecx; ret
Cody Gray

1
这是上述的“在线尝试”链接。由于您将其ECX用作输入寄存器,因此我们需要告诉GCC使用fastcall调用约定。
科迪·格雷

2

Dyalog APL,20个字节

{2⊃(⍳32)/⍨~⌽⍵⊤⍨32⍴2}

使用1索引,INDEX ERROR如果没有第二个零则抛出。

怎么样?

⍵⊤⍨-编码

32⍴2 -长度为32的二进制字符串

- 逆转

~ -取反(0→1,1→0)

(⍳32)/⍨ -压缩范围为1-32(保留零索引)

2⊃ -选择第二个元素


您可以使用Where()节省大量字节
-TwiNight

@TwiNight我使用dyalog 14
Uriel

如果需要,TIO拥有Dyalog 16
TwiNight


1

果冻,12 字节

,4BUFḣ32¬TḊḢ

使用无符号选项的单数链接,使用整数并返回1索引的结果(如果不存在,则返回0)。

在线尝试!

要么

32Ḷ2*|€n⁸TḊḢ

试试看

怎么样?

1。

,4BUFḣ32¬TḊḢ - Link: number, n   e.g. 14
,4           - pair with 4            [14,4]
  B          - to binary              [[1,1,1,0],[1,0,0]]
   U         - upend                  [[0,1,1,1],[0,0,1]]
    F        - flatten                [0,1,1,1,0,0,1]
     ḣ32     - head to 32             [0,1,1,1,0,0,1] (truncates the right if need be)
        ¬    - not (vectorises)       [1,0,0,0,1,1,0]
         T   - truthy indexes         [1,5,6]
          Ḋ  - dequeue                [5,6]
           Ḣ - head                   5
             -   if the dequeued list is empty the head yields 0

2。

32Ḷ2*|€n⁸TḊḢ - Link: number, n   e.g. 14
32Ḷ          - lowered range of 32    [ 0, 1, 2, 3, 4, 5, ...,31]
   2*        - 2 exponentiated        [ 1, 2, 4, 8,16,32, ...,2147483648]
     |€      - bitwise or for €ach    [15,14,14,14,30,46, ...,2147483662]
        ⁸    - chain's right argument 14
       n     - not equal?             [ 1, 0, 0, 0, 1, 1, ..., 1]
         T   - truthy indexes         [ 1, 5, 6, ..., 32]
          Ḋ  - dequeue                [ 5, 6, ..., 32]
           Ḣ - head                   5
             -   if the dequeued list is empty the head yields 0

1

x86_64机器码,34 32字节

不知道这是否是正确的方法,有很多字节(结果是不是):

31 c0 83 c9 ff 89 fa 83 e2 01 83 f2 01 01 d1 7f 09 ff c0 d1 ef eb ee 83 c8 ff 83 f8 1f 7f f8 c3

在线尝试!

second_zero:
  # Set eax = 0
  xor  %eax, %eax
  # Set ecx = -1
  xor %ecx,%ecx
  not %ecx

  # Loop over all bits
Loop:
  # Get current bit
  mov %edi, %edx
  and $0x1, %edx
  # Check if it's zero and possibly increment ecx
  xor $0x1, %edx
  add %edx, %ecx
  # If ecx > 0: we found the position & return
  jg Return
  # Increment the position
  inc %eax
  # Shift the input and loop
  shr %edi
  jmp Loop

Fix:
  # If there's not two 0, set value to -1
  xor %eax,%eax
  not %eax

Return:
  # Nasty fix: if position > 31 (e.g for -1 == 0b11..11)
  cmp $31, %eax
  jg  Fix

  ret

感谢@CodyGray提供的-2字节数。


1
对于代码高尔夫或现实世界,遍历所有位可能不是正确的方法。真正的突破将是使用,让您一次处理所有的32(或64)位的指令之一,像BSFBSRPOPCNTBT,等Anatolyg已提交沿着这些路线的解决方案。我尚未确定是否可以击败。:-p
Cody Gray

1
顺便说一句,将寄存器设置为-1的潜在有用的代码搜寻技巧是将其与-1或。例如,or ecx, -1。这是3个字节,比XOR + NEG短1个字节。当打高尔夫球时,这不是一个好技巧,因为它在目标寄存器上引入了错误的读取依赖关系,但是您只需要使用mov ecx, -1并花费5个字节。
科迪·格雷

1

8th,149字节

2 base swap >s nip decimal s:len 32 swap n:- ( "0" s:<+ ) swap times s:rev null s:/ a:new swap ( "0" s:= if a:push else drop then ) a:each swap 1 a:@

注释代码

: f \ n -- a1 a2 n 

  \ decimal to binary conversion
  2 base swap >s nip decimal     

  \ 32bit formatting (padding with 0)            
  s:len 32 swap n:- ( "0" s:<+ ) swap times  

  \ put reversed binary number into an array 
  s:rev null s:/

  \ build a new array with position of each zero 
  a:new swap ( "0" s:= if a:push else drop then ) a:each

  \ put on TOS the position of the 2nd least least-significant zero digit
  swap 1 a:@
;

用法和输出

ok> : f 2 base swap >s nip decimal s:len 32 swap n:- ( "0" s:<+ ) swap times s:rev null s:/ a:new swap ( "0" s:= if a:push else drop then ) a:each swap 1 a:@ ;

ok> [0, 1, 10, 11, 12, 23, -1, -2, -4, -5, -9, 2147483646]

ok> ( dup . " -> " .  f . 2drop cr ) a:each
0 -> 1
1 -> 2
10 -> 2
11 -> 4
12 -> 1
23 -> 5
-1 -> null
-2 -> null
-4 -> 1
-5 -> null
-9 -> null
2147483646 -> 31

0

R,66个字节

w=which.min
x=strtoi(intToBits(scan()));w(x[-w(x)])*any(!x[-w(x)])

从stdin读取;不返回0第二个零,否则不返回。

在线尝试!

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.