क्विकॉर्ट पार्टिशनिंग: होरे बनाम लोमुटो


82

कॉर्मेन में उल्लिखित दो क्विकसर्ट विभाजन विधियाँ हैं:

Hoare-Partition(A, p, r)
x = A[p]
i = p - 1
j = r + 1
while true
    repeat
        j = j - 1
    until A[j] <= x
    repeat
        i = i + 1
    until A[i] >= x
    if i < j
        swap( A[i], A[j] )
    else
        return j

तथा:

Lomuto-Partition(A, p, r)
x = A[r]
i = p - 1
for j = p to r - 1
    if A[j] <= x
        i = i + 1
        swap( A[i], A[j] )
swap( A[i +1], A[r] )
return i + 1

धुरी चुनने की विधि की उपेक्षा करना, किन स्थितियों में एक दूसरे के लिए बेहतर है? मैं उदाहरण के लिए जानता हूं कि लोमुटो अपेक्षाकृत खराब रूप से खराब होता है, जब नकली मूल्यों का एक उच्च प्रतिशत होता है (यानी जहां 2 / 3rds से अधिक सरणी समान मान है), जहां होरे उस स्थिति में ठीक प्रदर्शन करता है।

क्या अन्य विशेष मामलों में एक विभाजन विधि दूसरे की तुलना में महत्वपूर्ण है?


2
मैं किसी भी स्थिति के बारे में नहीं सोच सकता जिसमें लोमुटो होरे से बेहतर है। ऐसा लगता है कि जब भी लोमुटो अतिरिक्त स्वैप करता है A[i+1] <= x। एक क्रमबद्ध सरणी में (और यथोचित रूप से चुने गए पिवोट्स) होरे लगभग कोई स्वैप नहीं करता है और लोमुटो एक टन करता है (एक बार जे काफी छोटा हो जाता है तो सभी A[j] <= x।) मुझे क्या याद आ रहा है?
भटकते हुए तर्क

2
@WanderingLogic मुझे यकीन नहीं है, लेकिन ऐसा लगता है कि कॉर्मेन का अपनी पुस्तक में लोमुटो विभाजन का उपयोग करने का निर्णय शैक्षणिक हो सकता है - ऐसा लगता है कि एक सीधे-सीधे लूप अपरिवर्तनीय है।
रॉबर्ट एस। बार्न्स

2
ध्यान दें कि वे दो एल्गोरिदम एक ही काम नहीं करते हैं। होरे के एल्गोरिथ्म के अंत में, धुरी अंतिम स्थान पर नहीं है। आप swap(A[p], A[j])दोनों के लिए समान व्यवहार पाने के लिए होरे के अंत में जोड़ सकते हैं ।
औरेलीन ओम्स

आपको होरे के विभाजन i < jके 2 दोहराए गए लूप की भी जांच करनी चाहिए ।
औरेलिन ओम्स

@ AurélienOoms कोड को सीधे पुस्तक से कॉपी किया जाता है।
रॉबर्ट एस। बार्न्स

जवाबों:


92

शैक्षणिक आयाम

इसकी सादगी के कारण लोमुटो की विभाजन विधि को लागू करना आसान हो सकता है। सॉर्टिंग पर जॉन बेंटले के प्रोग्रामिंग पर्ल में एक अच्छा किस्सा है :

"क्विकॉर्ट की अधिकांश चर्चाएं दो दृष्टिकोण सूचकांकों [...] [यानी होरेस] पर आधारित एक विभाजन योजना का उपयोग करती हैं। हालांकि उस योजना का मूल विचार सीधा है, मैंने हमेशा विवरण को मुश्किल पाया है - मैंने एक बार दो दिनों के बेहतर हिस्से को एक छोटे से विभाजन पाश में छिपे हुए बग का पीछा करते हुए बिताया। प्रारंभिक मसौदे के एक पाठक ने शिकायत की कि मानक दो-सूचकांक विधि वास्तव में लोमुटो की तुलना में सरल है और अपनी बात बनाने के लिए कुछ कोड को स्केच किया गया है; दो बग देखने के बाद मैंने देखना बंद कर दिया। "

प्रदर्शन आयाम

व्यावहारिक उपयोग के लिए, कार्यकुशलता के लिए कार्यान्वयन में आसानी का त्याग किया जा सकता है। सैद्धांतिक आधार पर, हम प्रदर्शन की तुलना करने के लिए तत्व तुलना और स्वैप की संख्या निर्धारित कर सकते हैं। इसके अतिरिक्त, वास्तविक चलने का समय अन्य कारकों से प्रभावित होगा, जैसे कैशिंग प्रदर्शन और शाखा की गड़बड़ी।

जैसा कि नीचे दिखाया गया है, एल्गोरिदम स्वैप की संख्या को छोड़कर यादृच्छिक क्रमपरिवर्तन पर बहुत समान है । वहाँ लोमुटो को होरे के रूप में तीन बार चाहिए !

तुलना की संख्या

लंबाई की एक सरणी को विभाजित करने के लिए तुलना का उपयोग करके दोनों तरीकों को लागू किया जा सकता है । यह अनिवार्य रूप से इष्टतम है, क्योंकि हमें यह तय करने के लिए हर तत्व की धुरी से तुलना करने की आवश्यकता है कि इसे कहां रखा जाए।n1n

स्वैप की संख्या

सरणी में तत्वों के आधार पर दोनों एल्गोरिदम के लिए स्वैप की संख्या यादृच्छिक है। यदि हम यादृच्छिक क्रमपरिवर्तन मानते हैं , अर्थात सभी तत्व अलग-अलग हैं और तत्वों के प्रत्येक क्रमांकन की समान रूप से संभावना है, तो हम स्वैप की अपेक्षित संख्या का विश्लेषण कर सकते हैं ।

जैसा कि केवल सापेक्ष क्रम मायने रखता है, हम मानते हैं कि तत्व संख्या । यह एक तत्व की रैंक और उसके मूल्य के मेल के बाद से चर्चा को आसान बनाता है।1,,n

लोमुटो की विधि

इंडेक्स वेरिएबल पूरे ऐरे को स्कैन करता है और जब भी हमें कोई तत्व पिवट से छोटा लगता है , हम एक स्वैप करते हैं। तत्वों में से , बिल्कुल वाले से छोटे हैं , इसलिए हमें स्वैप मिलता है यदि धुरी ।jA[j]x1,,nx1xx1x

समग्र अपेक्षा तो सभी पिवोट्स के औसत से होती है। में प्रत्येक मूल्य समान रूप से धुरी बनने की संभावना है (अर्थात् । ), इसलिए हमारे पास है{1,,n}1n

1nx=1n(x1)=n212.

Lomuto की विधि के साथ लंबाई की एक सरणी को विभाजित करने के लिए औसत पर स्वैप ।n

होरे की विधि

यहां, विश्लेषण थोड़ा अधिक पेचीदा है: यहां तक ​​कि पिवट ठीक करने पर , स्वैप की संख्या यादृच्छिक रहती है।x

अधिक सटीक: सूचक और एक दूसरे की ओर तब तक चलते हैं जब तक वे पार नहीं हो जाते, जो हमेशा पर होता है (होरे के विभाजन एल्गोरिथ्म की शुद्धता के द्वारा!)। यह सरणी को प्रभावी रूप से दो भागों में विभाजित करता है: एक बायां भाग जो कि द्वारा स्कैन किया गया है और एक दायां भाग द्वारा स्कैन किया गया है ।ijxij

अब, "गलत" तत्वों की प्रत्येक जोड़ी के लिए एक स्वैप किया जाता है , अर्थात एक बड़ा तत्व ( से बड़ा , इस प्रकार दाएं विभाजन से संबंधित) जो वर्तमान में बाएं भाग में स्थित है और एक छोटा तत्व सही भाग में स्थित है। ध्यान दें कि यह जोड़ी बनाने वाले हमेशा काम करते हैं, यानी दाएं हिस्से में शुरू में छोटे तत्वों की संख्या बाएं हिस्से में बड़े तत्वों की संख्या के बराबर होती है।x

एक दिखा सकता है कि इन जोड़ियों की संख्या अतिशयोक्तिपूर्ण रूप से वितरित की गई है: बड़े तत्वों के लिए हम बेतरतीब ढंग से उनके पदों को खींचते हैं और स्थिति रखते हैं बचा हुआ हिस्सा। तदनुसार, जोड़े की अपेक्षित संख्या है जिसे देखते हुए धुरी ।Hyp(n1,nx,x1)nxx1(nx)(x1)/(n1)x

अंत में, हम होरे के विभाजन के लिए स्वैप की समग्र अपेक्षित संख्या प्राप्त करने के लिए सभी धुरी मूल्यों पर फिर से औसत रखते हैं:

1nx=1n(nx)(x1)n1=n613.

(अधिक विस्तृत विवरण मेरे गुरु की थीसिस में पाया जा सकता है , पृष्ठ 29।)

मेमोरी एक्सेस पैटर्न

दोनों एल्गोरिदम दो बिंदुओं को सरणी में उपयोग करते हैं जो इसे क्रमिक रूप से स्कैन करते हैं । इसलिए दोनों लगभग इष्टतम wrt कैशिंग व्यवहार करते हैं।

समान तत्व और पहले से ही क्रमबद्ध सूची

जैसा कि पहले से ही भटकने वाले तर्क द्वारा उल्लेख किया गया है, एल्गोरिदम का प्रदर्शन उन सूचियों के लिए अधिक भिन्न होता है जो यादृच्छिक क्रमपरिवर्तन नहीं हैं।

पहले से छांटे गए सरणी पर, होरे की विधि कभी भी स्वैप नहीं होती है , क्योंकि कोई गलत जोड़े नहीं हैं (ऊपर देखें), जबकि लोमुटो की विधि अभी भी अपने लगभग स्वैप करती है!n/2

समान तत्वों की उपस्थिति को क्विकॉर्ट में विशेष देखभाल की आवश्यकता होती है। (मैंने स्वयं इस जाल में कदम रखा; मेरे गुरु की थीसिस , पृष्ठ 36, "समय पर अनुकूलन के लिए कथा" के लिए देखें) एक चरम उदाहरण के रूप में विचार करें जो एस से भरा है । इस तरह के एक सरणी पर, होरे की विधि हर जोड़ी तत्वों के लिए एक स्वैप करती है - जो कि होरे के विभाजन के लिए सबसे खराब स्थिति है - लेकिन और हमेशा सरणी के बीच में मिलते हैं। इस प्रकार, हमारे पास इष्टतम विभाजन है और कुल चलने का समय ।0ijO(nlogn)

लोम्यूटो की विधि सभी सरणी पर बहुत अधिक मूर्खतापूर्ण व्यवहार करती है : तुलना हमेशा सच होगी, इसलिए हम हर एक तत्व के लिए एक स्वैप करते हैं ! लेकिन इससे भी बदतर: लूप के बाद, हमारे पास हमेशा , इसलिए हम सबसे खराब स्थिति विभाजन का निरीक्षण करते हैं, जिससे समग्र प्रदर्शन नीचा हो जाता है !0A[j] <= xi=nΘ(n2)

निष्कर्ष

लोमुटो की विधि सरल और लागू करने में आसान है, लेकिन इसका उपयोग लाइब्रेरी सॉर्टिंग विधि को लागू करने के लिए नहीं किया जाना चाहिए।


16
वाह, यह एक विस्तृत जवाब है। अच्छी तरह से किया!
राफेल

राफेल के साथ सहमत होना है, वास्तव में अच्छा जवाब!
रॉबर्ट एस। बार्न्स

1
मैं एक छोटा स्पष्टीकरण दूंगा, कि कुल तत्वों के लिए अद्वितीय तत्वों का अनुपात कम हो जाता है, लोमुटो की तुलना की संख्या होरे की तुलना में काफी तेजी से बढ़ती है। लूमुटो के हिस्से में खराब विभाजन और होरे के हिस्से में अच्छे औसत विभाजन के कारण यह संभव है।
रॉबर्ट एस। बार्न्स

दो तरीकों की शानदार व्याख्या! धन्यवाद!
v kouk

आप आसानी से लोमुटो विधि का एक प्रकार बना सकते हैं जो धुरी के बराबर सभी तत्वों को निकाल सकता है, और उन्हें पुनरावृत्ति से बाहर कर सकता है, हालांकि मुझे यकीन नहीं है कि यह औसत मामले में मदद करेगा या बाधा देगा।
जकुब नारबस्की

5

कुछ टिप्पणियाँ उत्कृष्ट सेबेस्टियन उत्तर में जोड़ दी गईं।

मैं सामान्य रूप से विभाजन पुनर्व्यवस्था एल्गोरिदम के बारे में बात करने जा रहा हूं और क्विकॉर्ट के लिए इसके विशेष उपयोग के बारे में नहीं ।

स्थिरता

Lomuto एल्गोरिथ्म है semistable : संतोषजनक नहीं तत्वों के सापेक्ष क्रम विधेय संरक्षित है। होरे का एल्गोरिथ्म अस्थिर है।

एलीमेंट एक्सेस पैटर्न

लोमुटो के एल्गोरिथ्म का उपयोग सिंगली लिंक्ड लिस्ट या इसी तरह के फॉरवर्ड-ओनली डेटा स्ट्रक्चर्स के साथ किया जा सकता है। होरे के एल्गोरिथ्म को द्विदिश की आवश्यकता है ।

तुलना की संख्या

लोमुटो के एल्गोरिथ्म को लंबाई अनुक्रम को विभाजित करने के लिए विधेय के अनुप्रयोगों का प्रदर्शन किया जा सकता है । (होरे का भी)।n1n

लेकिन ऐसा करने के लिए हमें 2 गुणों का त्याग करना होगा:

  1. विभाजन का अनुक्रम खाली नहीं होना चाहिए।
  2. एल्गोरिथ्म विभाजन बिंदु को वापस करने में असमर्थ है।

अगर हमें इन 2 गुणों में से किसी की आवश्यकता है, तो हमारे पास तुलना करके एल्गोरिथम को लागू करने के अलावा कोई विकल्प नहीं होगा ।n

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