Quão par é um número?


47

Os gregos antigos tinham essas coisas chamadas números individuais e duplamente pares. Um exemplo de um número par é 14. Ele pode ser dividido por 2 uma vez e nesse ponto se tornou um número ímpar (7), após o qual não é mais divisível por 2. Um número duplamente uniforme é 20. Ele pode ser dividido por 2 duas vezes e depois se torna 5.

Sua tarefa é escrever uma função ou programa que use um número inteiro como entrada e produza o número de vezes que é divisível por 2 como número inteiro, no menor número possível de bytes. A entrada será um número inteiro diferente de zero (qualquer valor positivo ou negativo, dentro dos limites do seu idioma).

Casos de teste:

14 -> 1

20 -> 2

94208 -> 12

7 -> 0

-4 -> 2

A resposta com o mínimo de bytes vence.

Dica: Tente converter o número na base 2. Veja o que isso diz.


11
@AlexL. Você também pode ver que nunca está se tornando estranho, tão infinitamente uniforme. I pode economizar alguns bytes se um excesso de pilha é permitido;)
Geobits

1
The input will be a nonzero integerIsso precisa ser editado após o seu comentário sobre zero como uma entrada potencial?
Trichoplax

2
Isso é chamado de avaliação 2-adic ou ordem 2-adic.
Paul

7
A propósito, de acordo com a Wikipedia, a avaliação p-adic de 0 é definida como infinito.
Paul

3
Que pergunta estranha!
CorsiKa

Respostas:


23

Gelatina , 4 bytes

Æfċ2

Na versão mais recente do Jelly, ÆEḢ(3 bytes) funciona.

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

Experimente aqui .


Isso funciona também para entrada negativa.
21816 Lirtosiast

1
@ThomasKwa Eu não acho que isso conta. Talvez uma meta pergunta?
orlp 12/02

ÆEḢ não está bem? Na verdade, gera 0 para números ímpares.
busukxuan

@busukxuan Não funciona para ± 1.
lirtosiast

1
O @Tyzoid Jelly usa sua própria página de código no interpretador offline por padrão, na qual um caractere é um byte.
lirtosiast

93

código de máquina x86_64, 4 bytes

A instrução BSF (bit scan forward) faz exatamente isso !

0x0f    0xbc    0xc7    0xc3

Na montagem no estilo gcc, é:

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

A entrada é fornecida no registro EDI e retornada no registro EAX de acordo com as convenções padrão de chamada 64 bits .

Por causa da codificação binária do complemento de dois, isso funciona para os números -ve e + ve.

Além disso, apesar da documentação dizendo "Se o conteúdo do operando de origem for 0, o conteúdo do operando de destino será indefinido". , Acho na minha VM do Ubuntu que a saída f(0)é 0.

Instruções:

  • Guarde o que precede evenness.se monte comgcc -c evenness.s -o evenness.o
  • Salve o seguinte driver de teste evenness-main.ce compile com 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;
}

Então:

  • Ligação: gcc evenness-main.o evenness.o -o evenness
  • Corre: ./evenness

O @FarazMasroor pediu mais detalhes sobre como essa resposta foi obtida.

Eu estou mais familiarizado com que com os meandros do assembly x86, então normalmente eu uso um compilador para gerar código de assembly para mim. Eu sei por experiência que as extensões do CCG como __builtin_ffs(), __builtin_ctz()e__builtin_popcount() tipicamente compilar e montar a 1 ou 2 instruções sobre x86. Então eu comecei com uma função como:

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

Em vez de usar a compilação gcc regular até o código do objeto, você pode usar a -Sopção para compilar apenas para montagem - gcc -S -c evenness.c. Isso fornece um arquivo de montagem evenness.scomo este:

    .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

Muito disso pode ser jogado fora. Em particular, sabemos que a convenção de chamada para funções com assinatura é agradável e simples - o parâmetro de entrada é passado no registrador e o valor retornado é retornado no registrador. Portanto, podemos tirar a maioria das instruções - muitas delas estão preocupadas em salvar registros e configurar um novo quadro de pilha. Nós não usamos a pilha aqui e apenas o registro, portanto, não precisa se preocupar com outros registros. Isso deixa o código de montagem "golfed":int f(int n);EDIEAXEAX

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

Observe como o @zwol aponta, você também pode usar a compilação otimizada para obter um resultado semelhante. Em particular, -Osproduz exatamente as instruções acima (com algumas diretivas de montagem adicionais que não produzem nenhum código de objeto extra).

Agora isso é montado gcc -c evenness.s -o evenness.o, o qual pode ser vinculado a um programa de driver de teste, conforme descrito acima.

Existem várias maneiras de determinar o código da máquina correspondente a esta montagem. Meu favorito é usar o disasscomando gdb disassembly:

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

Portanto, podemos ver que o código da máquina para a bsfinstrução é 0f bc c7e para reté c3.


Não contamos isso como 2?
lirtosiast

2
Como eu aprendo o código de despejo de Machine Machine / Byte? Não consegue encontrar qualquer coisa online
Faraz Masroor

1
Isso não satisfaz a convenção de chamada C. No x86-32, o argumento é passado na pilha; em x86-64, o argumento é passado em% rdi. Parece funcionar apenas dentro do seu equipamento de teste porque, por acaso, seu compilador deixou uma cópia antiga do argumento em% eax. Ele quebrará se você compilar o chicote evenness-main.ccom diferentes configurações de otimização; para mim ele quebra com -O, -O2ou -O3.
Anders Kaseorg 13/16

1
@ AndersKaseorg - obrigado por apontar isso. Eu o restrinjai apenas a x86_64 agora, para que a entrada seja fornecida no RDI.
Digital Trauma

3
"Além disso, apesar da documentação [...] indicar" - Qualquer valor que você obtiver concorda necessariamente com a documentação. Isso não exclui outros modelos de processadores que fornecem um valor diferente do seu.
HVD

25

Python, 25 bytes

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

n & -n zera qualquer coisa, exceto o bit menos significativo, por exemplo:

100010101010100000101010000
            v
000000000000000000000010000

Como estamos interessados ​​no número de zeros à direita, convertemos para uma string binária usando bin, o que para o número acima será "0b10000". Como não nos importamos com o 0b, nem o 1, subtraímos 3 desse comprimento das strings.


depois de postar minha resposta, achei que a sua era muito inteligente, então tentei convertê-la para Pyth e ver se a sua era mais curta que a minha. Rendeu l. & Q_Q, usando log2 em vez de len (bin (_)). Foi o mesmo comprimento que a minha resposta Pyth, bem como uma outra resposta Pyth, parece que este não fica mais curto do que 6 bytes em Pyth ...
busukxuan

21

Pitão, 6 bytes

/P.aQ2

Experimente aqui .

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

15

JavaScript (ES6), 18 bytes

n=>Math.log2(n&-n)

4 bytes mais curtos que 31-Math.clz32. Hah.


1
Oh uau, e só recentemente aprendi sobre Math.clz32...
Neil

1
Porra, eu estava indo para postar exatamente isso! +1
Cyoce

13

JavaScript ES6, 22 19 bytes

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

Parece que a recursão é a rota mais curta.


Oh não! Você me venceu! Bem feito :) +1
Connor Bell

6

Pitão, 8 bytes

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

Por exemplo, a representação binária de 94208é:

10111000000000000

Após dividir em se 1pegar o último elemento da matriz resultante, isso se torna:

000000000000

São 12 zeros, então é "12-pares".

Isso funciona porque x / 2é essencialmente x >> 1- ou seja, um deslocamento de bits à direita 1. Portanto, um número é divisível por 2 somente quando o LSB estiver 0(assim como um número decimal é divisível por 10 quando seu último dígito for 0).


6

05AB1E , 4 5 bytes

Agora suporta números negativos. Código:

Äb1¡g

Experimente online!

Explicação:

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

Usa a codificação CP-1252.


6

Pitão, 6 bytes

x_.BQ1

Basicamente apenas

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

6

MATL , 5 bytes

Yf2=s

Isso funciona para todos os números inteiros.

Experimente online!

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

"E agora, para algo completamente diferente ..."
copo

6

C, 36 (28) bytes

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

(Não foi testado o argumento zero, pois foi especificado um argumento diferente de zero.)

Atualização (em resposta ao comentário) : se permitirmos declarações de função no estilo K&R, poderemos ter uma versão de 28 bytes:

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

Nesse caso, contamos com o fato de o compilador usar como padrão ambos ne o tipo de retorno de fcomo int. Este formulário gera um aviso com C99 e não é compilado como código C ++ válido.


Se você alterar int n-> nainda é um código C válido e corta 4 caracteres.
Josh

Bom ponto. Eu diria que isso dispara pelo menos um aviso com C99, mas o mesmo ocorre com a omissão do tipo de retorno. E os dois disparam erros em C ++. Então, eu estou mudando minha resposta adequadamente.
Viktor Toth

5

Java 7, 39 ou talvez 44 bytes

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

Yay recursão! Eu tive que usar uma !=comparação em vez de uma comparação mais curta, para que ela não transbordasse com informações negativas, mas fora isso é bem simples. Se for estranho, envie um zero. Se for o caso, adicione um e faça-o novamente.

Existem duas versões porque, no momento, a saída para zero é desconhecida. O primeiro recursará até a pilha estourar e não produzir nada, porque 0 é infinitamente uniforme. O segundo cospe um 0 agradável, seguro, mas provavelmente não matematicamente rigoroso para a saída.


4

JavaScript (ES6), 20 bytes 19 bytes.

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

Esta é uma porta da solução Haskell de @nimi para JavaScript. Ele usa as propriedades de "curto-circuito" &&que retornam seu lado esquerdo se for falsey (o que neste caso é -0) ou então retornam seu lado direito. Para implementar odd x = 0, portanto, fazemos o lado esquerdo 1 - (x % 2)que borbulha 0através do &&, caso contrário, recorremos a 1 + f(x / 2).

A raspagem de 1 - (x % 2)as (~x) % 2é devida a @ Neil abaixo e tem a propriedade estranha que faz com que a função acima seja emitida -0para pequenos números ímpares. Esse valor é uma peculiaridade da decisão da JS de que números inteiros são dobra IEEE754; esse sistema possui um código separado +0e -0que é especial em JavaScript para serem ===um para o outro. O ~operador calcula a inversão bit a bit de número inteiro com sinal de 32 bits para o número, que para números ímpares pequenos será um número par negativo. (O número positivo, Math.pow(2, 31) + 1por exemplo, produz em 0vez de -0.) A restrição estranha aos números inteiros assinados de 32 bits não tem outros efeitos; em particular, não afeta a correção.


~x&1é um byte menor que 1-x%2.
Neil

@ Neil Muito legal. Isso tem uma propriedade um tanto contra-intuitiva, mas eu aceito de qualquer maneira.
CR Drost

4

Perl 6, 23 18 bytes

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

uso

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

Meu primeiro envio de código de golfe (sim!)

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

Como cheguei aqui :

Primeiro, eu queria obter um código que realmente atendesse às especificações para resolver o problema, então criei o método sem considerar o número de bytes:

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

com esse conhecimento, eu recursionei a função em um loop while e adicionei $*(ARGV) como entrada ei como a contagem de quantas vezes o número foi dividido pela metade antes que se tornasse ímpar.

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

Eu estava bastante orgulhoso disso e quase o enviei antes que me parecesse que toda essa divisão por dois parecia um pouco binária para mim, sendo um engenheiro de software, mas não tanto como um cientista da computação, essa não foi a primeira coisa que me veio à mente.

Então, reuni alguns resultados sobre como eram os valores de entrada em binário:

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

Percebi que o resultado foi o número de posições à esquerda que precisamos percorrer antes que o número se torne ímpar.

Fazendo algumas manipulações simples de sequência, divida a sequência na última ocorrência de 1 e contei o comprimento dos 0s restantes:

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

usando a ("%b" % x)formatação para transformar um número em binário e String # slice para dividir minha string.

Eu aprendi algumas coisas sobre o rubi nessa missão e estou ansioso por mais golfe em breve!


2
Bem-vindo à Programação de quebra-cabeças e troca de pilha de código de golfe. Esta é uma ótima resposta; Eu realmente gosto da explicação. +1! Se você quiser mais desafios de código-golfe, clique na tag código-golfe . Estou ansioso para ver mais de suas respostas.
Wizzwizz4

1
Sinta-se livre para me perguntar sobre qualquer dúvida que você tenha. Digite @wizzwizz4no início de um comentário para responder a mim. (Isso funciona com todos os nomes de usuário!)
wizzwizz4

4

J, 6 bytes

1&q:@|

Explicação:

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

4

C, 37 bytes

f(int x){return x?x&1?0:1+f(x/2):0;} Verifique recursivamente o último bit até que não seja um 0.


Além disso, existe f(int n){return __builtin_ctz(n);}se você estiver disposto a usar extensões gcc. Ou até mesmo#define f __builtin_ctz
Digital Trauma

Retire int . Está implícito, assim como o tipo de retorno.
Luser droog

@luserdroog, você quer dizer f(n){...}? O GCC não o compilará. Não sou especialista em C, mas uma pesquisa rápida revela que talvez esse recurso tenha sido removido nas versões mais recentes do C. Então, talvez ele seja compilado com os sinalizadores apropriados?
Andy Soffer

@AndySoffer eu vejo. Talvez -ansiou -gnu99? Eu sei que consegui funcionar. Eu escrevi uma resposta de dicas sobre isso!
Luser droog

3

Haskell, 28 bytes

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

Exemplo de uso: f 94208-> 12.

Se o número for ímpar, o resultado é 0, outra coisa 1além de uma chamada recursiva com metade do número.


div x 2? Por que não x/2?
CalculatorFeline

@CatsAreFluffy: Haskell possui um sistema de tipos muito rigorosos. divé divisão inteira, /divisão de ponto flutuante.
nimi

3

Befunge, 20

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

A execução do código continua se movendo para a direita e passando para o segundo caractere da primeira linha (graças à direita #) até as 2%saídas 1, o que faz _com que você mude a direção para a esquerda e depois |para cima, o que envolve <a segunda linha, quais saídas e saídas. Nós incrementamos o segundo da parte superior da pilha todas as vezes através do loop e, em seguida, dividimos a parte superior por 2.


3

Retina ,29 17

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

Experimente online!

2 bytes economizados graças ao Martin!

Recebe entrada unária. Isso corresponde repetidamente à maior quantidade de 1s possível, de modo que esse número de 1s corresponda exatamente ao restante dos 1s no número. Cada vez que faz isso, ele anexa ;a à string. No final, contamos o número de ;s na string.

Se você deseja entrada decimal, adicione:

\d+
$0$*1

para o início do programa.


3

Jolf, 6 bytes

Experimente aqui!

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

Bastante simples ... Parabéns ao ETHProductions por derrubar Jolf com a versão que realmente deve funcionar!


1
6 bytes parece ser o número mágico para este desafio
Cyoce


3

6502 linguagem de máquina, 7 bytes

Para encontrar o valor local do 1 bit menos significativo do valor diferente de zero no acumulador, deixando o resultado no registro X:

A2 FF E8 4A 90 FC 60

Para executar isso no simulador 6502 no e-tradition.net , prefixe-o A9seguido de um número inteiro de 8 bits.

Isso desmonta para o seguinte:

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

Isso é equivalente ao C a seguir, exceto que C precisa intter pelo menos 16 bits:

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

O mesmo funciona em um 65816, assumindo MX = 01 (acumulador de 16 bits, índice de 8 bits) e é equivalente ao snippet C acima.


2

Braquilog , 27 15 bytes

$pA:2xlL,Al-L=.

Explicação

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

rizmf2e=

Leia inteiro, valor absoluto, fatorize primo, conte dois.


2

JavaScript ES6, 36 38 bytes

Golpeou dois bytes graças a @ETHproductions

Resposta bastante chata, mas faz o trabalho. Na verdade, pode ser muito semelhante a outra resposta, se ele adicionar as alterações sugeridas, removerei as minhas.

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

Para executar, atribua-o a uma variável ( a=>{for...) como uma função anônima e, em seguida, chame-o com a(100).


Boa resposta! b%2==0pode ser alterado para b%2-1e c++pode ser movido para a última parte da forinstrução. Eu acho que isso também funcionaria:b=>eval("for(c=0;b%2-1;b/=2)++c")
ETHproductions

@ETHproductions Então pode! Prendedor agradável :)
Connor Sino

Mais um byte: b%2-1=> ~b&1Além disso, penso que esta falha na entrada de 0, o que pode ser corrigido comb&&~b&1
ETHproductions

Congelei meu computador testando isso em um número negativo. b%2-1a verificação falha para números ímpares negativos.
Patrick Roberts

2

ES6, 22 bytes

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

Retorna -1 se você passar 0.


Ah legal. Eu esqueci clz32: P
Conor O'Brien

2

DUP , 20 bytes

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

Try it here!

Convertida em recursão, a saída é agora o número superior na pilha. Uso:

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

Explicação

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

Japonês, 9 5 bytes

¢w b1

Teste online!

A versão anterior deveria ter cinco bytes, mas esta realmente funciona.

Como funciona

       // 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 bytes

2 bytes de desconto, graças a @JohnWHSmith . 2 bytes de desconto, graças a @luserdroog .

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

Teste ao vivo em ideone .


Você pode tirar 1 byte substituindo o caro !(n%2)por um pouco mais ~n&1.
John WH Smith

@JohnWHSmith. Aquilo foi legal!! Obrigado
removido

Retire o =0. Globals são implicitamente inicializado com 0.
luser Droog

@luserdroog. Obrigado, eu não sabia disso.
removido

Corrija-me se estiver errado, mas como essa função usa a variável global a, não é garantido que funcione apenas na primeira vez em que é chamada? Eu não sabia que isso era permitido.
Patrick Roberts
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.