x86-64 Kode Mesin, 22 byte
48 B8 41 92 34 6D DB F7 FF FF 83 F9 40 7D 03 48 D3 E8 83 E0 01 C3
Byte di atas menentukan fungsi dalam kode mesin x86 64-bit yang menentukan apakah nilai input adalah nomor McNugget Chicken. Parameter integer positif tunggal dilewatkan dalam ECX
register, mengikuti konvensi panggilan Microsoft 64-bit yang digunakan pada Windows. Hasilnya adalah nilai Boolean yang dikembalikan dalam EAX
register.
Mnemonik perakitan tidak dikumpulkan:
; 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
Jelas, ini memainkan banyak solusi Anders Kaseorg dengan Python , dalam hal ini didasarkan pada bidang-bit yang mewakili nilai-nilai yang merupakan angka Chicken McNugget. Secara khusus, setiap bit dalam bidang ini yang sesuai dengan nomor McNugget Chicken yang valid diatur ke 1; semua bit lainnya ditetapkan ke 0. (Ini menganggap 0 sebagai nomor Chicken McNugget yang valid, tetapi jika Anda tidak menyukainya, preferensi Anda adalah modifikasi satu-bit.)
Kami memulai dengan hanya memuat nilai ini ke dalam register. Ini adalah nilai 64-bit, yang sudah membutuhkan 8 byte untuk mengkodekan, ditambah kita membutuhkan awalan REX.W satu-byte, jadi kita benar-benar cukup boros dalam hal byte, tapi ini adalah inti dari solusi, jadi Saya kira itu sepadan.
Kami kemudian menggeser bidang ke kanan dengan nilai input. * Akhirnya, kami menutup semua kecuali bit urutan terendah, dan itu menjadi hasil Boolean kami.
Namun, karena Anda tidak dapat menggeser lebih dari jumlah bit sebenarnya dalam nilai, ini hanya berfungsi untuk input dari 0-63. Untuk mendukung nilai input yang lebih tinggi, kami menyisipkan tes di bagian atas fungsi yang bercabang ke bagian bawah nilai input adalah> = 64. Satu-satunya hal yang menarik tentang ini adalah kami memuat ulang konstanta bit-field diRAX
, dan kemudian bercabang ke instruksi yang menutupi bit orde terendah, sehingga memastikan bahwa kami selalu mengembalikan 1.
Cobalah online!
(Fungsi C panggilan ada dijelaskan dengan atribut yang menyebabkan GCC menyebutnya dengan menggunakan konvensi panggilan Microsoft yang menggunakan kode perakitan saya. Jika TIO telah menyediakan MSVC, ini tidak akan diperlukan.)
__
* Sebagai alternatif dari shift, kita bisa menggunakan BT
instruksi x86 , tapi itu 1 byte lebih lama untuk dikodekan, jadi tidak ada keuntungan. Kecuali kami dipaksa untuk menggunakan konvensi panggilan yang berbeda yang tidak dengan mudah menyampaikan nilai input dalam ECX
register. Ini akan menjadi masalah karena SHR
mengharuskan operand sumbernya CL
untuk perhitungan shift dinamis. Oleh karena itu, konvensi panggilan yang berbeda akan mengharuskan kami MOV
mengedit nilai input dari register apa pun yang diteruskan ECX
, yang akan memakan biaya 2 byte. The BT
instruksi dapat menggunakan setiap mendaftar sebagai operan sumber, dengan biaya hanya 1 byte. Jadi, dalam situasi itu, akan lebih baik.BT
menempatkan nilai bit yang sesuai ke dalam flag carry (CF), jadi Anda akan menggunakanSETC
instruksi untuk mendapatkan nilai itu dalam register integer seperti AL
sehingga bisa dikembalikan ke pemanggil.
Implementasi alternatif, 23 byte
Berikut ini adalah implementasi alternatif yang menggunakan operasi modulo dan multiplikasi untuk menentukan apakah nilai input adalah nomor McNugget Ayam.
Ia menggunakan konvensi pemanggilan Sistem V AMD64 , yang meneruskan nilai input dalam EDI
register. Hasilnya masih Boolean, kembali di EAX
.
Namun, perlu diketahui bahwa tidak seperti kode di atas, ini adalah Boolean terbalik (untuk kenyamanan implementasi). Ini mengembalikan false
jika nilai input adalah nomor McNugget Chicken, atau true
jika nilai input bukan nomor McNugget Chicken.
; 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
Apa yang jelek tentang ini adalah kebutuhan untuk secara eksplisit menangani nilai input> = 43 oleh cabang perbandingan-dan-di atas. Jelas ada cara lain untuk melakukan ini yang tidak memerlukan percabangan, seperti algoritma caird coinheringaahing , tetapi ini akan membutuhkan lebih banyak byte untuk dikodekan, jadi bukan solusi yang masuk akal. Saya pikir saya mungkin kehilangan beberapa trik bit-twiddling yang akan membuat ini bekerja lebih elegan dan lebih sedikit byte daripada solusi berbasis bitfield di atas (karena pengkodean bitfield itu sendiri membutuhkan begitu banyak byte), tetapi saya telah mempelajari ini untuk beberapa saat dan masih tidak bisa melihatnya.
Oh well, coba online saja!