Perl, 137 символов
($x,$y)=<>;while($x=~s/.. *//s){$e=hex$&;$i=0;$s=$r[$i]+=$e*hex,$r[$i]&=255,$r[++$i]+=$s>>8 for$y=~/.. */gs;$y="00$y"}printf'%02x 'x@r,@r
Предостережения
- Иногда печатает дополнительный
00байт в конце результата. Конечно, результат все еще правильный даже с этим лишним байтом.
- Печатает дополнительный пробел после последнего шестнадцатеричного байта в результате.
объяснение
Объяснение будет немного длинным, но я думаю, что большинство людей найдут его интересным.
Прежде всего, когда мне было 10 лет, меня научили следующему маленькому трюку. Вы можете умножить любые два положительных числа с этим. Я опишу это на примере 13 × 47. Вы начинаете с написания первого числа 13 и деления его на 2 (округление каждый раз), пока не достигнете 1:
13
6
3
1
Теперь, рядом с 13, вы пишете другое число, 47, и продолжаете умножать его на 2 столько же раз:
13 47
6 94
3 188
1 376
Теперь вы вычеркиваете все линии, где число слева чётно . В данном случае это только 6. (Я не могу сделать зачеркнутый код, поэтому я просто удалю его.) Наконец, вы добавляете все оставшиеся числа справа:
13 47
3 188
1 376
----
611
И это правильный ответ. 13 × 47 = 611.
Теперь, поскольку вы все компьютерные гики, вы поймете, что то, что мы на самом деле делаем в левом и правом столбцах, это x >> 1и y << 1, соответственно. Кроме того, мы добавляем yтолько если x & 1 == 1. Это переводит непосредственно в алгоритм, который я напишу здесь в псевдокоде:
input x, y
result = 0
while x > 0:
if x & 1 == 1:
result = result + y
x = x >> 1
y = y << 1
print result
Мы можем переписать, ifчтобы использовать умножение, и затем мы можем легко изменить это так, чтобы оно работало побайтово, а не побитно:
input x, y
result = 0
while x > 0:
result = result + (y * (x & 255))
x = x >> 8
y = y << 8
print result
Это по-прежнему содержит умножение на y, которое имеет произвольный размер, поэтому нам нужно также преобразовать его в цикл. Мы сделаем это на Perl.
Теперь переведите все на Perl:
$xи $yявляются входными данными в шестнадцатеричном формате, поэтому они имеют младший байт первым .
Таким образом, вместо x >> 8меня $x =~ s/.. *//s. Мне нужен пробел + звезда, потому что последний байт может не иметь пробела (может использовать пробел + ?тоже). Это автоматически помещает удаленный byte ( x & 255) в $&.
y << 8это просто $y = "00$y".
На resultсамом деле это числовой массив @r. В конце каждый элемент @rсодержит один байт ответа, но в середине вычисления он может содержать более одного байта. Ниже я докажу вам, что каждое значение никогда не превышает двух байтов (16 бит) и что результат всегда равен одному байту в конце.
Итак, вот код Perl, который был раскрыт и прокомментирован:
# Input x and y
($x, $y) = <>;
# Do the equivalent of $& = x & 255, x = x >> 8
while ($x =~ s/.. *//s)
{
# Let e = x & 255
$e = hex $&;
# For every byte in y... (notice this sets $_ to each byte)
$i = 0;
for ($y =~ /.. */gs)
{
# Do the multiplication of two single-byte values.
$s = $r[$i] += $e*hex,
# Truncate the value in $r[$i] to one byte. The rest of it is still in $s
$r[$i] &= 255,
# Move to the next array item and add the carry there.
$r[++$i] += $s >> 8
}
# Do the equivalent of y = y << 8
$y = "00$y"
}
# Output the result in hex format.
printf '%02x ' x @r, @r
Теперь для доказательства того, что это всегда выводит байты , и что вычисление никогда не генерирует значения больше двух байтов. Я докажу это по индукции через 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цикла, также является одним байтом.
Это завершает замечательный и захватывающий вызов. Большое спасибо за публикацию!