जीसीडी के लिए सबसे अधिक कुशल क्या है?


26

मुझे पता है कि यूक्लिड का एल्गोरिथ्म सकारात्मक पूर्णांक की एक सूची का जीसीडी (महान आम भाजक) प्राप्त करने के लिए सबसे अच्छा एल्गोरिथ्म है। लेकिन व्यवहार में आप इस एल्गोरिथ्म को विभिन्न तरीकों से कोड कर सकते हैं। (मेरे मामले में, मैंने जावा का उपयोग करने का निर्णय लिया, लेकिन C / C ++ एक और विकल्प हो सकता है)।

मुझे अपने कार्यक्रम में सबसे कुशल कोड का उपयोग करने की आवश्यकता है।

पुनरावर्ती मोड में, आप लिख सकते हैं:

static long gcd (long a, long b){
    a = Math.abs(a); b = Math.abs(b);
    return (b==0) ? a : gcd(b, a%b);
  }

और पुनरावृति मोड में, यह इस तरह दिखता है:

static long gcd (long a, long b) {
  long r, i;
  while(b!=0){
    r = a % b;
    a = b;
    b = r;
  }
  return a;
}

जीसीडी के लिए बाइनरी एल्गोरिदम भी है, जिसे इस तरह से कोडित किया जा सकता है:

int gcd (int a, int b)
{
    while(b) b ^= a ^= b ^= a %= b;
    return a;
}

3
मुझे लगता है कि यह बहुत व्यक्तिपरक है, और शायद StackOverflow के लिए बेहतर अनुकूल है। "व्यवहार में सबसे कुशल" कई (यहां तक ​​कि अप्रत्याशित) कारकों पर निर्भर करता है, जैसे कि अंतर्निहित आर्किटेकचर, मेमोरी पदानुक्रम, आकार और इनपुट का रूप आदि
Juho

5
यह वही एल्गोरिदम है जो पुनरावर्ती और पुनरावृत्त तरीके से व्यक्त किया गया है। मुझे लगता है कि यूक्लिड एल्गोरिथ्म बहुत तेजी से परिवर्तित होने के बाद से उनका अंतर नगण्य है। अपनी पसंद के अनुसार एक चुनें।
पैड

6
आप इन दोनों की रूपरेखा तैयार करने का प्रयास कर सकते हैं। चूंकि पुनरावर्ती संस्करण एक पूंछ कॉल है, इसलिए यह संभावना नहीं है कि संकलक वास्तव में लगभग समान कोड का उत्सर्जन करता है।
लुई

1
ये गलत है। जबकि b होना चाहिए! = 0, और उसके बाद a। अन्यथा यह शून्य से विभाजन पर बग करता है। यदि आप वास्तव में बड़े gcds हैं तो भी पुनरावर्तन का उपयोग न करें .... आपको स्टैक और फ़ंक्शन स्टेट्स का ढेर मिलता है ... क्यों न केवल ऊर्जावान बनें?
संकट स्ट्रींगफेलो

4
ध्यान दें कि असमान रूप से तेज़ GCD एल्गोरिदम हैं। जैसे en.wikipedia.org/wiki/Binary_GCD_algorithm
नील युवा

जवाबों:


21

आपके दो एल्गोरिदम समतुल्य हैं (कम से कम सकारात्मक पूर्णांक के लिए, नकारात्मक संस्करण में नकारात्मक पूर्णांक के साथ क्या होता है यह जावा के शब्दार्थ पर निर्भर करता है %जिसके लिए मैं दिल से नहीं जानता)। पुनरावर्ती संस्करण में, चलो और मैं का तर्क यह हो मैं वें पुनरावर्ती कॉल: एक मैंaibii

ai+1=bibi+1=aimodbi

जरूरी संस्करण में, चलो और ' मैं चरों के मान हो और की शुरुआत में मैं पाश की वें यात्रा। एक ' मैं + 1 = ' मैं' मैं + 1 = एक ' मैं हूँ ' मैंaibiabi

ai+1=bibi+1=aimodbi

एक समानता नोटिस? आपका अनिवार्य संस्करण और आपके पुनरावर्ती संस्करण बिल्कुल समान मूल्यों की गणना कर रहे हैं। इसके अलावा, वे एक ही समय में दोनों अंत में, जब (resp। एक ' मैं = 0 ), तो वे पुनरावृत्तियों की एक ही नंबर प्रदर्शन करते हैं। इसलिए एल्गोरिदम से कहें तो दोनों में कोई अंतर नहीं है। किसी भी अंतर को लागू करने की बात होगी, संकलक पर अत्यधिक निर्भर, यह जिस हार्डवेयर पर चलता है, और संभवतः संभवतः ऑपरेटिंग सिस्टम और अन्य कार्यक्रम समवर्ती रूप से चल रहे हैं।ai=0ai=0

पुनरावर्ती संस्करण केवल पूंछ पुनरावर्ती कॉल करता है । अत्यावश्यक भाषाओं के लिए अधिकांश कंपाइलर इनका अनुकूलन नहीं करते हैं, और इसलिए यह संभावना है कि जो कोड वे उत्पन्न करते हैं वह प्रत्येक पुनरावृत्ति पर स्टैक फ्रेम बनाने में थोड़ा समय और मेमोरी बर्बाद करेगा। एक संकलक के साथ जो पूंछ कॉल का अनुकूलन करता है (कार्यात्मक भाषाओं के लिए कंपाइलर लगभग हमेशा करते हैं), उत्पन्न मशीन कोड दोनों के लिए समान हो सकता है (यह मानते हुए कि आप उन कॉलों का सामंजस्य स्थापित करते हैं abs)।


8

संख्या के लिए जो छोटे हैं, बाइनरी जीसीडी एल्गोरिथ्म पर्याप्त है।

जीएमपी, एक अच्छी तरह से बनाए रखा और वास्तविक दुनिया का परीक्षण किया गया पुस्तकालय, एक विशेष दहलीज से गुजरने के बाद एक विशेष आधा जीसीडी एल्गोरिथ्म में बदल जाएगा, लेहमर एल्गोरिथ्म का एक सामान्यीकरण। मानक यूक्लिडियन एल्गोरिदम में सुधार के लिए लेहमर मैट्रिक्स गुणन का उपयोग करता है। डॉक्स के अनुसार, एचजीसीडी और जीसीडी दोनों का एसिम्प्टोटिक रनिंग टाइम है O(M(N)*log(N)), जहां M(N)दो एन- लिम्ब संख्याओं को गुणा करने का समय है।

उनके एल्गोरिथ्म पर पूर्ण विवरण यहां पाया जा सकता है


लिंक वास्तव में पूर्ण विवरण प्रदान नहीं करता है, और यह भी परिभाषित नहीं करता है कि एक "अंग" क्या है ...
इइनपोकलम - मोनिका की बहाली


2

जैसा कि मुझे पता है कि जावा सामान्य रूप से पूंछ पुनरावृत्ति अनुकूलन का समर्थन नहीं करता है, लेकिन आप इसके लिए अपने जावा कार्यान्वयन का परीक्षण कर सकते हैं; यदि यह इसका समर्थन नहीं करता है, तो एक साधारण- forलूप तेजी से होना चाहिए, अन्यथा पुनरावृत्ति केवल तेज होनी चाहिए। दूसरी ओर, ये थोड़े अनुकूलन वाले होते हैं, ऐसा कोड चुनें जो आपको लगता है कि आसान और अधिक पठनीय है।

मुझे यह भी ध्यान रखना चाहिए कि सबसे तेज जीसीडी एल्गोरिथ्म यूक्लिड का एल्गोरिथ्म नहीं है, लेहमर का एल्गोरिथ्म थोड़ा तेज है।


क्या आपका मतलब है जहाँ तक मुझे पता है ? क्या आपका मतलब है कि भाषा विनिर्देश इस अनुकूलन को अनिवार्य नहीं करता है (यदि यह किया गया तो आश्चर्य होगा), या यह कि अधिकांश कार्यान्वयन इसे लागू नहीं करते हैं?
PJTraill

1

पहले, एक तंग पाश को बदलने के लिए पुनरावृत्ति का उपयोग न करें। यह धीमा है। इसे बाहर अनुकूलित करने के लिए संकलक पर भरोसा न करें। दूसरे, आपके कोड में, आप हर पुनरावर्ती कॉल के भीतर Math.abs () कहते हैं, जो बेकार है।

अपने पाश में, आप आसानी से अस्थायी चर से बच सकते हैं और हर समय ए और बी की अदला-बदली कर सकते हैं।

int gcd(int a, int b){
    if( a<0 ) a = -a;
    if( b<0 ) b = -b;
    while( b!=0 ){
        a %= b;
        if( a==0 ) return b;
        b %= a;
    }
    return a;
}

एक ^ = b ^ = a ^ = का उपयोग करके स्वैप करने से स्रोत छोटा हो जाता है लेकिन निष्पादित करने के लिए कई निर्देश लेता है। यह एक अस्थायी चर के साथ बोरिंग स्वैप की तुलना में धीमी होगी।


3
“पुनरावृत्ति से बचें। यह धीमी है ”- सामान्य सलाह के रूप में प्रस्तुत, यह फर्जी है। यह कंपाइलर पर निर्भर करता है। आमतौर पर, यहां तक ​​कि संकलक के साथ भी जो पुनरावृत्ति का अनुकूलन नहीं करते हैं, यह धीमा नहीं है, बस स्टैक-खपत है।
गिल्स एसओ- बुराई को रोकना '

3
लेकिन इस तरह के छोटे कोड के लिए, अंतर महत्वपूर्ण है। स्टैक-उपभोग का अर्थ है, स्मृति से लिखना और पढ़ना। यह धीमा है। ऊपर दिया गया कोड 2 रजिस्टरों पर चलता है। पुनरावृत्ति का अर्थ कॉल करना भी है, जो सशर्त कूद से अधिक लंबा है। एक पुनरावर्ती कॉल शाखा भविष्यवाणी और कठिन इनलाइन के लिए बहुत कठिन है।
फ्लोरियन एफ

-2

के लिए कम संख्या ,% काफी एक महंगी आपरेशन, शायद सरल पुनरावर्ती है

GCD[a,b] := Which[ 
   a==b , Return[a],
   b > a, Return[ GCD[a, b-a]],
   a > b, Return[ GCD[b, a-b]]
];

जल्दी है? (क्षमा करें, गणितज्ञ कोड और C ++ नहीं)


यह सही नहीं लगता है। बी == 1 के लिए, इसे 1. वापस करना चाहिए और जीसीडी [2,1000000000] धीमा हो जाएगा।
फ्लोरियन एफ

आह, हाँ, मैंने एक गलती की। फिक्स्ड (मुझे लगता है), और स्पष्ट किया।
प्रति अलेक्जेंडरसन

आम तौर पर, जीसीडी [ए, 0] को भी वापस आ जाना चाहिए। तुम्हारा हमेशा के लिए खो देता है।
फ्लोरियन एफ

मैं नीच कर रहा हूँ क्योंकि आपके उत्तर में केवल कोड है। हम इस साइट पर विचारों पर ध्यान देना पसंद करते हैं। उदाहरण के लिए,% एक महंगा ऑपरेशन क्यों है? मेरी राय में, कोड के एक टुकड़े पर अटकलें वास्तव में इस साइट के लिए एक अच्छा जवाब नहीं है।
जुहो

1
मुझे लगता है कि घटाव की तुलना में मोडुलो धीमा है, लोककथाओं पर विचार किया जा सकता है। यह दोनों छोटे पूर्णांक के लिए रखता है (घटाव आमतौर पर एक चक्र लेता है, मोडुलो शायद ही कभी होता है) और बड़े पूर्णांकों के लिए (घटाव रैखिक है, मुझे यकीन नहीं है कि सबसे अच्छा जटिलता मोडुलो के लिए क्या है लेकिन यह निश्चित रूप से इससे भी बदतर है)। बेशक आपको आवश्यक पुनरावृत्तियों की संख्या पर विचार करने की आवश्यकता है।
गिल्स एसओ- बुराई को रोकना '

-2

जीसीडी की गणना के लिए यूक्लिड एल्गोरिथ्म सबसे कुशल है:

स्टेटिक लॉन्ग gcd (लॉन्ग ए, लॉन्ग बी)
{
अगर (== ख 0)
एक वापसी;
अन्य
रिटर्न जीडीसी (, एक% बी);
}

उदाहरण:-

चलो ए = 16, बी = 10।
जीसीडी (16, 10) = जीसीडी (10, 16% 10) = जीसीडी (10, 6)
जीसीडी (10, 6) = जीसीडी (6, 10% 6) = जीसीडी (6, 4)
जीसीडी (6, 4) = जीसीडी (4, 6% 4) = जीसीडी (4, 2)
जीसीडी (4, 2) = जीसीडी (2, 4% 2) = जीसीडी (2, 0)


चूंकि B = 0 इसलिए GCD (2, 0) 2 वापस आएगा। 

4
इस सवाल का जवाब नहीं है। पूछने वाला यूक्लिड के दो संस्करण प्रस्तुत करता है और पूछता है कि कौन सा तेज है। आपको लगता है कि ऐसा नहीं लगता है और सिर्फ पुनरावर्ती संस्करण को केवल यूक्लिड का एल्गोरिथ्म घोषित किया है, और कोई सबूत नहीं है कि जो भी यह सब कुछ से तेज है।
डेविड रिचेर्बी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.