भोली गुणा की तुलना में फैक्टरियल एल्गोरिदम अधिक कुशल है


38

मुझे पता है कि पुनरावृत्तियों और पुनरावर्ती (उदाहरण के n * factorial(n-1)लिए जैसे) का उपयोग करके फैक्टरियल के लिए कोड कैसे बनाया जाए । मैंने एक पाठ्यपुस्तक में पढ़ा (बिना कोई स्पष्टीकरण दिए) कहा गया है कि पुनरावर्ती रूप से विभाजित करके भाज्य के लिए कोडिंग का और भी अधिक कुशल तरीका है।

मैं समझता हूं कि ऐसा क्यों हो सकता है। हालांकि मैं इसे अपने दम पर कोडिंग करने की कोशिश करना चाहता था, और मुझे नहीं लगता कि मुझे पता है कि हालांकि कहां से शुरू करना है। एक मित्र ने सुझाव दिया कि मैं पहले आधार के मामले लिखता हूँ। और मैं सरणियों का उपयोग करने के बारे में सोच रहा था ताकि मैं संख्याओं पर नज़र रख सकूं ... लेकिन मैं वास्तव में इस तरह के कोड को डिजाइन करने का कोई तरीका नहीं देख सकता।

मुझे किस तरह की तकनीकों पर शोध करना चाहिए?

जवाबों:


40

सबसे अच्छा एल्गोरिथ्म जो ज्ञात है वह मुख्य शक्तियों के उत्पाद के रूप में फैक्टरियल को व्यक्त करने के लिए है। एक छलनी दृष्टिकोण का उपयोग करके प्रत्येक प्राइम के लिए सही तरीके से और साथ ही साथ जल्दी से सही शक्ति निर्धारित कर सकते हैं। प्रत्येक शक्ति का कम्प्यूटिंग दोहराया स्क्वैरिंग का उपयोग करके कुशलता से किया जा सकता है, और फिर कारकों को एक साथ गुणा किया जाता है। यह पीटर बी बोरवेइन द्वारा वर्णित किया गया था, कैलकुलेटिंग फैक्टर्स की जटिलता पर , जर्नल ऑफ़ अल्गोरिदम 6 6 376–80, 1985। ( पीडीएफ ) संक्षेप में,परिभाषा का उपयोग करते समय आवश्यक समय की तुलना में समय में गणना की जा सकती है ।n!O(n(logn)3loglogn)Ω(n2logn)

पाठ्यपुस्तक का शायद यह मतलब था कि फूट डालो और जीतो विधि थी। कोई भी उत्पाद के नियमित पैटर्न का उपयोग करके गुणन को कम कर सकता है ।n1

चलोएक सुविधाजनक संकेतन के रूप में को निरूपित करें । के कारकों को पुनर्व्यवस्थित करें as अब कुछ पूर्णांक लिए मान लीजिए । (यह निम्नलिखित चर्चा में जटिलताओं से बचने के लिए एक उपयोगी धारणा है, और विचार को सामान्य तक बढ़ाया जा सकता है ।) फिरऔर इस पुनरावृत्ति का विस्तार करके, कम्प्यूटिंग1 3 5 ( 2 n - 1 ) ( 2 n ) ! = 1 2 3 ( 2 n ) ( 2 n ) ! = एन ! 2 n3 5 7 ( 2 n - 1 ) n = 2 k k >n?135(2n1)(2n)!=123(2n)

(2n)!=n!2n357(2n1).
n=2kएन ( 2 k ) ! = ( 2 k - 1 ) ! 2 2 k - 1 ( 2 k - 1 ) ? ( के ) ! = ( 2 2 कश्मीर - 1 + 2 कश्मीर - 2 + + 2 0 ) कश्मीर - 1 Π मैं = 0 ( 2 मैं )k>0n(2k)!=(2k1)!22k1(2k1)?( k - ) ?
(2k)!=(22k1+2k2++20)i=0k1(2i)?=(22k1)i=1k1(2i)?.
(2k1)?और प्रत्येक चरण में आंशिक उत्पादों को गुणा करना गुणा करता है। यह केवल परिभाषा का उपयोग करके गुणा से लगभग कारक का सुधार है । की शक्ति की गणना करने के लिए कुछ अतिरिक्त परिचालनों की आवश्यकता होती है , लेकिन बाइनरी अंकगणित में यह सस्ते में किया जा सकता है (इसके अनुसार क्या ठीक है, इसके लिए बस शून्य के प्रत्यय को जोड़ने की आवश्यकता हो सकती है )।2 2 k - 2(k2)+2k1222k22 k - 122k1

निम्न रूबी कोड इसका एक सरलीकृत संस्करण लागू करता है। यह recomputing से नहीं बचता हैयहां तक ​​कि जहां यह ऐसा कर सकता है:n?

def oddprod(l,h)
  p = 1
  ml = (l%2>0) ? l : (l+1)
  mh = (h%2>0) ? h : (h-1)
  while ml <= mh do
    p = p * ml
    ml = ml + 2
  end
  p
end

def fact(k)
  f = 1
  for i in 1..k-1
    f *= oddprod(3, 2 ** (i + 1) - 1)
  end
  2 ** (2 ** k - 1) * f
end

print fact(15)

यहां तक ​​कि यह पहला-पास कोड तुच्छ पर सुधार करता है

f = 1; (1..32768).map{ |i| f *= i }; print f

मेरे परीक्षण में लगभग 20%।

थोड़े से काम के साथ, इसमें और सुधार किया जा सकता है, इस आवश्यकता को भी दूर किया जा सकता है कि शक्ति हो ( व्यापक चर्चा देखें )।n2


आपने एक महत्वपूर्ण कारक छोड़ दिया। बोरवेइन के कागज के अनुसार गणना समय ओ नहीं है (एन लॉग एन लॉग लॉग एन)। यह O (M (n लॉग एन) लॉग लॉग एन) है, जहां एम (एन लॉग एन) आकार एन लॉग एन के दो नंबर गुणा करने का समय है।
gnasher729

18

ध्यान रखें कि फैक्टरियल फंक्शन इतना तेजी से बढ़ता है कि आपको नाभिकीय दृष्टिकोण से अधिक कुशल तकनीकों का कोई लाभ प्राप्त करने के लिए मनमाने आकार के पूर्णांक की आवश्यकता होगी । 21 का गुट 64-बिट में फिट होने के लिए पहले से ही बहुत बड़ा है unsigned long long int

जहाँ तक मुझे पता है, गणना करने के लिए कोई एल्गोरिथ्म नहीं है(fact of ) जो गुणा करने से तेज हैnn!n

हालाँकि, जिस क्रम में आप गुणन करते हैं वह महत्वपूर्ण है। एक मशीन पूर्णांक पर गुणा एक मूल ऑपरेशन है जो एक ही समय में कोई फर्क नहीं पड़ता कि पूर्णांक का मूल्य क्या है। लेकिन मनमाने ढंग से आकार पूर्णांकों के लिए, समय लेता गुणा करने के लिए एक और के आकार पर निर्भर एक और : एक अनुभवहीन एल्गोरिथ्म समय में चल रही है (जहां है के अंकों की संख्या - आपको जो भी आधार पसंद है, जैसा कि परिणाम एक गुणक स्थिरांक के समान है)। कर रहे हैं तेजी से गुणा एल्गोरिदम , लेकिन वहाँ है एक स्पष्ट की बाध्य निचलेΘ(|a||b|)|x|xΩ(|a|+|b|)चूंकि गुणन में कम से कम सभी अंकों को पढ़ना पड़ता है। सभी ज्ञात गुणन एल्गोरिदम रैखिक रूप से तुलना में तेज़ी से बढ़ते हैं ।max(|a|,|b|)

इस पृष्ठभूमि के साथ, विकिपीडिया लेख को समझ में आना चाहिए।

चूँकि गुणन की जटिलता गुणकों के आकार पर निर्भर करती है जिसे गुणा किया जा रहा है, तो आप गुणाओं को एक क्रम में व्यवस्थित करके समय की बचत कर सकते हैं जो संख्याओं को कई गुना छोटा रखता है। यह बेहतर तरीके से काम करता है यदि आप संख्याओं को लगभग एक ही आकार के होने की व्यवस्था करते हैं। "विभाजन आधा" में है कि आपकी पाठ्यपुस्तक में पूर्णांक के एक (बहु) सेट को गुणा करने के लिए निम्नलिखित विभाजन और जीत के दृष्टिकोण शामिल हैं:

  1. संख्याओं को गुणा करने की व्यवस्था करें (शुरू में, सभी पूर्णांकों को से ) दो सेटों में जिसका उत्पाद लगभग एक ही आकार का है। यह गुणा करने की तुलना में बहुत कम महंगा है:(एक मशीन जोड़)।1n|ab||a|+|b|
  2. एल्गोरिथ्म को दो सबसेट में से प्रत्येक पर पुनरावर्ती रूप से लागू करें।
  3. दो मध्यवर्ती परिणामों को गुणा करें।

अधिक बारीकियों के लिए GMP मैनुअल देखें ।

ऐसे और भी तेज़ तरीके हैं जो न केवल कारकों को से तक पुनर्व्यवस्थित करते हैं बल्कि संख्याओं को उनके मुख्य कारक में विघटित करके विभाजित करते हैं और अधिकतर-छोटे पूर्णांकों के परिणामी लंबे उत्पाद को पुन: व्यवस्थित करते हैं। मैं सिर्फ पीटर बोरोविन द्वारा पीटर विकुसेन और कार्यान्वयन द्वारा विकिपीडिया लेख: "फैक्टरिंग की जटिलता की जटिलता" के संदर्भों का हवाला देता हूँ ।1n

¹ के कंप्यूटिंग सन्निकटन के तेज तरीके हैं, लेकिन यह किसी भी अधिक कंप्यूटिंग नहीं है, यह इसके बारे में अनुमान लगा रहा है।n!


9

चूंकि फैक्टरियल फ़ंक्शन इतनी तेजी से बढ़ता है, आपका कंप्यूटर केवल स्टोर कर सकता हैअपेक्षाकृत छोटे । उदाहरण के लिए, एक डबल तक मूल्यों को स्टोर कर सकता है। तो अगर आप कंप्यूटिंग के लिए एक बहुत तेजी से एल्गोरिथ्म चाहते हैं, आकार की तालिका का उपयोग करें ।n!n171!n!171

यदि आप या in फ़ंक्शन (या in ) में रुचि रखते हैं तो यह प्रश्न और दिलचस्प हो जाता है । इन सभी मामलों में ( सहित ), मैं वास्तव में आपकी पाठ्यपुस्तक की टिप्पणी को नहीं समझता।log(n!)ΓlogΓn!

एक तरफ के रूप में, आपके पुनरावृत्ति और पुनरावर्ती एल्गोरिदम समतुल्य हैं (फ्लोटिंग पॉइंट त्रुटियों तक), क्योंकि आप पूंछ पुनरावृत्ति का उपयोग कर रहे हैं।


"आपके पुनरावृत्त और पुनरावर्ती एल्गोरिदम समतुल्य हैं" आप उनकी असममित जटिलता का उल्लेख कर रहे हैं, है ना? पाठ्यपुस्तक में टिप्पणी के लिए, अच्छी तरह से मैं इसे किसी अन्य भाषा से अनुवाद कर रहा हूं, इसलिए शायद मेरा अनुवाद बेकार है।
user65165

पुस्तक पुनरावृत्त और पुनरावर्ती के बारे में बात करती है, और फिर टिप्पणी करती है कि यदि आप विभाजित और एन को विभाजित करने के लिए कैसे उपयोग करते हैं! आधे में आप एक तरह से तेज समाधान प्राप्त कर सकते हैं ...
2:65 पर user65165

1
समतुल्यता की मेरी धारणा पूरी तरह से औपचारिक नहीं है, लेकिन आप कह सकते हैं कि किए गए अंकगणितीय ऑपरेशन समान हैं (यदि आप पुनरावर्ती एल्गोरिदम में ऑपरेंड के आदेश को स्विच करते हैं)। एक "स्वाभाविक रूप से" अलग एल्गोरिथ्म एक अलग गणना करेगा, शायद कुछ "चाल" का उपयोग कर।
युवल फिल्मस

1
यदि आप पूर्णांक के आकार को गुणन की जटिलता में एक पैरामीटर के रूप में मानते हैं, तो समग्र जटिलता बदल सकती है भले ही अंकगणितीय संचालन "समान" हो।
Tpecatte

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