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
นี่แปลเป็นอัลกอริทึมโดยตรงซึ่งฉันจะเขียนที่นี่ใน pseudocode:
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
ฉันต้องการ space + star เพราะไบต์สุดท้ายอาจไม่มีที่ว่าง (สามารถใช้ space + ?
ด้วย) สิ่งนี้จะทำให้ไบต์ที่ถูกลบ ( x & 255
) เข้าไป$&
โดยอัตโนมัติ
y << 8
$y = "00$y"
เป็นเพียง
เป็นจริงอาร์เรย์ตัวเลขresult
@r
ในตอนท้ายแต่ละองค์ประกอบของ@r
ประกอบด้วยหนึ่งไบต์ของคำตอบ แต่ครึ่งทางผ่านการคำนวณมันอาจมีมากกว่าหนึ่งไบต์ ฉันจะพิสูจน์ให้คุณด้านล่างว่าแต่ละค่าไม่เกินสองไบต์ (16 บิต) และผลลัพธ์จะเป็นหนึ่งไบต์เสมอในตอนท้าย
ดังนั้นนี่คือรหัส Perl unraveled และแสดงความคิดเห็น:
# 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
วง:
นี่เป็นการสรุปความท้าทายที่ยอดเยี่ยมและน่าตื่นเต้น ขอบคุณมากสำหรับการโพสต์!