Mã máy x86-64, 22 byte
48 B8 41 92 34 6D DB F7 FF FF 83 F9 40 7D 03 48 D3 E8 83 E0 01 C3
Các byte trên xác định một hàm trong mã máy 64 bit x86 xác định xem giá trị đầu vào có phải là số Gà McNugget hay không. Tham số nguyên dương duy nhất được truyền vào thanh ECX
ghi, theo quy ước gọi 64 bit của Microsoft được sử dụng trên Windows. Kết quả là một giá trị Boolean được trả về trong thanh EAX
ghi.
Ma thuật lắp ráp bất khả xâm phạm:
; 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
Rõ ràng, điều này đóng vai trò rất lớn đối với giải pháp của Anders Kaseorg trong Python , ở chỗ nó dựa trên một trường bit đại diện cho các giá trị là các số Gà McNugget. Cụ thể, mỗi bit trong trường này tương ứng với số Gà McNugget hợp lệ được đặt thành 1; tất cả các bit khác được đặt thành 0. (Điều này coi 0 là số Gà McNugget hợp lệ, nhưng nếu bạn không thích điều đó, thì sở thích của bạn là một sửa đổi một bit.)
Chúng tôi bắt đầu bằng cách tải giá trị này vào một thanh ghi. Đó là một giá trị 64 bit, đã mất 8 byte để mã hóa, cộng với chúng ta cần một tiền tố REX.W một byte, vì vậy chúng tôi thực sự rất tốn kém về mặt byte, nhưng đây là trung tâm của giải pháp, vì vậy Tôi đoán nó đáng giá.
Sau đó chúng ta chuyển trường ngay theo giá trị đầu vào. * Cuối cùng, chúng tôi che dấu tất cả trừ bit thứ tự thấp nhất và điều đó trở thành kết quả Boolean của chúng tôi.
Tuy nhiên, vì bạn không thể thay đổi nhiều hơn số bit thực sự trong giá trị, nên điều này chỉ hoạt động đối với các đầu vào từ 0 Nott63. Để hỗ trợ các giá trị đầu vào cao hơn, chúng tôi chèn một bài kiểm tra ở đầu hàm phân nhánh xuống dưới cùng của giá trị đầu vào là> = 64. Điều thú vị duy nhất về điều này là chúng tôi tải trước hằng số trường bit trong RAX
và sau đó phân nhánh theo hướng dẫn che đi bit thứ tự thấp nhất, do đó đảm bảo rằng chúng ta luôn trả về 1.
Hãy thử trực tuyến!
(Cuộc gọi hàm C có chú thích với một thuộc tính khiến GCC gọi nó bằng cách sử dụng quy ước gọi của Microsoft mà mã lắp ráp của tôi sử dụng. Nếu TIO đã cung cấp MSVC, điều này sẽ không cần thiết.)
__
* Thay thế cho một ca làm việc, chúng ta có thể đã sử dụng lệnh x86 BT
, nhưng đó là 1 byte dài hơn để mã hóa, do đó không có lợi thế. Trừ khi chúng tôi buộc phải sử dụng một quy ước gọi khác mà không thuận tiện vượt qua giá trị đầu vào trong thanh ECX
ghi. Đây sẽ là một vấn đề vì SHR
yêu cầu toán hạng nguồn của nó phải có CL
số lần dịch chuyển động. Do đó, một quy ước gọi khác nhau sẽ yêu cầu chúng tôi MOV
chỉnh sửa giá trị đầu vào từ bất kỳ đăng ký nào được truyền vào ECX
, sẽ tiêu tốn của chúng tôi 2 byte. Lệnh BT
có thể sử dụng bất kỳ thanh ghi nào làm toán hạng nguồn, với chi phí chỉ 1 byte. Vì vậy, trong tình huống đó, nó sẽ được ưa thích hơn.BT
đặt giá trị của bit tương ứng vào cờ mang (CF), vì vậy bạn sẽ sử dụng một SETC
lệnh để nhận giá trị đó trong một thanh ghi số nguyên như AL
vậy để nó có thể được trả về cho người gọi.
Thực hiện thay thế, 23 byte
Dưới đây là một triển khai thay thế sử dụng các phép toán modulo và phép nhân để xác định xem giá trị đầu vào có phải là số Chicken McNugget hay không.
Nó sử dụng quy ước gọi System64 AMD64 , vượt qua giá trị đầu vào trong thanh EDI
ghi. Kết quả vẫn là Boolean, được trả lại EAX
.
Tuy nhiên, lưu ý rằng không giống như đoạn mã trên, đây là Boolean nghịch đảo (để thuận tiện cho việc triển khai). Nó trả về false
nếu giá trị đầu vào là số Gà McNugget hoặc true
nếu giá trị đầu vào không phải là số Gà 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
Điều xấu ở đây là cần phải xử lý rõ ràng các giá trị đầu vào> = 43 bằng cách so sánh và phân nhánh ở trên cùng. Rõ ràng có nhiều cách khác để làm điều này không yêu cầu phân nhánh, như thuật toán của caird coinheringaahing , nhưng điều này sẽ tốn nhiều byte hơn để mã hóa, vì vậy không phải là một giải pháp hợp lý. Tôi nghĩ rằng tôi có thể thiếu một số mẹo xoay vòng bit để làm cho công việc này trở nên thanh lịch hơn và ít byte hơn so với giải pháp dựa trên bitfield ở trên (vì việc mã hóa bitfield mất rất nhiều byte), nhưng tôi đã nghiên cứu điều này cho một thời gian và vẫn không thể nhìn thấy nó.
Oh tốt, thử nó trực tuyến nào!