Вам дана 16-битная машина и сказано, чтобы реализовать умножение целых чисел произвольного размера. Ваши регистры могут содержать только 16-битные числа, а самая большая команда умножения принимает два 8-битных входа и генерирует 16-битный результат.
Ваша программа должна принять в качестве входных данных два положительных числа произвольного размера и вывести их произведение. Каждый входной номер кодируется в отдельной строке в виде байтового массива с прямым порядком байтов, где каждый байт представляет собой двухзначное шестнадцатеричное число. Вывод должен быть отформатирован аналогично. Возможно, лучше всего объяснить на примере:
вход
1f 4a 07
63 a3
выход
fd 66 03 a7 04
который кодирует умножение 477727 * 41827 = 19981887229.
Вы можете предположить, что последний (самый значимый) байт каждого входного номера ненулевой, а последний кусок номера, который вы выводите, должен быть ненулевым. Оба входных числа будут иметь длину не более 100 байтов.
Наименьший код выигрывает.
Помните, что наибольшее умножение, которое вам разрешено использовать, составляет 1 байт * 1 байт, и никакие целочисленные типы не должны превышать 2 байта!
Ответы:
Perl, 137 символов
Предостережения
00
байт в конце результата. Конечно, результат все еще правильный даже с этим лишним байтом.объяснение
Объяснение будет немного длинным, но я думаю, что большинство людей найдут его интересным.
Прежде всего, когда мне было 10 лет, меня научили следующему маленькому трюку. Вы можете умножить любые два положительных числа с этим. Я опишу это на примере 13 × 47. Вы начинаете с написания первого числа 13 и деления его на 2 (округление каждый раз), пока не достигнете 1:
Теперь, рядом с 13, вы пишете другое число, 47, и продолжаете умножать его на 2 столько же раз:
Теперь вы вычеркиваете все линии, где число слева чётно . В данном случае это только 6. (Я не могу сделать зачеркнутый код, поэтому я просто удалю его.) Наконец, вы добавляете все оставшиеся числа справа:
И это правильный ответ. 13 × 47 = 611.
Теперь, поскольку вы все компьютерные гики, вы поймете, что то, что мы на самом деле делаем в левом и правом столбцах, это
x >> 1
иy << 1
, соответственно. Кроме того, мы добавляемy
только еслиx & 1 == 1
. Это переводит непосредственно в алгоритм, который я напишу здесь в псевдокоде:Мы можем переписать,
if
чтобы использовать умножение, и затем мы можем легко изменить это так, чтобы оно работало побайтово, а не побитно:Это по-прежнему содержит умножение на
y
, которое имеет произвольный размер, поэтому нам нужно также преобразовать его в цикл. Мы сделаем это на Perl.Теперь переведите все на Perl:
$x
и$y
являются входными данными в шестнадцатеричном формате, поэтому они имеют младший байт первым .Таким образом, вместо
x >> 8
меня$x =~ s/.. *//s
. Мне нужен пробел + звезда, потому что последний байт может не иметь пробела (может использовать пробел +?
тоже). Это автоматически помещает удаленный byte (x & 255
) в$&
.y << 8
это просто$y = "00$y"
.На
result
самом деле это числовой массив@r
. В конце каждый элемент@r
содержит один байт ответа, но в середине вычисления он может содержать более одного байта. Ниже я докажу вам, что каждое значение никогда не превышает двух байтов (16 бит) и что результат всегда равен одному байту в конце.Итак, вот код Perl, который был раскрыт и прокомментирован:
Теперь для доказательства того, что это всегда выводит байты , и что вычисление никогда не генерирует значения больше двух байтов. Я докажу это по индукции через
while
цикл:Пустое
@r
в начале явно не имеет значений больше 0xFF (потому что оно вообще не имеет значений). Это завершает базовый случай.Теперь, учитывая, что
@r
в начале каждойwhile
итерации содержится только один байт :for
Цикл явно&=
S все значения в результирующем массиве с 255 , за исключением последнего , так что мы только должны смотреть на этом последнем.Мы знаем , что мы всегда удалить только один байт из
$x
и$y
:Следовательно,
$e*hex
это умножение двух однобайтовых значений, что означает, что оно находится в диапазоне0 — 0xFE01
.По индуктивной гипотезе,
$r[$i]
это один байт; следовательно,$s = $r[$i] += $e*hex
находится в диапазоне0 — 0xFF00
.Поэтому
$s >> 8
всегда один байт.$y
увеличивается на00
каждой итерацииwhile
цикла:Следовательно, на каждой итерации
while
цикла внутреннийfor
цикл выполняется на одну итерацию больше, чем на предыдущейwhile
итерации.Таким образом,
$r[++$i] += $s >> 8
в последней итерацииfor
цикла всегда добавляет$s >> 8
к0
, и мы уже установили , что$s >> 8
всегда один байт.Следовательно, последнее значение, хранящееся в
@r
концеfor
цикла, также является одним байтом.Это завершает замечательный и захватывающий вызов. Большое спасибо за публикацию!
источник
C решение
Это решение не проверяет входные данные. Это также только слегка проверено. Скорость не была действительно рассмотрением. Память Маллока, и она не особенно умна в отношении того, сколько она захватывает. Гарантированно хватит и больше чем надо.
m () принимает строку, ожидает две строки в строке, по одному после каждого числа. Ожидаются только цифры, строчные буквы, пробелы и переводы строк. Ожидает, что шестнадцатеричные цифры всегда будут парой.
Операция умножения никогда не используется (сознательно). Сдвиг выполняется на 8-битных переменных. Выполняется одно 16-битное сложение. Нет 32-битных типов данных.
Сжатые от руки, и только мягко. edit: больше запутывания, меньше символов: D Компилируется с предупреждениями с помощью gcc.
Персонажи: 675
Вы можете проверить это:
Результат:
источник
OCaml + Батареи, 362 символа
Стандартный O (n * m) алгоритм умножения школьников. Обратите внимание, что для удовлетворения требований вызова операции выполняются с байтами строк, которые в OCaml (в данном случае удобно) являются изменяемыми. Отметим также, что аккумулятор
s
никогда не переполняется 16 битами, поскольку 2 (2 ^ 8 - 1) + (2 ^ 8 - 1) ^ 2 = (2 ^ 8 - 1) (2 ^ 8 + 1) = 2 ^ 16 - 1 ,Например,
источник
JavaScript (Node.js) , 160 байт
Попробуйте онлайн!
Гораздо более новый язык, чем тогда
источник