Perl, 137 ký tự
($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
Hãy cẩn thận
- Đôi khi in thêm một
00byte vào cuối kết quả. Tất nhiên kết quả vẫn đúng ngay cả với byte thêm đó.
- In một khoảng trắng thừa sau byte hex cuối cùng trong kết quả.
Giải trình
Lời giải thích sẽ hơi dài, nhưng tôi nghĩ hầu hết mọi người ở đây sẽ thấy nó thú vị.
Trước hết, khi tôi 10 tuổi, tôi được dạy những mẹo nhỏ sau đây. Bạn có thể nhân hai số dương bất kỳ với số này. Tôi sẽ mô tả điều này bằng ví dụ 13 × 47. Bạn bắt đầu bằng cách viết số đầu tiên, 13 và chia cho 2 (làm tròn xuống mỗi lần) cho đến khi bạn đạt 1:
13
6
3
1
Bây giờ, bên cạnh 13 bạn viết số khác, 47 và tiếp tục nhân nó với 2 số lần giống nhau:
13 47
6 94
3 188
1 376
Bây giờ bạn gạch bỏ tất cả các dòng trong đó số bên trái là số chẵn . Trong trường hợp này, đây chỉ là số 6. (Tôi không thể thực hiện nhập mã, vì vậy tôi sẽ xóa nó đi.) Cuối cùng, bạn thêm tất cả các số còn lại ở bên phải:
13 47
3 188
1 376
----
611
Và đây là câu trả lời đúng. 13 × 47 = 611.
Bây giờ, vì bạn là tất cả các chuyên viên máy tính, bạn sẽ nhận ra rằng những gì chúng ta thực sự đang làm ở cột bên trái và bên phải là x >> 1và y << 1tương ứng. Hơn nữa, chúng tôi thêm ychỉ khi x & 1 == 1. Điều này chuyển trực tiếp thành một thuật toán, mà tôi sẽ viết ở đây bằng mã giả:
input x, y
result = 0
while x > 0:
if x & 1 == 1:
result = result + y
x = x >> 1
y = y << 1
print result
Chúng ta có thể viết lại ifđể sử dụng phép nhân, và sau đó chúng ta có thể dễ dàng thay đổi điều này để nó hoạt động trên cơ sở từng byte thay vì từng bit một:
input x, y
result = 0
while x > 0:
result = result + (y * (x & 255))
x = x >> 8
y = y << 8
print result
Điều này vẫn chứa một phép nhân với ykích thước tùy ý, vì vậy chúng ta cũng cần thay đổi nó thành một vòng lặp. Chúng tôi sẽ làm điều đó trong Perl.
Bây giờ dịch mọi thứ sang Perl:
$xvà $ylà các đầu vào ở định dạng hex, vì vậy chúng có byte đầu tiên ít quan trọng nhất .
Như vậy, thay vì x >> 8tôi làm $x =~ s/.. *//s. Tôi cần khoảng trắng + sao vì byte cuối cùng có thể không có khoảng trắng trên đó (cũng có thể sử dụng khoảng trắng + ?). Điều này tự động đặt byte ( x & 255) bị loại bỏ vào $&.
y << 8chỉ đơn giản là $y = "00$y".
Đây resultthực sự là một mảng số , @r. Cuối cùng, mỗi phần tử @rchứa một byte của câu trả lời, nhưng khi tính toán được một nửa, nó có thể chứa nhiều hơn một byte. Tôi sẽ chứng minh cho bạn dưới đây rằng mỗi giá trị không bao giờ nhiều hơn hai byte (16 bit) và kết quả luôn luôn là một byte ở cuối.
Vì vậy, đây là mã Perl làm sáng tỏ và nhận xét:
# 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
Bây giờ để chứng minh rằng điều này luôn xuất ra byte và phép tính không bao giờ tạo ra các giá trị lớn hơn hai byte. Tôi sẽ chứng minh điều này bằng cách cảm ứng qua whilevòng lặp:
Khoảng trống @rở đầu rõ ràng không có giá trị nào lớn hơn 0xFF trong đó (vì nó không có giá trị nào trong đó). Điều này kết luận trường hợp cơ sở.
Bây giờ, được cho là @rchỉ chứa các byte đơn ở đầu mỗi whilelần lặp:
Các forvòng lặp một cách rõ ràng &=là tất cả các giá trị trong mảng kết quả với 255 ngoại trừ người cuối cùng , vì vậy chúng ta chỉ cần nhìn vào đó người cuối cùng.
Chúng tôi biết rằng chúng tôi luôn xóa chỉ một byte từ $xvà $y:
Do đó, $e*hexlà một phép nhân của hai giá trị byte đơn, có nghĩa là nó nằm trong phạm vi 0 — 0xFE01.
Theo giả thuyết quy nạp, $r[$i]là một byte; do đó, $s = $r[$i] += $e*hexlà trong phạm vi 0 — 0xFF00.
Do đó, $s >> 8luôn luôn là một byte.
$yphát triển thêm 00trong mỗi lần lặp của whilevòng lặp:
Do đó, trong mỗi lần lặp của whilevòng lặp, vòng lặp bên trong sẽ forchạy thêm một lần lặp so với whilelần lặp trước .
Do đó, $r[++$i] += $s >> 8trong lần lặp cuối cùng của forvòng lặp luôn luôn thêm $s >> 8vào 0và chúng tôi đã thiết lập $s >> 8luôn luôn là một byte.
Do đó, giá trị cuối cùng được lưu trữ ở @rcuối forvòng lặp cũng là một byte đơn.
Điều này kết thúc một thử thách tuyệt vời và thú vị. Cảm ơn rất nhiều vì đã đăng nó!