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
00byte 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 >> 1dan y << 1, masing-masing. Selanjutnya, kami menambahkan yhanya 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 ifuntuk 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:
$xdan $ymerupakan input dalam format hex, sehingga mereka memiliki byte paling signifikan pertama .
Jadi, bukannya x >> 8saya 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 << 8sederhana $y = "00$y".
The resultsebenarnya array numerik, @r. Pada akhirnya, setiap elemen @rberisi 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 whileloop:
Kosong @rdi awal jelas tidak memiliki nilai lebih besar dari 0xFF di dalamnya (karena tidak memiliki nilai sama sekali). Ini menyimpulkan kasus dasar.
Sekarang, mengingat bahwa @rhanya berisi satu byte pada awal setiap whileiterasi:
The forLoop 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 $xdan $y:
Oleh karena itu, $e*hexadalah 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*hexberada dalam kisaran 0 — 0xFF00.
Karena itu, $s >> 8selalu satu byte.
$ymenumbuhkan ekstra 00di setiap iterasi whileloop:
Oleh karena itu, dalam setiap iterasi whileloop, forloop dalam berjalan untuk satu iterasi lebih banyak daripada whileiterasi sebelumnya .
Oleh karena itu, $r[++$i] += $s >> 8dalam iterasi terakhir dari forlingkaran selalu menambahkan $s >> 8untuk 0, dan kita sudah menetapkan bahwa $s >> 8selalu satu byte.
Oleh karena itu, nilai terakhir yang disimpan di @rakhir forloop juga merupakan byte tunggal.
Ini mengakhiri tantangan yang luar biasa dan mengasyikkan. Terima kasih banyak untuk mempostingnya!