Código da máquina x86-64, 22 bytes
48 B8 41 92 34 6D DB F7 FF FF 83 F9 40 7D 03 48 D3 E8 83 E0 01 C3
Os bytes acima definem uma função no código de máquina x86 de 64 bits que determina se o valor de entrada é um número do Chicken McNugget. O parâmetro inteiro positivo único é passado no ECX
registro, seguindo a convenção de chamada da Microsoft de 64 bits usada no Windows. O resultado é um valor booleano retornado no EAX
registro.
Mnemônicos de montagem não destruídos:
; bool IsMcNuggetNumber(int n)
; n is passed in ECX
movabs rax, 0xFFFFF7DB6D349241 ; load a 64-bit constant (the bit field)
cmp ecx, 64
jge TheEnd ; if input value >= 64, branch to end
shr rax, cl
TheEnd:
and eax, 1 ; mask off all but LSB
ret
Obviamente, isso se assemelha bastante à solução de Anders Kaseorg em Python , na medida em que é baseada em um campo de bits que representa os valores que são números de Chicken McNugget. Especificamente, cada bit nesse campo que corresponde a um número válido de Chicken McNugget é definido como 1; todos os outros bits são definidos como 0. (Isso considera 0 como um número válido do Chicken McNugget, mas se você não gosta disso, sua preferência é modificar um bit).
Começamos simplesmente carregando esse valor em um registro. É um valor de 64 bits, que já leva 8 bytes para codificar, além de precisarmos de um prefixo REX.W de um byte; portanto, estamos realmente perdendo o peso em termos de bytes, mas esse é o coração da solução. Eu acho que vale a pena.
Em seguida, deslocamos o campo para a direita pelo valor de entrada. * Finalmente, mascaramos tudo, exceto o bit de ordem mais baixa, e isso se torna nosso resultado booleano.
No entanto, como você não pode mudar mais do que o número de bits realmente no valor, isso funciona apenas para entradas de 0 a 63. Para oferecer suporte a valores de entrada mais altos, inserimos um teste na parte superior da função que se ramifica na parte inferior do valor de entrada é> = 64. A única coisa interessante sobre isso é que pré - carregamos a constante do campo de bits em RAX
e depois ramificamos até a instrução que mascara o bit de ordem mais baixa, garantindo assim que sempre retornemos 1.
Experimente online!
(A chamada de função C é anotada com um atributo que faz com que o GCC a chame usando a convenção de chamada da Microsoft que meu código de assembly usa. Se o TIO tivesse fornecido o MSVC, isso não seria necessário.)
__
* Como alternativa a um turno, poderíamos ter usado a BT
instrução x86 , mas é um byte a mais para codificar, portanto não há vantagem. A menos que fôssemos forçados a usar uma convenção de chamada diferente que não passasse convenientemente o valor de entrada no ECX
registro. Isso seria um problema, porque SHR
requer que seu operando de origem seja CL
para uma contagem de turnos dinâmicos. Portanto, uma convenção de chamada diferente exigiria que MOV
editássemos o valor de entrada de qualquer registro para o qual fosse passado ECX
, o que nos custaria 2 bytes. A BT
instrução pode usar qualquer um registro como um operando de origem, a um custo de apenas 1 byte. Então, nessa situação, seria preferível.BT
coloca o valor do bit correspondente no sinalizador de transporte (CF), portanto você usaria umSETC
instrução para obter esse valor em um registro inteiro, como AL
para que ele pudesse ser retornado ao chamador.
Implementação alternativa, 23 bytes
Aqui está uma implementação alternativa que usa operações de módulo e multiplicação para determinar se o valor de entrada é um número do Chicken McNugget.
Ele usa a convenção de chamada System64 AMD64 , que passa o valor de entrada no EDI
registro. O resultado ainda é um booleano, retornado EAX
.
Observe, porém, que, diferentemente do código acima, esse é um booleano inverso (para conveniência da implementação). Retorna false
se o valor de entrada for um número do Chicken McNugget ou true
se o valor de entrada não for um número do Chicken McNugget.
; bool IsNotMcNuggetNumber(int n)
; n is passed in EDI
8D 04 3F lea eax, [rdi+rdi*1] ; multiply input by 2, and put result in EAX
83 FF 2B cmp edi, 43
7D 0E jge TheEnd ; everything >= 43 is a McNugget number
99 cdq ; zero EDX in only 1 byte
6A 03 push 3
59 pop rcx ; short way to put 3 in ECX for DIV
F7 F1 div ecx ; divide input value by 3
6B D2 14 imul edx, edx, 20 ; multiply remainder of division by 20
39 D7 cmp edi, edx
0F 9C C0 setl al ; AL = (original input) < (input % 3 * 20)
TheEnd:
C3 ret
O que é feio nisso é a necessidade de lidar explicitamente com valores de entrada> = 43 por meio de uma comparação e ramificação na parte superior. Obviamente, existem outras maneiras de fazer isso que não exigem ramificação, como o algoritmo de caird coinheringaahing , mas isso levaria muito mais bytes para codificar, portanto, não é uma solução razoável. Eu acho que provavelmente estou perdendo algum truque de manipulação de bits que faria isso funcionar com mais elegância e ter menos bytes do que a solução baseada em campo de bits acima (já que a codificação do próprio campo de bits leva tantos bytes), mas eu o estudei um tempo e ainda não consigo vê-lo.
Bem, tente online de qualquer maneira!