लंबे समय तक, एक बार में 8 बिट


13

आपको 16-बिट मशीन दी जाती है और कहा जाता है कि मनमाने आकार के पूर्णांक को गुणा करना। आपके रजिस्टर में केवल 16-बिट संख्या हो सकती है, और सबसे बड़ी गुणा निर्देश में दो 8-बिट इनपुट होते हैं और 16-बिट परिणाम उत्पन्न करता है।

आपके प्रोग्राम को इनपुट दो मनमाने आकार के सकारात्मक संख्याओं के रूप में लेना चाहिए और उनके उत्पाद का उत्पादन करना चाहिए। प्रत्येक इनपुट नंबर को अपनी लाइन पर एक छोटे-से-बाइट बाइट सरणी के रूप में एन्कोड किया गया है, जहां प्रत्येक बाइट 2-अंकीय हेक्स संख्या है। आउटपुट समान रूप से स्वरूपित होना चाहिए। शायद एक उदाहरण के साथ सबसे अच्छा समझाया:

इनपुट

1f 4a 07
63 a3

उत्पादन

fd 66 03 a7 04

जो 477727 * 41827 = 19981887229 गुणा करता है।

आप यह मान सकते हैं कि प्रत्येक इनपुट नंबर का अंतिम (सबसे महत्वपूर्ण) नॉनजरो है, और आपके द्वारा आउटपुट किए जाने वाले नंबर का अंतिम हिस्सा नॉनजरो होना चाहिए। दोनों इनपुट नंबर 100 बाइट्स लंबे होंगे।

सबसे छोटा कोड जीतता है।

याद रखें, आपको सबसे बड़ी गुणा करने की अनुमति है 1 बाइट * 1 बाइट, और 2 बाइट्स से बड़ा कोई पूर्णांक प्रकार नहीं है!


यह भाषाओं के लिए महत्वपूर्ण है, जिसमें कोई डिफ़ॉल्ट 8-बिट प्रकार नहीं है, जैसे हास्केल।
फूजएक्सएल

1
इसके अलावा क्या? क्या हम तैयार-किए गए मनमाने आकार के अतिरिक्त कार्य करने का दिखावा कर सकते हैं? यदि नहीं, तो हम क्या जोड़ सकते हैं?
तिमवी

@ टिमी: आप कुछ भी कर सकते हैं जो एक बार में 16 बिट्स को पसंद करेंगे। जोड़ता है, पार करता है, जो भी हो। किसी भी बड़े ऑपरेशन के लिए आपको खुद को संश्लेषित करने की आवश्यकता होती है।
कीथ रान्डेल

+1 सही बाइट ऑर्डर के लिए
12M2121

जवाबों:


13

पर्ल, 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भी एक एकल बाइट है।

यह एक अद्भुत और रोमांचक चुनौती है। इसे पोस्ट करने के लिए बहुत बहुत धन्यवाद!


4

C समाधान

यह समाधान कोई इनपुट सत्यापन नहीं करता है। यह भी केवल हल्के ढंग से परीक्षण किया गया है। गति वास्तव में एक विचार नहीं था। मल्लोक की स्मृति, और यह कितना पकड़ लेता है, इसके बारे में विशेष रूप से चतुर नहीं है। पर्याप्त, और आवश्यकता से अधिक होने की गारंटी।

m () एक स्ट्रिंग को स्वीकार करता है, स्ट्रिंग में दो नई संख्याओं की अपेक्षा करता है, प्रत्येक संख्या के बाद। केवल संख्याओं की अपेक्षा करता है, वर्ण, रिक्त स्थान, और नए अंक। हमेशा एक जोड़ी होने के लिए हेक्स अंकों की अपेक्षा करता है।

कोई भी बहुक्रिया संचालन कभी भी (जानबूझकर) नहीं किया जाता है। शिफ्टिंग 8-बिट वैरिएबल पर की जाती है। एक 16-बिट जोड़ प्रदर्शन किया जाता है। कोई 32-बिट डेटा प्रकार नहीं।

हाथ से सिकोड़ें, और केवल हल्के से। संपादित करें: अधिक गति, कम वर्ण: डी संकलक के साथ चेतावनी के साथ जीसीसी।

वर्ण: ६ers५

typedef unsigned char u8;
#define x calloc
#define f for
#define l p++
#define E *p>57?*p-87:*p-48
#define g(a) --i;--a;continue
void m(u8*d){short n=0,m=0,a,b,i,k,s;u8*t,*q,*r,*p=d,o;f(;*p!=10;n++,l){}l;f(;*p
!=10;m++,l){}t=x(n,1);q=x(m,1);r=x(n,1);p=d;a=n;i=0;f(;*p!=10;i++,l){if(*p==32){
g(a);}t[i]=E;t[i]<<=4;l;t[i]|=E;}a/=2;b=m;i=0;l;f(;*p!=10;i++,l){if(*p==32){g(b)
;}q[i]=E;q[i]<<=4;l;q[i]|=E;}b/=2;f(k=0;k<8*b;k++){if(q[0]&1){o=0;f(i=0;i<n;i++)
{s=o+t[i]+r[i];o=s>>8;r[i]=s&255;}}f(i=n;i;i--){o=t[i-1]>>7&1;t[i-1]*=2;if(i!=n)
t[i]|=o;}f(i=0;i<m;i++){o=q[i]&1;q[i]/=2;if(i)q[i-1]|=(o<<7);}}k=(r[a+b-1]==0)?a
+b-1:b+a;f(i=0;i<k;i++){printf("%02x ",r[i]);}putchar(10);}

आप इसके साथ परीक्षण कर सकते हैं:

int main(void){
  m("1f 4a 07\n63 a3\n");
  m("ff ff ff ff\nff ff ff ff\n");
  m("10 20 30 40\n50 60 70\n");
  m("01 02 03 04 05 06\n01 01 01\n");
  m("00 00 00 00 00 00 00 00 00 00 00 00 01\n00 00 00 00 00 00 00 00 02\n");
  return 0;
}

परिणाम:

$ ./long 
fd 66 03 a7 04 
01 00 00 00 fe ff ff ff 
00 05 10 22 34 2d 1c 
01 03 06 09 0c 0f 0b 06 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 

3

OCaml + बैटरियों, 362 वर्ण

एक मानक O (n * m) स्कूलबॉय गुणा एल्गोरिथ्म। ध्यान दें कि चुनौती की आवश्यकताओं को पूरा करने के लिए, संचालन स्ट्रिंग्स के बाइट्स पर किए जाते हैं, जो ओमेक्एल में हैं (आसानी से, इस मामले में) परस्पर। यह भी ध्यान दें कि संचायक sकभी भी 16 बिट्स को ओवरफ्लो नहीं करता है, क्योंकि 2 (2 ^ 8 - 1) + (2 ^ 8 - 1) ^ 2 = (2 ^ 8 - 1) (2 ^ 8 + 1) = 2 ^ 16 - 1 ।

let(@)=List.map
let m a b=Char.(String.(let e s=of_list(((^)"0x"|-to_int|-chr)@nsplit s" ")in
let a,b=e a,e b in let m,n=length a,length b in let c=make(m+n)'\000'in
iteri(fun i d->let s,x=ref 0,code d in iteri(fun j e->let y=code e in
s:=!s+code c.[i+j]+x*y;c.[i+j]<-chr(!s mod
256);s:=!s/256)b;c.[i+n]<-chr!s)a;join" "((code|-Printf.sprintf"%02x")@to_list c)))

उदाहरण के लिए,

# m "1f 4a 07" "63 a3" ;;
- : string = "fd 66 03 a7 04"

# m "ff ff ff ff" "ff ff ff ff" ;;
- : string = "01 00 00 00 fe ff ff ff"

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.