Perl, 137 karakter
($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
Peringatan
- Terkadang mencetak
00
byte tambahan di akhir hasil. Tentu saja hasilnya masih benar bahkan dengan byte tambahan itu.
- Mencetak ruang ekstra setelah hex byte terakhir dalam hasilnya.
Penjelasan
Penjelasannya akan agak panjang, tapi saya pikir sebagian besar orang di sini akan menganggapnya menarik.
Pertama-tama, ketika saya berusia 10 tahun, saya diajari trik kecil berikut. Anda dapat mengalikan dua angka positif dengan ini. Saya akan menjelaskan ini menggunakan contoh 13 × 47. Anda mulai dengan menulis angka pertama, 13, dan membaginya dengan 2 (dibulatkan setiap kali) sampai Anda mencapai 1:
13
6
3
1
Sekarang, di sebelah angka 13 Anda menulis angka lainnya, 47, dan terus mengalikannya dengan 2 kali yang sama:
13 47
6 94
3 188
1 376
Sekarang Anda mencoret semua garis di mana angka di sebelah kiri genap . Dalam hal ini, ini hanya 6. (saya tidak bisa melakukan kode pemogokan, jadi saya hanya akan menghapusnya.) Akhirnya, Anda menambahkan semua angka yang tersisa di sebelah kanan:
13 47
3 188
1 376
----
611
Dan ini jawaban yang tepat. 13 × 47 = 611.
Sekarang, karena Anda semua ahli komputer, Anda akan menyadari bahwa apa yang sebenarnya kami lakukan di kolom kiri dan kanan adalah x >> 1
dan y << 1
, masing-masing. Selanjutnya, kami menambahkan y
hanya jika x & 1 == 1
. Ini diterjemahkan langsung ke dalam algoritma, yang akan saya tulis di sini di pseudocode:
input x, y
result = 0
while x > 0:
if x & 1 == 1:
result = result + y
x = x >> 1
y = y << 1
print result
Kita dapat menulis ulang if
untuk menggunakan perkalian, dan kemudian kita dapat dengan mudah mengubah ini sehingga bekerja pada basis byte demi byte alih-alih sedikit demi sedikit:
input x, y
result = 0
while x > 0:
result = result + (y * (x & 255))
x = x >> 8
y = y << 8
print result
Ini masih mengandung perkalian dengan y
, yang merupakan ukuran arbitrer, jadi kita perlu mengubahnya menjadi sebuah loop juga. Kami akan melakukannya di Perl.
Sekarang terjemahkan semuanya ke Perl:
$x
dan $y
merupakan input dalam format hex, sehingga mereka memiliki byte paling signifikan pertama .
Jadi, bukannya x >> 8
saya lakukan $x =~ s/.. *//s
. Saya memerlukan bintang ruang + karena byte terakhir mungkin tidak memiliki ruang di atasnya (bisa menggunakan ruang + ?
juga). Ini secara otomatis menempatkan byte yang dihapus ( x & 255
) ke $&
.
y << 8
sederhana $y = "00$y"
.
The result
sebenarnya array numerik, @r
. Pada akhirnya, setiap elemen @r
berisi satu byte jawaban, tetapi di tengah-tengah perhitungan, ia mungkin berisi lebih dari satu byte. Saya akan membuktikan kepada Anda di bawah ini bahwa setiap nilai tidak pernah lebih dari dua byte (16 bit) dan hasilnya selalu satu byte pada akhirnya.
Jadi di sini adalah kode Perl terurai dan berkomentar:
# 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
Sekarang sebagai bukti bahwa ini selalu menghasilkan byte , dan perhitungan tidak pernah menghasilkan nilai lebih dari dua byte. Saya akan membuktikan ini dengan induksi di atas while
loop:
Kosong @r
di awal jelas tidak memiliki nilai lebih besar dari 0xFF di dalamnya (karena tidak memiliki nilai sama sekali). Ini menyimpulkan kasus dasar.
Sekarang, mengingat bahwa @r
hanya berisi satu byte pada awal setiap while
iterasi:
The for
Loop eksplisit &=
s semua nilai dalam hasil array dengan 255 kecuali yang terakhir , jadi kita hanya perlu melihat yang terakhir.
Kami tahu bahwa kami selalu menghapus hanya satu byte dari $x
dan $y
:
Oleh karena itu, $e*hex
adalah perkalian dua nilai byte tunggal, yang berarti berada dalam kisaran 0 — 0xFE01
.
Dengan hipotesis induktif, $r[$i]
adalah satu byte; oleh karena itu, $s = $r[$i] += $e*hex
berada dalam kisaran 0 — 0xFF00
.
Karena itu, $s >> 8
selalu satu byte.
$y
menumbuhkan ekstra 00
di setiap iterasi while
loop:
Oleh karena itu, dalam setiap iterasi while
loop, for
loop dalam berjalan untuk satu iterasi lebih banyak daripada while
iterasi sebelumnya .
Oleh karena itu, $r[++$i] += $s >> 8
dalam iterasi terakhir dari for
lingkaran selalu menambahkan $s >> 8
untuk 0
, dan kita sudah menetapkan bahwa $s >> 8
selalu satu byte.
Oleh karena itu, nilai terakhir yang disimpan di @r
akhir for
loop juga merupakan byte tunggal.
Ini mengakhiri tantangan yang luar biasa dan mengasyikkan. Terima kasih banyak untuk mempostingnya!