一个数字是多少?


47

古希腊人把这些东西称为单双偶数。单个偶数的示例是14。它可以被2除以一次,并且在那一刻成为奇数(7),此后不再可以被2整除。双偶数是20。可以将其除以2两次,然后变为5。

您的任务是编写一个函数或程序,该函数或程序将整数作为输入,并输出可被整数整除2的次数,并以尽可能少的字节数输出。输入将为非零整数(在您的语言范围内,任何正值或负值)。

测试用例:

14 -> 1

20 -> 2

94208 -> 12

7 -> 0

-4 -> 2

字节数最少的答案将获胜。

提示:尝试将数字转换为基数2。请参阅提示


11
@AlexL。您也可以看看它永远不会变得奇怪,甚至无限。如果允许堆栈溢出,我可以节省一些字节;)
Geobits,2016年

1
The input will be a nonzero integer在您评论零为潜在输入之后,是否需要对此进行编辑?
trichoplax

2
这称为2-adic估值或2-adic顺序。
保罗

7
顺便说一下,根据Wikipedia所述,p-adic值为0定义为无穷大。
保罗,

3
真是个奇怪的问题!
corsiKa '16

Answers:


23

果冻,4 字节

Æfċ2

在最新版本的Jelly中,ÆEḢ(3字节)有效。

Æf      Calculate the prime factorization. On negative input, -1 appended to the end.
  ċ2    Count the 2s.

在这里尝试。


这也适用于负输入。
lirtosiast

1
@ThomasKwa我不认为这很重要。也许是个元问题?
orlp 2016年

ÆEḢ还好吗?实际上,它为奇数输出0。
busukxuan

@busukxuan对于±1无效。
lirtosiast

1
@Tyzoid Jelly 默认情况下在脱机解释器上使用其自己的代码页,其中一个字符为一个字节。
lirtosiast '16

93

x86_64机器码,4个字节

BSF(正向位扫描)指令正是这样做的

0x0f    0xbc    0xc7    0xc3

在gcc样式的汇编中,这是:

    .globl  f
f:
    bsfl    %edi, %eax
    ret

根据标准的64位调用约定,输入在EDI寄存器中提供,并在EAX寄存器中返回。

由于二进制补码二进制编码,因此它适用于-ve和+ ve数字。

此外,尽管文档中说“如果源操作数的内容为0,则目标操作数的内容未定义”。,我在Ubuntu VM上发现输出f(0)为0。

说明:

  • 将上面的另存为evenness.s并组装gcc -c evenness.s -o evenness.o
  • 将以下测试驱动程序另存为evenness-main.c并使用进行编译gcc -c evenness-main.c -o evenness-main.o
#include <stdio.h>

extern int f(int n);

int main (int argc, char **argv) {
    int i;

    int testcases[] = { 14, 20, 94208, 7, 0, -4 };

    for (i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) {
        printf("%d, %d\n", testcases[i], f(testcases[i]));
    }

    return 0;
}

然后:

  • 链接: gcc evenness-main.o evenness.o -o evenness
  • 跑: ./evenness

@FarazMasroor要求提供有关如何得出此答案的更多详细信息。

我对的理解比对x86汇编的复杂性更熟悉,因此通常我使用编译器为我生成汇编代码。我知道从经验中的gcc扩展如__builtin_ffs()__builtin_ctz()__builtin_popcount()典型地编译和组装在x86 1点或2的指令。所以我从一个函数开始:

int f(int n) {
    return __builtin_ctz(n);
}

您可以使用该-S选项仅将其编译为Assembly- ,而不必始终使用常规的gcc编译来生成目标代码gcc -S -c evenness.c。这给出了如下的汇编文件evenness.s

    .file   "evenness.c"
    .text
    .globl  f
    .type   f, @function
f:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    %edi, -4(%rbp)
    movl    -4(%rbp), %eax
    rep bsfl    %eax, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   f, .-f
    .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
    .section    .note.GNU-stack,"",@progbits

其中很多可以打出去。特别是,我们知道具有签名的函数的 调用约定int f(int n);非常好而且很简单-输入参数在EDI寄存器中传递,返回值在EAX寄存器中返回。因此,我们可以删除大部分指令-其中许多指令与保存寄存器和设置新的堆栈框架有关。我们在这里不使用堆栈,而仅使用EAX寄存器,因此无需担心其他寄存器。这留下了“ golfed”汇编代码:

    .globl  f
f:
    bsfl    %edi, %eax
    ret

请注意,正如@zwol指出的那样,您也可以使用优化的编译来获得类似的结果。特别是-Os完全生成上述指令(带有一些不会产生任何额外目标代码的附加汇编程序指令。)

现在gcc -c evenness.s -o evenness.o,将其与组装在一起,然后可以将其链接到如上所述的测试驱动程序中。

有几种方法可以确定与此程序集相对应的机器代码。我最喜欢的是使用gdb disass反汇编命令:

$ gdb ./evenness
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
...
Reading symbols from ./evenness...(no debugging symbols found)...done.
(gdb) disass /r f
Dump of assembler code for function f:
   0x00000000004005ae <+0>: 0f bc c7    bsf    %edi,%eax
   0x00000000004005b1 <+3>: c3  retq   
   0x00000000004005b2 <+4>: 66 2e 0f 1f 84 00 00 00 00 00   nopw   %cs:0x0(%rax,%rax,1)
   0x00000000004005bc <+14>:    0f 1f 40 00 nopl   0x0(%rax)
End of assembler dump.
(gdb) 

因此,我们可以看到,对于机器代码bsf指令是0f bc c7retc3


我们不算为2吗?
lirtosiast '16

2
如何学习机器语言/字节转储代码?不能在网上找到任何东西
法拉兹Masroor

1
这不满足C调用约定。在x86-32上,参数在堆栈上传递;在x86-64上,参数在%rdi中传递。它似乎仅在您的测试工具中起作用,因为您的编译器恰巧在%eax中留下了该参数的陈旧副本。如果evenness-main.c使用不同的优化设置编译线束,它将破坏。对我来说,有突破-O-O2-O3
Anders Kaseorg'2

1
@AndersKaseorg-感谢您指出这一点。我现在仅将其限制为x86_64,因此输入来自RDI。
Digital Trauma

3
“而且,尽管文档说[...]” -您获得的任何价值都必须与文档一致。这并不排除其他处理器型号提供与您的处理器不同的价值。
hvd

25

Python,25个字节

lambda n:len(bin(n&-n))-3

n & -n 将除最低有效位以外的任何东西都清零,例如:

100010101010100000101010000
            v
000000000000000000000010000

我们对尾随零的数量感兴趣,因此我们使用来将其转换为二进制字符串bin,对于上述数字将为"0b10000"。由于我们既不在乎0b,也不在乎,因此1从该字符串长度中减去3。


发布答案后,我发现您的答案非常聪明,因此我尝试将其转换为Pyth,以查看您的答案是否比我的短。它使用log2而不是len(bin(_))产生l。&Q_Q。它的长度与我的Pyth答案以及另一个Pyth答案的长度相同,似乎这在Pyth中不会少于6个字节...
busukxuan

21

Pyth,6个字节

/P.aQ2

在这里尝试。

 P.aQ         In the prime factorization of the absolute value of the input
/    2        count the number of 2s.


13

JavaScript ES6,22 19字节

f=x=>x%2?0:f(x/2)+1

看起来递归是最短的路线。


哦,不!你打我!干得漂亮:) +1
康纳贝尔

6

Pyth,8个字节

lec.BQ\1
     Q    autoinitialized to eval(input())
   .B     convert to binary string
  c   \1  split on "1", returning an array of runs of 0s
 e        get the last run of 0s, or empty string if number ends with 1
l         take the length

例如,的二进制表示形式94208是:

10111000000000000

1s上拆分并获取结果数组的最后一个元素后,将变为:

000000000000

那是12个零,所以它是“ 12位数偶数”。

之所以可行,是因为x / 2从本质上来说,x >> 1即的右移1。因此,仅当LSB为1时,数字才能被2整除0(就像十进制数的最后一位为时,十进制数字可以被10整除一样0)。


6

05AB1E4 5字节

现在支持负数。码:

Äb1¡g

在线尝试!

说明:

Ä      # Abs(input)
 b     # Convert the number to binary
  1¡   # Split on 1's
    g  # Take the length of the last element

使用CP-1252编码。


6

Pyth,6个字节

x_.BQ1

基本上只是

convert2BinString(evaluatedInput())[::-1].index("1")

6

MATL,5个字节

Yf2=s

这适用于所有整数。

在线尝试!

Yf      % implicit input. Compute (repeated) prime factors. For negative input
        % it computes the prime factors of the absolute value, except that for
        % -1 it produces an empty array instead of a single 1
2=s     % count occurrences of "2" in the array of prime factors

“现在,换个完全不同的东西……”
烧杯,

6

C,36(28)个字节

int f(int n){return n&1?0:f(n/2)+1;}

(未测试零参数,因为指定了非零参数。)

更新(响应评论):如果我们允许K&R样式函数声明,那么我们可以使用28字节的版本:

f(n){return n&1?0:f(n/2)+1;}

在这种情况下,我们依赖于这样一个事实,即编译器默认使用默认值,n并且返回类型fint。但是,此表单会使用C99生成警告,并且不会编译为有效的C ++代码。


如果更改int n-> n它仍然是有效的C代码,并且会截断4个字符。
乔什

好点子。我要说的是,这至少会触发C99警告,但省略返回类型也是如此。两者都触发C ++中的错误。因此,我正在适当地更改答案。
维克多·托斯

5

Java 7、39或44字节

int s(int a){return a%2!=0?0:s(a/2)+1;}

int s(int a){return a%2!=0|a==0?0:s(a/2)+1;}

耶递归!我必须使用a !=而不是较短的比较,这样它就不会在负输入上溢出,但除此之外,它非常简单。如果很奇怪,请发送零。如果均匀,则添加一个并再次执行。

有两个版本,因为目前未知的输出为零。第一个将递归直到堆栈溢出,并且什么也不输出,因为0是无限偶数。第二个输出一个不错的,安全的但可能不是数学严格的0来输出。


4

JavaScript(ES6),20字节 19字节。

f=x=>~x%2&&1+f(x/2)

这是@nimi将Haskell解决方案移植到JavaScript的端口。它使用“短路”属性,&&如果它为false(在本例中为-0),则返回其左侧;否则返回其右侧。odd x = 0因此,要实现该目标,我们使1 - (x % 2)气泡的左侧0穿过&&,否则我们递归到1 + f(x / 2)

1 - (x % 2)as 的剃须(~x) % 2归因于下面的@Neil,并具有奇怪的属性,导致上面的函数以-0较小的奇数发出。这个值是JS决定整数是IEEE754倍数的特性。该系统具有独立的+0-0它们是特例,在JavaScript为===彼此。的~操作者计算该数目,这对于小的奇数将是一个负偶数32位签名整数按位反转。(Math.pow(2, 31) + 1例如,正数产生0而不是-0。)对32位有符号整数的奇怪限制没有任何其他作用;它只对数字有效。特别是它不会影响正确性。


~x&11-x%2。短一个字节。
尼尔

@Neil非常酷。那有点违反直觉,但我还是会考虑的。
CR Drost

4

Perl 6中,23 18个字节

{+($_,*/2...^*%2)}

用法

> my &f = {+($_,*/2...^*%2)}
-> ;; $_? is raw { #`(Block|117104200) ... }
> f(14)
1
> f(20)
2
> f(94208)
12
> f(7)
0
> f(-4)
2

4

Ruby 24字节

我的第一个代码高尔夫球提交(是!)

("%b"%$*[0])[/0*$/].size

我如何到达这里

首先,我想获取实际上符合规范的代码来解决这个问题,因此我构建了一种方法,而无需考虑字节数:

def how_even(x, times=1)
  half = x / 2
  if half.even?
    how_even(half, times+1)
  else
    times
  end
end

以此知识,我将该函数递归为while循环,并添加$*(ARGV)作为输入,i作为该数字在变成奇数之前减半的次数的计数。

x=$*[0];i=1;while(x=x/2)%2<1;i+=1;end;i

我为此感到非常自豪,并且几乎在不知不觉中就提交了这篇文章,这让我震惊,因为我是软件工程师,但对计算机科学家却不是那么多,这并不是我想到的第一件事。

因此,我收集了一些有关二进制输入值的结果:

input      in binary      result
---------------------------------
   14               1110   1
   20              10100   2
94208  10111000000000000  12

我注意到结果是在数字变为奇数之前我们必须遍历的左侧位置数。

做一些简单的字符串操作,我在最后一次出现1时拆分了字符串,并计算了剩余0的长度:

("%b"%$*[0])[/0*$/].size

使用("%b" % x)格式将数字转换为二进制,然后使用String#slice分割我的字符串。

在此任务中,我已经学到了一些有关红宝石的知识,并期待不久后再打高尔夫球!


2
欢迎使用编程难题和Code Golf Stack Exchange。这是一个很好的答案。我真的很喜欢这个解释。+1!如果您想要更多的代码高尔夫挑战,请单击代码高尔夫标签。我期待看到您的更多答案。
wizzwizz4 '16

1
随时问我任何问题。类型@wizzwizz4在注释的开始回复我。(这适用于所有用户名!)
wizzwizz4 '16

4

J,6个字节

1&q:@|

说明:

     |    absolute value
1&q:      exponent of 2 in the prime factorization

4

C,37个字节

f(int x){return x?x&1?0:1+f(x/2):0;} 递归检查最后一位,直到它不是0。


另外,还有f(int n){return __builtin_ctz(n);}您是否愿意使用gcc扩展名。甚至#define f __builtin_ctz
数字创伤

删除int 。它是隐式的,就像返回类型一样。
luser droog 2016年

@luserdroog,你的意思是f(n){...}?GCC不会编译它。我不是C专家,但快速搜索后发现,此功能可能已在C的最新版本中删除。因此,它可能会使用适当的标志进行编译?
安迪·索弗

@AndySoffer,我明白了。也许-ansi还是-gnu99?我知道我已经开始工作了。我为此写了一个提示答案!
luser droog

3

Haskell,28个字节

f x|odd x=0|1<2=1+f(div x 2)

用法示例:f 94208-> 12

如果数字为奇数,则结果为0,否则1加上数字一半的递归调用。


div x 2?为什么不x/2呢?
CalculatorFeline

@CatsAreFluffy:Haskell具有非常严格的类型系统。div是整数除法,/浮点除法。
nimi

3

Befunge,20岁

&:2%#|_\1+\2/#
   @.<

代码执行将一直向右移动并环绕到第一行的第二个字符(由于尾随#),直到2%输出1,这导致_将方向切换为从左向右,然后再|向上切换至<第二行的,输出和退出。每次通过循环,我们都会递增堆栈的第二个到最上层元素,然后将最上层元素除以2。


3

视网膜29 17

+`\b(1+)\1$
;$1
;

在线尝试!

感谢马丁,节省了2个字节!

接受一元输入。这会反复匹配最大的1s,从而使1s的数量与该数量中s的其余部分完全匹配1。每次执行此操作时;,都会在字符串前加上a 。最后,我们计算;字符串中s 的数量。

如果要十进制输入,请添加:

\d+
$0$*1

到程序的开始。


3

Jolf,6个字节

在这里尝试!

Zlm)j2
Zl   2  count the number occurrences of 2 in
  m)j   the prime factorization of j (input)

相当简单...对ETHProductions赞誉不已,Jolf用了真正应该起作用的版本!


1
6字节似乎是此挑战的魔幻数字
Cyoce


3

6502机器语言,7个字节

要在累加器中查找非零值的最低有效1位的位置值,并将结果保留在寄存器X中:

A2 FF E8 4A 90 FC 60

要在运行此6502模拟器上e-tradition.net,使用前缀与A9随后的8位整数。

这将分解为以下内容:

count_trailing_zeroes:
    ldx #$FF
loop:
    inx
    lsr a     ; set carry to 0 iff A divisible by 2, then divide by 2 rounding down
    bcc loop  ; keep looping if A was divisible by 2
    rts       ; return with result in X

这等效于以下C,不同之处在于C必须int至少为16位:

unsigned int count_trailing_zeroes(int signed_a) {
    unsigned int carry;
    unsigned int a = signed_a;  // cast to unsigned makes shift well-defined
    unsigned int x = UINT_MAX;
    do {
        x += 1;
        carry = a & 1;
        a >>= 1;
    } while (carry == 0);
    return x;
}

假设MX = 01(16位累加器,8位索引),在65816上也是如此,并且等效于上述C代码段。


2

Brachylog27 15字节

$pA:2xlL,Al-L=.

说明

$pA             § Unify A with the list of prime factors of the input
   :2x          § Remove all occurences of 2 in A
      lL,       § L is the length of A minus all the 2s
         Al-L=. § Unify the output with the length of A minus L

2

CJam,8个字节

rizmf2e=

读取整数,绝对值,素数分解,计数二。


2

JavaScript ES6,36 38字节

感谢@ETHproductions打了两个字节

答案很无聊,但却能胜任。可能实际上与另一个答案过于相似,如果他添加了建议的更改,那么我将删除我的。

b=>{for(c=0;b%2-1;c++)b/=2;alert(c)}

要运行,请将其分配给a=>{for...作为匿名函数的变量(),然后使用进行调用a(100)


好答案!b%2==0可以更改为b%2-1,并且c++可以在for语句的最后一部分内移动。我认为这也将起作用:b=>eval("for(c=0;b%2-1;b/=2)++c")
ETHproductions 2016年

@ETHproductions可以!不错的收获:)
康纳·贝尔

还有一个字节:b%2-1=> ~b&1另外,我认为这在输入时失败0,可以用b&&~b&1
ETHproductions

冻结我的计算机以负数测试。b%2-1负奇数检查失败。
帕特里克·罗伯茨


2

DUP,20字节

[$2/%0=[2/f;!1+.][0]?]f:

Try it here!

转换为递归后,输出现在是堆栈上的头号。用法:

94208[2/\0=[f;!1+][0]?]f:f;!

说明

[                ]f: {save lambda to f}
 2/\0=               {top of stack /2, check if remainder is 0}
      [     ][ ]?    {conditional}
       f;!1+         {if so, then do f(top of stack)+1}
              0      {otherwise, push 0}

2

Japt,9个 5字节

¢w b1

在线测试!

以前的版本应该是五个字节,但是这个实际上可以工作。

这个怎么运作

       // Implicit: U = input integer
¢      // Take the binary representation of U.
w      // Reverse.
b1     // Find the first index of a "1" in this string.
       // Implicit output

2

C,44 40 38 36字节

感谢@JohnWHSmith减少 2个字节。2个字节的感谢@luserdroog

a;f(n){for(;~n&1;n/=2)a++;return a;}

ideone上进行实时测试。


通过用!(n%2)一个漂亮的小东西代替昂贵的东西,您也许可以减少1个字节~n&1
约翰·史密斯

@JohnWHSmith。很好!谢谢
删除

删除=0。全局隐式初始化为0
luser楚格设计

@luserdroog。谢谢,我对此一无所知。
删除

如果我错了,请纠正我,但是由于此函数使用了全局变量a,难道不是只能保证在第一次调用它时起作用吗?我不知道那是允许的。
帕特里克·罗伯茨
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.