Машинный код x86-64, 22 байта
48 B8 41 92 34 6D DB F7 FF FF 83 F9 40 7D 03 48 D3 E8 83 E0 01 C3
Вышеуказанные байты определяют функцию в 64-битном машинном коде x86, которая определяет, является ли входное значение числом Чикен МакНуггет. Один положительный целочисленный параметр передается в ECXрегистр в соответствии с 64-битным соглашением о вызовах Microsoft, используемым в Windows. Результатом является логическое значение, возвращаемое в EAXрегистре.
Неуправляемая сборка мнемоники:
; 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
Очевидно, что это сильно противоречит решению Андерса Касерга в Python , так как оно основано на битовом поле, представляющем значения, представляющие собой числа Чикен МакНуггет. В частности, каждый бит в этом поле, который соответствует действительному номеру Chicken McNugget, устанавливается в 1; все остальные биты установлены в 0. (Это считает, что 0 является действительным числом Chicken McNugget, но если вам это не нравится, ваши предпочтения - это однобитная модификация.)
Мы начинаем с простой загрузки этого значения в регистр. Это 64-битное значение, для кодирования которого уже требуется 8 байтов, плюс нам нужен однобайтовый префикс REX.W, так что мы действительно потратили немного времени на байты, но это суть решения, поэтому Я думаю, это того стоит.
Затем мы сдвигаем поле вправо на входное значение. * Наконец, мы маскировать все , кроме бита низшего порядка, и становится нашим Логическое результат.
Однако, поскольку вы не можете сдвинуться больше, чем на количество битов, фактически находящихся в значении, это работает только для входов от 0 до 63 Чтобы поддерживать более высокие входные значения, мы вставляем тест в верхней части функции, которая ветвится в нижней части входного значения:> = 64. Единственное, что интересно в этом, это то, что мы предварительно загружаем константу битового поля в RAX, а затем выполняем переход вплоть до инструкции, которая маскирует бит самого младшего порядка, гарантируя, что мы всегда возвращаем 1.
Попробуйте онлайн!
(Вызов функции C там помечен атрибутом, который заставляет GCC вызывать его, используя соглашение о вызовах Microsoft, которое использует мой ассемблерный код. Если бы TIO предоставил MSVC, в этом не было бы необходимости.)
__
* В качестве альтернативы сдвигу мы могли бы использовать BTинструкцию x86 , но для кодирования это на 1 байт больше, так что никаких преимуществ. Если только мы не были вынуждены использовать другое соглашение о вызовах, в котором не было удобно передавать входное значение в ECXрегистр. Это было бы проблемой, потому что SHR требует, чтобы его исходный операнд был CLдля счетчика динамического сдвига. Поэтому другое соглашение о вызовах потребовало бы, чтобы мы MOVредактировали входное значение из любого регистра, в который оно было передано ECX, что стоило бы нам 2 байта. BTКоманда может использовать любой регистр в качестве операнда - источника, при стоимости только 1 байт. Так что в такой ситуации это было бы предпочтительнее.BT заносит значение соответствующего бита в флаг переноса (CF), так что вы бы использоватьSETCинструкция для получения этого значения в целочисленном регистре, например, ALчтобы оно могло быть возвращено вызывающей стороне.
Альтернативная реализация, 23 байта
Вот альтернативная реализация, которая использует операции по модулю и умножению, чтобы определить, является ли входное значение числом Чикен МакНуггет.
Он использует соглашение о вызовах System V AMD64 , которое передает входное значение в EDIрегистр. Результат все еще логический, возвращенный в EAX.
Обратите внимание, что, в отличие от приведенного выше кода, это обратный логический тип (для удобства реализации). Он возвращает, falseесли входное значение является числом Чикен МакНуггет, или trueесли входное значение не является числом Чикен МакНуггет.
; 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
Уродливым в этом является необходимость явной обработки входных значений> = 43 с помощью функции сравнения и ветвления вверху. Очевидно, есть другие способы сделать это, которые не требуют ветвления, например алгоритм caird coinheringaahing , но для кодирования потребуется намного больше байтов, так что это не является разумным решением. Я полагаю, что мне, вероятно, не хватает некоторого хитрого трюка, который сделал бы эту работу более элегантной и занимал бы меньше байтов, чем решение на основе битового поля выше (поскольку кодирование самого битового поля занимает так много байтов), но я изучил это для некоторое время и до сих пор не вижу этого.
Ну ладно, попробуйте в любом случае онлайн !