Kod maszynowy x86-64, 22 bajty
48 B8 41 92 34 6D DB F7 FF FF 83 F9 40 7D 03 48 D3 E8 83 E0 01 C3
Powyższe bajty definiują funkcję w 64-bitowym kodzie maszynowym x86, który określa, czy wartością wejściową jest liczba Chicken McNugget. Pojedynczy dodatni parametr liczby całkowitej jest przekazywany do ECXrejestru zgodnie z 64-bitową konwencją wywoływania Microsoft stosowaną w systemie Windows. Wynikiem jest wartość logiczna zwrócona do EAXrejestru.
Mnemoniki do montażu bez golfa:
; 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
Oczywiście ma to duży wpływ na rozwiązanie Andersa Kaseorga w Pythonie , ponieważ opiera się na polu bitowym reprezentującym wartości, które są liczbami Chicken McNugget. W szczególności każdy bit w tym polu, który odpowiada prawidłowemu numerowi Chicken McNugget, jest ustawiony na 1; wszystkie inne bity są ustawione na 0. (Uważa to 0 za prawidłowy numer Chicken McNugget, ale jeśli ci się to nie podoba, twoja preferencja jest modyfikacją jednobitową).
Zaczynamy od załadowania tej wartości do rejestru. Jest to wartość 64-bitowa, która do kodowania zajmuje już 8 bajtów, a ponadto potrzebujemy jednobajtowego prefiksu REX.W, więc tak naprawdę jesteśmy dość rozrzutni w kategoriach bajtów, ale to jest sedno rozwiązania, więc Myślę, że warto.
Następnie przesuwamy pole w prawo o wartość wejściową. * Wreszcie, maskujemy wszystkie bity oprócz najniższego rzędu, i to staje się naszym wynikiem logicznym.
Ponieważ jednak nie można przesunąć o więcej niż rzeczywistą liczbę bitów wartości, działa to tylko dla danych wejściowych od 0–63. Aby obsługiwać wyższe wartości wejściowe, wstawiamy test u góry funkcji, która rozgałęzia się na dole wartości wejściowej to> = 64. Jedyne interesujące w tym jest to, że wstępnie ładujemy stałą pola bitowego RAX, a następnie rozgałęziamy aż do instrukcji, która maskuje bit najniższego rzędu, zapewniając w ten sposób, że zawsze zwracamy 1.
Wypróbuj online!
(Wywołanie funkcji C tam jest opatrzone adnotacją, która powoduje, że GCC wywołuje ją przy użyciu konwencji wywoływania Microsoft, której używa mój kod zestawu. Gdyby TIO dostarczyło MSVC, nie byłoby to konieczne).
__
* Alternatywnie do zmiany moglibyśmy użyć BTinstrukcji x86 , ale jest to 1 bajt dłuższy do zakodowania, więc nie ma przewagi. Chyba że byliśmy zmuszeni zastosować inną konwencję wywoływania, która nie przekazała wygodnie wartości wejściowej do ECXrejestru. Byłby to problem, ponieważ SHR wymaga, aby jego operand źródłowy służył CLdo dynamicznego liczenia przesunięcia. Dlatego inna konwencja wywoływania wymagałaby MOVedycji wartości wejściowej z dowolnego rejestru, do którego została przekazana ECX, co kosztowałoby nas 2 bajty. BTInstrukcja może wykorzystać dowolny rejestr jako argumentu źródłowego, kosztem tylko 1 bajt. W takiej sytuacji byłoby lepiej.BTwstawia wartość odpowiedniego bitu do flagi carry (CF), więc użyłbyś SETCinstrukcji, aby uzyskać tę wartość w rejestrze liczb całkowitych, tak ALaby mogła zostać zwrócona do wywołującego.
Alternatywne wdrożenie, 23 bajty
Oto alternatywna implementacja wykorzystująca operacje modulo i mnożenia w celu ustalenia, czy wartością wejściową jest liczba Chicken McNugget.
Korzysta z konwencji wywoływania AMD64 Systemu V , która przekazuje wartość wejściową do EDIrejestru. Wynikiem jest nadal wartość logiczna, zwrócona w EAX.
Zauważ jednak, że w przeciwieństwie do powyższego kodu, jest to odwrotna wartość logiczna (dla wygody implementacji). Zwraca, falsejeśli wartość wejściowa to liczba Chicken McNugget lub truejeśli wartość wejściowa nie jest liczbą 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
Brzydka w tym jest potrzeba jawnego obsługiwania wartości wejściowych> = 43 przez porównanie i odgałęzienie u góry. Istnieją oczywiście inne sposoby wykonania tego zadania, które nie wymagają rozgałęzienia, takie jak algorytm Caira coinheringaahing , ale kodowanie zajmie znacznie więcej bajtów, więc nie jest to rozsądne rozwiązanie. Myślę, że prawdopodobnie brakuje mi sztuczki, która sprawiłaby, że to zadziałałoby bardziej elegancko i było mniej bajtów niż powyższe rozwiązanie oparte na polu bitowym (ponieważ kodowanie samego pola bitowego zajmuje tak wiele bajtów), ale przestudiowałem to dla jakiś czas i wciąż tego nie widzę.
No cóż, spróbuj mimo to online !