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
. Мені потрібен пробіл + зірка, тому що останній байт може не мати пробілу (він також може використовувати пробіл + ?
). Це автоматично ставить видалений байт ( 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
циклу, також є єдиним байтом.
Це завершує чудовий та захоплюючий виклик. Дякую велике за публікацію!