Code machine x86-64, 22 octets
48 B8 41 92 34 6D DB F7 FF FF 83 F9 40 7D 03 48 D3 E8 83 E0 01 C3
Les octets ci-dessus définissent une fonction dans le code machine x86 64 bits qui détermine si la valeur d'entrée est un nombre Chicken McNugget. Le paramètre entier positif unique est transmis dans le ECX
registre, conformément à la convention d'appel Microsoft 64 bits utilisée sous Windows. Le résultat est une valeur booléenne renvoyée dans le EAX
registre.
Mnémoniques d'assemblage non golfés:
; 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
De toute évidence, cela joue fortement hors de la solution Anders Kaseorg en Python , en ce sens qu'elle est basée sur un champ de bits représentant les valeurs qui sont des nombres de Chicken McNugget. Plus précisément, chaque bit de ce champ qui correspond à un numéro Chicken McNugget valide est défini sur 1; tous les autres bits sont définis sur 0. (Cela considère 0 comme un nombre valide de Chicken McNugget, mais si vous n'aimez pas cela, votre préférence est une modification à un seul bit.)
Nous commençons par charger simplement cette valeur dans un registre. C'est une valeur de 64 bits, qui prend déjà 8 octets à coder, et nous avons besoin d'un préfixe REX.W d'un octet, donc nous sommes vraiment très dépensiers en termes d'octets, mais c'est le cœur de la solution, donc Je suppose que ça vaut le coup.
Nous décalons ensuite le champ vers la droite de la valeur d'entrée. * Enfin, nous masquons tout sauf le bit de poids faible, et cela devient notre résultat booléen.
Cependant, puisque vous ne pouvez pas décaler de plus que le nombre de bits réellement dans la valeur, cela ne fonctionne que pour les entrées de 0 à 63. Pour prendre en charge des valeurs d'entrée plus élevées, nous insérons un test en haut de la fonction qui se ramifie au bas de la valeur d'entrée est> = 64. La seule chose intéressante à ce sujet est que nous préchargeons la constante de champ de bits dans RAX
, puis branchons jusqu'à l'instruction qui masque le bit de poids faible, garantissant ainsi que nous renvoyons toujours 1.
Essayez-le en ligne!
(L'appel de fonction C y est annoté avec un attribut qui fait que GCC l'appelle à l'aide de la convention d'appel Microsoft que mon code d'assembly utilise. Si TIO avait fourni MSVC, cela ne serait pas nécessaire.)
__
* Comme alternative à un shift, nous aurions pu utiliser l' BT
instruction x86 , mais c'est 1 octet de plus à encoder, donc aucun avantage. Sauf si nous avons été obligés d'utiliser une convention d'appel différente qui ne transmettait pas commodément la valeur d'entrée dans le ECX
registre. Ce serait un problème car il SHR
faut que son opérande source soit CL
pour un compte de décalage dynamique. Par conséquent, une convention d'appel différente nécessiterait que nous MOV
éditions la valeur d'entrée du registre dans ECX
lequel elle a été transmise , ce qui nous coûterait 2 octets. L' BT
instruction peut utiliser n'importe quelle registre comme opérande source, à un coût de seulement 1 octet. Donc, dans cette situation, ce serait préférable.BT
valeur pour mettre le bit correspondant dans l'indicateur de report (CF), vous utiliserez donc unSETC
instruction pour obtenir cette valeur dans un registre entier commeAL
pour qu'elle puisse être renvoyée à l'appelant.
Implémentation alternative, 23 octets
Voici une implémentation alternative qui utilise des opérations de modulo et de multiplication pour déterminer si la valeur d'entrée est un nombre Chicken McNugget.
Il utilise la convention d'appel System V AMD64 , qui transmet la valeur d'entrée dans le EDI
registre. Le résultat est toujours un booléen retourné EAX
.
Notez, cependant, que contrairement au code ci-dessus, il s'agit d'un booléen inverse (pour la commodité de l'implémentation). Il renvoie false
si la valeur d'entrée est un nombre Chicken McNugget ou true
si la valeur d'entrée n'est pas un nombre 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
Ce qui est laid à ce sujet, c'est la nécessité de gérer explicitement les valeurs d'entrée> = 43 par une comparaison et une branche en haut. Il existe évidemment d'autres façons de le faire qui ne nécessitent pas de branchement, comme l'algorithme de caird coinheringaahing , mais cela prendrait beaucoup plus d'octets à coder, ce n'est donc pas une solution raisonnable. Je pense qu'il me manque probablement une astuce de twiddling qui rendrait cela plus élégant et serait moins d'octets que la solution basée sur le champ de bits ci-dessus (car l'encodage du champ de bits lui-même prend tellement d'octets), mais j'ai étudié cela pour un moment et ne peut toujours pas le voir.
Eh bien, essayez quand même en ligne !