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
00
byte 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 >> 1
và y << 1
tương ứng. Hơn nữa, chúng tôi thêm y
chỉ 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 y
kí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:
$x
và $y
là 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 >> 8
tô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 << 8
chỉ đơn giản là $y = "00$y"
.
Đây result
thực sự là một mảng số , @r
. Cuối cùng, mỗi phần tử @r
chứ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 while
vò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à @r
chỉ chứa các byte đơn ở đầu mỗi while
lần lặp:
Các for
vò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ừ $x
và $y
:
Do đó, $e*hex
là 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*hex
là trong phạm vi 0 — 0xFF00
.
Do đó, $s >> 8
luôn luôn là một byte.
$y
phát triển thêm 00
trong mỗi lần lặp của while
vòng lặp:
Do đó, trong mỗi lần lặp của while
vòng lặp, vòng lặp bên trong sẽ for
chạy thêm một lần lặp so với while
lần lặp trước .
Do đó, $r[++$i] += $s >> 8
trong lần lặp cuối cùng của for
vòng lặp luôn luôn thêm $s >> 8
vào 0
và chúng tôi đã thiết lập $s >> 8
luôn luôn là một byte.
Do đó, giá trị cuối cùng được lưu trữ ở @r
cuối for
vò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ó!