पर्ल, 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, जो मनमाने आकार का है, इसलिए हमें इसे एक पाश में भी बदलने की आवश्यकता है। हम पर्ल में ऐसा करेंगे।
अब सब कुछ पर्ल पर अनुवाद करें:
$xऔर $yहेक्स प्रारूप में इनपुट हैं, इसलिए उनके पास पहले कम से कम महत्वपूर्ण बाइट है ।
इस प्रकार, के बजाय x >> 8मैं करता हूँ $x =~ s/.. *//s। मुझे स्पेस + स्टार की आवश्यकता है क्योंकि अंतिम बाइट में उस पर जगह नहीं हो सकती है (अंतरिक्ष + का ?भी उपयोग कर सकते हैं )। यह स्वचालित रूप से हटाए गए बाइट ( x & 255) में डालता है $&।
y << 8बस है $y = "00$y"।
resultवास्तव में, एक संख्यात्मक सरणी है @r। अंत में, प्रत्येक तत्व में @rउत्तर का एक बाइट होता है, लेकिन गणना के आधे हिस्से में एक से अधिक बाइट हो सकती हैं। मैं आपको नीचे साबित करूँगा कि प्रत्येक मान कभी भी दो बाइट्स (16 बिट्स) से अधिक नहीं होता है और इसका परिणाम हमेशा एक बाइट होता है।
तो यहाँ पर्ल कोड अप्रकाशित है और टिप्पणी की गई है:
# 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लूप पर शामिल करके साबित करूंगा :
@rशुरुआत में खाली होने से स्पष्ट रूप से इसमें 0xFF से अधिक कोई मूल्य नहीं है (क्योंकि इसमें इसका कोई मूल्य नहीं है)। यह आधार मामले को समाप्त करता है।
अब, यह दिया कि @rप्रत्येक whileपुनरावृत्ति की शुरुआत में केवल एक बाइट्स शामिल हैं :
forपाश स्पष्ट रूप से &=255 के साथ परिणाम सरणी में सभी मूल्यों है कि पिछले एक को छोड़कर इसलिए हम केवल कि पिछले एक को देखने के लिए की जरूरत है,।
हम जानते हैं कि हम हमेशा केवल एक बाइट निकालते हैं $xऔर $y:
इसलिए, $e*hexदो एकल-बाइट मानों का गुणन है, जिसका अर्थ है कि यह सीमा में है 0 — 0xFE01।
आगमनात्मक परिकल्पना द्वारा, $r[$i]एक बाइट है; इसलिए, $s = $r[$i] += $e*hexसीमा में है 0 — 0xFF00।
इसलिए, $s >> 8हमेशा एक बाइट है।
$yलूप के 00प्रत्येक पुनरावृत्ति में एक अतिरिक्त बढ़ता है while:
इसलिए, whileलूप के प्रत्येक पुनरावृत्ति में , आंतरिक forलूप पिछले whileपुनरावृत्ति की तुलना में एक अधिक पुनरावृत्ति के लिए चलता है ।
इसलिए, लूप $r[++$i] += $s >> 8के अंतिम पुनरावृत्ति में forहमेशा जोड़ता $s >> 8है 0, और हम पहले से ही स्थापित है कि $s >> 8हमेशा एक बाइट है।
इसलिए, लूप @rके अंत में संग्रहीत अंतिम मान forभी एक एकल बाइट है।
यह एक अद्भुत और रोमांचक चुनौती है। इसे पोस्ट करने के लिए बहुत बहुत धन्यवाद!