अभ्यास में अन्य सॉर्टिंग एल्गोरिदम की तुलना में क्विकसॉर्ट बेहतर क्यों है?


308

एक मानक एल्गोरिदम पाठ्यक्रम में हम सिखाया जाता है कि quicksort है औसत और पर सबसे खराब स्थिति में। उसी समय, अन्य सॉर्टिंग एल्गोरिदम का अध्ययन किया जाता है जो सबसे खराब स्थिति में होते हैं (जैसे मर्जसॉर्ट और हेस्पोर्ट ), और सबसे अच्छा मामले में भी रैखिक समय ( बुलबुले की तरह ) लेकिन स्मृति की कुछ अतिरिक्त जरूरतों के साथ।O ( n 2 ) O ( n लॉग एन )O(nlogn)O(n2)O(nlogn)

कुछ अधिक चलने वाले समय में तेज नज़र के बाद यह कहना स्वाभाविक है कि क्विकॉर्ट दूसरों की तरह कुशल नहीं होना चाहिए

इसके अलावा, विचार करें कि छात्र बुनियादी प्रोग्रामिंग पाठ्यक्रमों में सीखते हैं कि पुनरावृत्ति वास्तव में सामान्य नहीं है क्योंकि यह बहुत अधिक मेमोरी का उपयोग कर सकता है, आदि इसलिए (और भले ही यह एक वास्तविक तर्क नहीं है), इससे यह विचार मिलता है कि क्विकॉर्ट्स नहीं हो सकता है वास्तव में अच्छा है क्योंकि यह एक पुनरावर्ती एल्गोरिथ्म है।

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


अद्यतन 1: एक विहित जवाब कह रही है कि स्थिरांक में शामिल औसत मामले के अन्य में शामिल स्थिरांक तुलना में छोटे होते एल्गोरिदम। हालाँकि, मुझे अभी तक इसका एक उचित औचित्य देखना है, केवल सहज ज्ञान युक्त विचारों के बजाय सटीक गणना के साथ।O ( n लॉग एन )O(nlogn)O(nlogn)

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


अपडेट 2: कई वेब पेज हैं जो कि छंटाई एल्गोरिदम की तुलना की पेशकश करते हैं, दूसरों की तुलना में कुछ कट्टरपंथी (सबसे विशेष रूप से सॉर्टिंग-अल्गोरिद्म डॉट कॉम )। एक अच्छा दृश्य सहायता प्रस्तुत करने के अलावा, यह दृष्टिकोण मेरे प्रश्न का उत्तर नहीं देता है।


2
मर्ज सॉर्ट सबसे खराब स्थिति में है, और पूर्णांकों की एक सरणी को छांटता है जहां पूर्णांक के आकार पर एक ज्ञात बाउंड को समय में एक काउंटिंग सॉर्ट के साथ किया जा सकता है । O ( n )O(nlogn)O(n)
कार्ल मम्मर्ट

13
Sorting-algorithms.com में छँटाई एल्गोरिदम की एक पूरी तरह से तुलना है।
जो

2
विज्ञापन अपडेट 1: मैं अनुमान लगाता हूं कि आपके पास कठोर विश्लेषण या यथार्थवादी धारणा हो सकती है। मैंने दोनों को नहीं देखा है। उदाहरण के लिए, अधिकांश औपचारिक विश्लेषण केवल तुलनाओं की गणना करते हैं।
राफेल

9
इस सवाल ने प्रोग्रामर पर हाल ही में एक प्रतियोगिता जीती ।
राफेल

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

जवाबों:


215

संक्षिप्त जवाब

कैश दक्षता तर्क पहले से ही विस्तार से बताया गया है। इसके अलावा, एक आंतरिक तर्क है, क्विकॉर्ट तेज़ क्यों है। यदि इसे दो "क्रॉसिंग पॉइंटर्स" के साथ लागू किया जाता है, जैसे कि यहाँ , आंतरिक छोरों का शरीर बहुत छोटा है। जैसा कि इस कोड को सबसे अधिक बार निष्पादित किया जाता है, यह भुगतान करता है।

लंबा जवाब

सबसे पहले,

औसत प्रकरण मौजूद नहीं है!

जैसा कि सबसे अच्छा और सबसे खराब मामला अक्सर चरम पर होता है शायद ही कभी अभ्यास में होता है, औसत केस विश्लेषण किया जाता है। लेकिन कोई भी औसत केस विश्लेषण इनपुट के कुछ वितरण को मान लेता है ! छंटाई के लिए, विशिष्ट विकल्प यादृच्छिक क्रमचय मॉडल (विकिपीडिया पर शांति से ग्रहण किया गया) है।

क्यों नोटेशन?O

एल्गोरिदम के विश्लेषण में स्थिरांक का निर्वहन एक मुख्य कारण से किया जाता है: अगर मुझे सटीक चलने के समय में दिलचस्पी है , तो मुझे सभी शामिल बुनियादी कार्यों की लागत (सापेक्ष) की आवश्यकता है (यहां तक ​​कि अभी भी कैशिंग मुद्दों की अनदेखी, आधुनिक प्रोसेसर में पाइपलाइनिंग ...)। गणितीय विश्लेषण यह गिन सकता है कि प्रत्येक निर्देश को कितनी बार निष्पादित किया जाता है, लेकिन एकल निर्देशों का समय प्रोसेसर के विवरण पर निर्भर करता है, जैसे कि 32-बिट पूर्णांक गुणन में कितना समय लगता है।

दो तरीके हैं:

  1. कुछ मशीन मॉडल को ठीक करें।

    यह लेखक द्वारा आविष्कार किए गए एक कृत्रिम "विशिष्ट" कंप्यूटर के लिए डॉन नूथ की पुस्तक श्रृंखला "द आर्ट ऑफ कंप्यूटर प्रोग्रामिंग" में किया गया है। वॉल्यूम 3 में आपको कई सॉर्टिंग एल्गोरिदम के लिए सटीक औसत केस परिणाम मिलते हैं , उदाहरण के लिए

    • क्विकसॉर्ट:11.667(n+1)ln(n)1.74n18.74
    • विलय:12.5nln(n)
    • हीप्सोर्ट: 16nln(n)+0.01n
    • निवेशन: [ स्रोत ]2.25n2+7.75n3ln(n) कई सॉर्टिंग एल्गोरिदम के रनटाइम

    इन परिणामों से संकेत मिलता है कि क्विकसॉर्ट सबसे तेज है। लेकिन, यह केवल नूथ की कृत्रिम मशीन पर सिद्ध होता है, यह जरूरी नहीं कि आपके x86 पीसी को कहने के लिए कुछ भी हो। यह भी ध्यान दें कि एल्गोरिदम छोटे इनपुट के लिए अलग से संबंधित हैं:
    छोटे इनपुट के लिए कई सॉर्टिंग एल्गोरिदम के रनटाइम
    [ स्रोत ]

  2. सार मूल संचालन का विश्लेषण करें ।

    तुलना आधारित छँटाई के लिए, यह आम तौर पर स्वैप और प्रमुख तुलना है । रॉबर्ट सिडगविक की पुस्तकों में, उदाहरण के लिए "एल्गोरिदम" , इस दृष्टिकोण का पीछा किया जाता है। तुम वहाँ खोजो

    • Quicksort: तुलना और औसत पर स्वैप12nln(n)13nln(n)
    • मर्जेसर्ट: तुलना, लेकिन तक ऐरे एक्सेसिस (मर्ज़ोर्टॉर्ट स्वैप आधारित नहीं है, इसलिए हम इसे गिन नहीं सकते)।1.44nln(n)8.66nln(n)
    • सम्मिलन: तुलना और स्वैप औसतन।14n214n2

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

अन्य इनपुट वितरण

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


8
टाइप 2 के परिणाम 2 प्रकार के परिणामों में तब्दील हो सकते हैं। मशीन-निर्भर स्थिरांक डालकर। इसलिए, मैं तर्क करूंगा कि 2. एक बेहतर तरीका है।
राफेल

2
@ राफेल +1। मुझे लगता है कि आप मान रहे हैं कि मशीन-निर्भर कार्यान्वयन-निर्भर भी है, है ना? मेरा मतलब है, तेज मशीन + खराब कार्यान्वयन शायद बहुत कुशल नहीं है।
जानो

2
@ जानोमा I ने विश्लेषण किया एल्गोरिथ्म को बहुत विस्तृत रूप में दिया जाना चाहिए (जैसा कि विश्लेषण विस्तृत है) और अक्षर जितना संभव हो उतना कार्यान्वयन होना चाहिए। लेकिन हाँ, कार्यान्वयन में भी कारक होगा।
राफेल

3
दरअसल, टाइप 2 विश्लेषण व्यवहार में हीन है। वास्तविक दुनिया की मशीनें इतनी जटिल हैं कि टाइप 2 से परिणाम टाइप 1 के लिए संभवत: अनुवादित नहीं किए जा सकते हैं। इसकी तुलना टाइप 1 करने के लिए: प्रयोगात्मक रनिंग समय की साजिश रचने में 5 मिनट लगते हैं।
जूल्स

4
@ जूल्स: "प्रयोगात्मक चल रहे समय की साजिश रचने" टाइप 1 नहीं है ; यह किसी प्रकार का औपचारिक विश्लेषण नहीं है और यह अन्य मशीनों के लिए हस्तांतरणीय नहीं है। इसलिए हम औपचारिक विश्लेषण करते हैं, आखिरकार।
राफेल

78

इस प्रश्न के संबंध में कई बिंदु दिए जा सकते हैं।

क्विकॉर्ट आमतौर पर तेज है

हालांकि क्वॉर्ट्सर्ट में सबसे खराब स्थिति व्यवहार है, यह आमतौर पर तेज है: यादृच्छिक धुरी चयन को मानते हुए, एक बहुत बड़ी संभावना है कि हम कुछ संख्या को चुनें जो इनपुट को दो समान आकार के सबसेट में अलग करती है, जो वास्तव में हम चाहते हैं। की है।O(n2)

विशेष रूप से, यहां तक ​​कि अगर हम एक धुरी को चुनते हैं जो प्रत्येक 10 विभाजन (जो एक meh विभाजन है) में 10% -90% विभाजन बनाता है, और 1 तत्व - तत्व विभाजित होता है अन्यथा (जो सबसे खराब विभाजन आपको मिल सकता है) , हमारा चलने का समय अभी भी (ध्यान दें कि यह स्थिरांक को एक बिंदु तक उड़ा देगा कि मर्ज सॉर्ट शायद तेज है)।n1O(nlogn)

क्विकॉर्ट आमतौर पर ज्यादातर प्रकारों से तेज होता है

Quicksort आमतौर पर की तुलना में धीमे होते हैं (जैसे, इसके रनिंग टाइम के साथ सम्मिलन सॉर्ट ), क्योंकि बड़े उनका रनिंग टाइम फट जाता है।O(nlogn)O(n2)n

एक और कारण है कि क्विकॉर्ट्स अन्य एल्गोरिदम जैसे हीप्सॉर्ट की तुलना में व्यवहार में इतना तेज है , ऐसा इसलिए है क्योंकि यह अपेक्षाकृत कैश-कुशल है। इसका रनिंग टाइम वास्तव में , जहां ब्लॉक का आकार है। दूसरी ओर, हीप्सोर्ट के पास ऐसा कोई भी स्पीडअप नहीं है: यह मेमोरी कैश-कुशलता तक पहुँचने में बिलकुल नहीं है।O(nlogn)O(nBlog(nB))B

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

कैशे दक्षता को और बेहतर किया जा सकता है , जहां हमारी मुख्य मेमोरी का आकार है , अगर हम -way Quicksort का उपयोग करते हैं । ध्यान दें कि मर्जसॉर्ट में क्विकसॉर्ट के समान कैश-दक्षता भी है, और इसका के-वे संस्करण वास्तव में बेहतर प्रदर्शन (कम निरंतर कारकों के माध्यम से) है यदि स्मृति एक गंभीर बाधा है। यह अगले बिंदु को जन्म देता है: हमें क्वर्कॉर्ट से अन्य कारकों पर मर्जेसर्ट की तुलना करने की आवश्यकता होगी।एमकेO(nBlogMB(nB))Mk

क्विकॉर्ट आमतौर पर मर्जेसॉर्ट से तेज है

यह तुलना पूरी तरह से स्थिर कारकों के बारे में है (यदि हम विशिष्ट मामले पर विचार करते हैं)। विशेष रूप से, क्विकॉर्ट के लिए पिवट की एक उप-दांतेदार पसंद के बीच विकल्प है। मर्जेसर्ट के लिए पूरे इनपुट की प्रति (या इस प्रतिलिपि से बचने के लिए आवश्यक एल्गोरिथ्म की जटिलता)। यह पता चला है कि पूर्व अधिक कुशल है: इसके पीछे कोई सिद्धांत नहीं है, यह सिर्फ तेज होने के लिए होता है।

ध्यान दें कि क्विकसॉर्ट अधिक पुनरावर्ती कॉल करेगा, लेकिन स्टैक स्थान आवंटित करना सस्ता है (वास्तव में, जब तक आप स्टैक को नहीं उड़ाते हैं) मुफ्त है और आप इसे फिर से उपयोग करते हैं। (या आपके हार्ड ड्राइव करता है, तो ढेर पर एक विशाल ब्लॉक का आवंटन है वास्तव में बड़े) काफ़ी अधिक महंगा है, लेकिन दोनों कर रहे हैं ओवरहेड्स कि की तुलना में पीली काम ऊपर उल्लेख किया है।O ( लॉग एन ) O ( n )nO(logn)O(n)

अंत में, ध्यान दें कि क्विकसॉर्ट इनपुट के प्रति थोड़ा संवेदनशील है जो कि सही क्रम में होता है, इस स्थिति में यह कुछ स्वैप को छोड़ सकता है। Mergesort के पास ऐसी कोई अनुकूलन नहीं है, जो Mergesort की तुलना में Quicksort को थोड़ा तेज़ बनाता है।

उस तरह का उपयोग करें जो आपकी आवश्यकताओं के अनुरूप हो

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


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

4
@ राफेल, McIllroy के "ए किलर एडवरसरी फॉर क्विकोर्ट", सॉफ्टवेयर - प्रैक्टिस एंड एक्सपीरियंस 29 (4), 341-344 (1999) पर एक नज़र डालें। यह क्विकॉर्ट टेक समय को हमेशा के लिए बनाने के लिए एक कुटिल तकनीक का वर्णन करता है । बेंटले और मैकलरॉय का "इंजीनियरिंग एक सॉर्ट फंक्शन", सॉफ्टवेयर - अभ्यास और अनुभव 23 (11), 1249-1265 (1993) भी प्रासंगिक हो सकता है। O(n2)
वॉनब्रांड

8
"[टी] इसके पीछे कोई सिद्धांत नहीं है, यह सिर्फ तेज होने के लिए होता है।" यह कथन वैज्ञानिक दृष्टिकोण से अत्यधिक असंतोषजनक है। न्यूटन की कल्पना करें, "तितलियां उड़ जाती हैं, सेब नीचे गिर जाते हैं: इसके पीछे कोई सिद्धांत नहीं है, सेब सिर्फ गिरने के लिए होता है।"
डेविड रिचरबी

2
@ एलेक्स दस ब्रिंक, क्या आप के साथ "विशेष रूप से, एल्गोरिथ्म कैश-अनजान है " क्या मतलब है ?
हिबू ५

4
@ डेविड रिचर्बी, "यह कथन वैज्ञानिक दृष्टिकोण से अत्यधिक असंतोषजनक है": वह सिर्फ इस तथ्य के साक्षी हो सकता है कि बिना किसी दिखावा के हम उसके साथ खुश रहें। कुछ एल्गोरिथम परिवार पूर्ण औपचारिकता की कमी से ग्रस्त हैं; हैशिंग कार्य एक उदाहरण का मामला है।
हिबू ५

45

मेरे विश्वविद्यालय में एक प्रोग्रामिंग ट्यूटोरियल में, हमने छात्रों से क्विकॉर्ट्स, मर्जसॉर्ट, इंसर्शन सॉर्ट बनाम पाइथन की बिल्ट-इन लिस्ट की तुलना करने के लिए कहा । लिस्ट (जिसे टिम्सॉर्ट कहा जाता है )। अंतर्निहित सूची के बाद से प्रायोगिक परिणामों ने मुझे गहराई से आश्चर्यचकित किया। एसओटी ने अन्य छंटाई एल्गोरिदम की तुलना में बहुत बेहतर प्रदर्शन किया, यहां तक ​​कि ऐसे उदाहरणों के साथ, जिन्होंने आसानी से क्विकॉर्ट, मर्जर्ट क्रैश किया। इसलिए यह निष्कर्ष निकालना समय से पहले है कि सामान्य क्विकसर्ट कार्यान्वयन व्यवहार में सबसे अच्छा है। लेकिन मुझे यकीन है कि वहाँ क्विकसॉर्ट, या इसके कुछ हाइब्रिड संस्करण का बेहतर क्रियान्वयन हो सकता है।

यह डेविड आर। मैकाइवर द्वारा एक अच्छा ब्लॉग लेख है जो Timsort को अनुकूली विलय के रूप में समझाता है।


17
@Raphael इसे सक्सेसफुल बनाने के लिए, Timsort शॉर्ट इनपुट्स के लिए एसिम्पोटिक्स प्लस इंसर्शन सॉर्ट के लिए मर्ज होता है और कुछ ह्यूरिस्टिक्स डेटा के साथ कुशलतापूर्वक सामना करने के लिए होता है जिसमें कभी-कभार पहले से सॉर्ट किया गया फट (जो अक्सर प्रैक्टिस में होता है)। दाई: एल्गोरिथ्म के अलावा, list.sortपेशेवरों द्वारा अनुकूलित एक अंतर्निहित फ़ंक्शन होने से लाभ। एक निष्पक्ष तुलना में समान स्तर के प्रयास में एक ही भाषा में लिखे गए सभी कार्य होंगे।
गाइल्स

1
@ दाई: आप कम से कम यह बता सकते हैं कि किस परिस्थिति में किस तरह के इनपुट्स (रिस्पॉन्स, उनके डिस्ट्रीब्यूशन) के तहत (कम रैम, एक इम्प्लिमेंट समांतरन किया, ...) आपने अपने परिणाम प्राप्त किए।
राफेल

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

3
इस पर भी यहां चर्चा की गई: cstheory.stackexchange.com/a/927/74
Jukka Suomela

34

मुझे लगता है कि एक मुख्य कारण है कि क्विकसॉर्ट अन्य छँटाई एल्गोरिदम की तुलना में बहुत तेज़ है क्योंकि यह कैश-फ्रेंडली है। जब QS एक सेगमेंट के सेगमेंट को प्रोसेस करता है, तो यह सेगमेंट के आरंभ और अंत में तत्वों को एक्सेस करता है, और सेगमेंट के केंद्र की ओर बढ़ता है।

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

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


5
यह विवादास्पद स्पष्टीकरण है: मर्जर्ट कैश के अनुकूल भी है।
Dmytro Korduban

2
मुझे लगता है कि यह उत्तर मूल रूप से सही है, लेकिन यहाँ कुछ विवरण youtube.com/watch?v=aMnn0Jq0J-E
rgrig

3
संभवतया त्वरित-प्रकार की औसत स्थिति समय जटिलता के लिए गुणक स्थिर भी बेहतर है (आपके द्वारा उल्लेखित कैश कारक से स्वतंत्र)।
केव

1
आपके द्वारा उल्लेखित बिंदु त्वरित प्रकार के अन्य अच्छे गुणों की तुलना में महत्वपूर्ण नहीं है।
MMS

1
@Kaveh: "क्विक-सॉर्ट की औसत केस टाइम जटिलता के लिए गुणक स्थिरांक भी बेहतर है" क्या आपके पास इसका कोई डेटा है?
जियोर्जियो

29

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

O(nlogn)

ध्यान दें कि क्विकॉर्ट के कई संस्करण हैं (उदाहरण के लिए सेडगविक के शोध प्रबंध देखें)। वे अलग-अलग इनपुट डिस्ट्रीब्यूशन (समान, लगभग सॉर्ट किए गए, लगभग व्युत्क्रम सॉर्ट किए गए, कई डुप्लिकेट, ...), और अन्य एल्गोरिदम कुछ के लिए बेहतर हो सकते हैं।

k10


20

O(nlgn)

पीएस: सटीक होने के लिए, अन्य एल्गोरिदम की तुलना में बेहतर होना कार्य पर निर्भर है। कुछ कार्यों के लिए अन्य छँटाई एल्गोरिदम का उपयोग करना बेहतर हो सकता है।

यह सभी देखें:


3
@ जानो यह आप किस भाषा और संकलक का उपयोग करने का विषय है। लगभग सभी कार्यात्मक भाषाएँ (एमएल, लिस्प, हास्केल) ऐसे अनुकूलन कर सकती हैं जो स्टैक को बढ़ने से रोकते हैं, और अनिवार्य भाषाओं के लिए स्मार्ट कंपाइलर भी ऐसा कर सकते हैं (जीसीसी, जी ++, और मेरा मानना ​​है कि एमएसवीसी यह सब करते हैं)। उल्लेखनीय अपवाद जावा है, जो इस अनुकूलन को कभी नहीं करेगा, इसलिए यह जावा में आपकी पुनरावृत्ति को पुनरावृत्ति के रूप में फिर से लिखने के लिए समझ में आता है।
राफे केटलर

4
@JD, आप क्विकॉर्ट के साथ टेल कॉल ऑप्टिमाइज़ेशन का उपयोग नहीं कर सकते (कम से कम पूरी तरह से नहीं), क्योंकि यह खुद को दो बार कॉल करता है। आप दूसरी कॉल को दूर कर सकते हैं, लेकिन पहली कॉल को नहीं।
svick

1
@ जानोमा, आपको वास्तव में पुनरावर्ती कार्यान्वयन की आवश्यकता नहीं है। उदाहरण के लिए, यदि आप C में qsort फ़ंक्शन के कार्यान्वयन को देखते हैं, तो यह पुनरावर्ती कॉल का उपयोग नहीं करता है, और इसलिए कार्यान्वयन बहुत तेज हो जाता है।
केव

1
हीप्सोर्ट भी इन-प्लेस है, क्यूएस अक्सर तेज क्यों होता है?
केविन

6
23240

16

Θ(n2)Θ(nlogn)

दूसरा कारण यह है कि यह in-placeछंटाई करता है और वर्चुअल-मेमोरी वातावरण के साथ बहुत अच्छा काम करता है।

अद्यतन: (Janoma की और Svick टिप्पणियों के बाद)

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

निम्नलिखित दृश्य पर विचार करें:

12,30,21,8,6,9,1,7. The merge sort algorithm works as follows:

(a) 12,30,21,8    6,9,1,7  //divide stage
(b) 12,30   21,8   6,9   1,7   //divide stage
(c) 12   30   21   8   6   9   1   7   //Final divide stage
(d) 12,30   8,21   6,9   1,7   //Merge Stage
(e) 8,12,21,30   .....     // Analyze this stage

यदि आप पूरी तरह से ध्यान रखते हैं कि अंतिम चरण कैसे हो रहा है, तो पहले 12 की तुलना 8 से की जाती है और 8 की तुलना में छोटा होता है इसलिए यह पहले जाता है। अब 21 और 12 की तुलना में 12 अगेन है और आगे और इतने पर आगे बढ़ता है। यदि आप अंतिम मर्ज अर्थात 4 तत्वों को 4 अन्य तत्वों के साथ लेते हैं, तो यह बहुत अधिक मात्रा में स्थिरांक की तुलना करता है, जो त्वरित क्रम में नहीं होता है। यही कारण है कि त्वरित प्रकार को प्राथमिकता दी जाती है।


1
लेकिन क्या स्थिरांक इतना छोटा बनाता है?
svick

1
@svick क्योंकि वे क्रमबद्ध हैं in-place, कोई अतिरिक्त मेमोरी की आवश्यकता नहीं है।
0x0

Θ(nlgn)

15

वास्तविक विश्व डेटा के साथ काम करने का मेरा अनुभव है कि क्विकसॉर्ट एक खराब विकल्प है । Quicksort यादृच्छिक डेटा के साथ अच्छी तरह से काम करता है, लेकिन वास्तविक दुनिया डेटा सबसे अधिक बार यादृच्छिक नहीं है।

2008 में वापस मैंने क्विकॉर्ट के उपयोग के लिए एक हैंगिंग सॉफ्टवेयर बग को ट्रैक किया। थोड़ी देर बाद मैंने सम्मिलन प्रकार, क्विकसॉर्ट, हीप सॉर्ट और मर्ज सॉर्ट के सरल निहितार्थ लिखे और इनका परीक्षण किया। बड़े डेटा सेट पर काम करने के दौरान मेरी मर्ज सभी को बेहतर बनाती है।

तब से, मर्ज सॉर्ट मेरी पसंद का एल्गोरिथ्म है। यह सुरुचिपूर्ण है। इसे लागू करना सरल है। यह एक स्थिर प्रकार है। यह द्विघात व्यवहार को पतित नहीं करता है जैसे क्विकॉर्ट्स करता है। मैं छोटे सरणियों को सॉर्ट करने के लिए प्रविष्टि सॉर्ट पर स्विच करता हूं।

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

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


मैंने ऐसा ही कुछ देखा था क्योंकि हम पहले से ही सॉर्ट किए गए डेटा के एक बैच में सम्मिलित करने के लिए लगातार अपील / सहारा ले रहे थे। आप इस पर औसत रूप से एक रैंडमाइज्ड एस्कॉर्ट का उपयोग करके काम कर सकते हैं (और एक दुर्लभ और बेतरतीब ढंग से धीमी गति से आश्चर्यचकित हो जाते हैं), या आप हमेशा धीमी गति को सहन कर सकते हैं जो कभी भी समाप्त होने के लिए आश्चर्यजनक समय नहीं लेता है। कभी-कभी आपको क्रमबद्ध स्थिरता की भी आवश्यकता होती है। जावा एक क्विकॉर्ट वेरिएंट में मर्ज सॉर्ट का उपयोग करने से चला गया है।
रोब

@ रब यह सटीक नहीं है। जावा आज भी एक प्रकार का मर्जर्ट्स (Timsort) का उपयोग करता है। यह क्विकॉर्ट के एक संस्करण का उपयोग करता है, भी (दोहरी धुरी क्विकॉर्ट)।
एरवान लीग्रैंड

14

1 - त्वरित सॉर्ट इनहेल है (अतिरिक्त राशि की जरूरत नहीं है, एक स्थिर राशि के अलावा।)

2 - त्वरित छँटाई अन्य कुशल छँटाई एल्गोरिदम की तुलना में लागू करने के लिए आसान है।

3 - त्वरित सॉर्ट में छोटे लगातार कारक हैं जो अन्य कुशल सॉर्टिंग एल्गोरिदम की तुलना में समय चल रहा है।

अद्यतन: मर्ज सॉर्ट के लिए, आपको कुछ "मर्जिंग" करने की आवश्यकता होती है, जो विलय से पहले डेटा को स्टोर करने के लिए अतिरिक्त सरणी (ओं) की आवश्यकता होती है; लेकिन त्वरित क्रम में, आप नहीं करते। इसीलिए त्वरित सॉर्ट इन-प्लेस है। विलय के लिए कुछ अतिरिक्त तुलनाएं भी की गई हैं जो विलय के क्रम में निरंतर कारकों को बढ़ाती हैं।


3
आपके पास देखा यथा-स्थान उन्नत, पुनरावृत्ति quicksort कार्यान्वयन? वे कई चीजें हैं लेकिन "आसान" नहीं हैं।
राफेल

2
नंबर 2 मेरे सवाल का जवाब नहीं देता है , और मेरी राय में नंबर 1 और 3 को उचित औचित्य की आवश्यकता है।
१२:१२ बजे

@ राफेल: वे आसान हैं। पॉइंटर्स के बजाय एक सरणी का उपयोग करते हुए त्वरित सॉर्ट इन-प्लेस को लागू करना बहुत आसान है। और यह जगह में होने के लिए पुनरावृत्त होना जरूरी नहीं है।
एमएमएस

विलय के लिए सरणियाँ उतनी बुरी नहीं हैं। एक बार जब आप एक आइटम को एक स्रोत ढेर से गंतव्य ढेर में स्थानांतरित कर देते हैं, तो उसे अब वहां रहने की आवश्यकता नहीं है। यदि आप डायनेमिक सरणियों का उपयोग कर रहे हैं, तो विलय के समय निरंतर मेमोरी ओवरहेड होता है।
Oskar Skog

@ 1 मर्जेसट भी अयोग्य हो सकता है। @ 2 कुशल क्या परिभाषित करता है? मुझे मर्ज सॉर्ट पसंद है क्योंकि यह मेरी राय में बहुत सरल और अभी तक कुशल है। @ 3 अप्रासंगिक जब आप बड़ी मात्रा में डेटा सॉर्ट कर रहे हैं, और आवश्यकता है कि एल्गोरिथ्म कुशलता से लागू हो।
Oskar Skog

11

किन परिस्थितियों में एक विशिष्ट छँटाई एल्गोरिथ्म वास्तव में सबसे तेज़ है?

Θ(log(n)2)Θ(nlog(n)2)

Θ(nk)Θ(nm)k=2#number_of_Possible_valuesm=#maximum_length_of_keys

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

Θ(n)

5) क्या अंतर्निहित डेटा का आकार एक छोटे से मध्यम आकार के लिए बाध्य हो सकता है? उदा। n <10,000 ... 100,000,000 (अंतर्निहित वास्तुकला और डेटा संरचना पर निर्भर करता है)? हां -> बिटोनिक सॉर्ट या बैचर ऑड-इवन मर्जॉर्ट का उपयोग करें। गोटो 1)

Θ(n)Θ(n2)Θ(nlog(n)2)सबसे खराब स्थिति के समय को जाना जाता है, या हो सकता है कि कंघी की कोशिश करें। मुझे यकीन नहीं है कि शेल प्रकार या कंघी की तरह व्यवहार में काफी अच्छा प्रदर्शन करेंगे।

Θ(log(n))Θ(n)Θ(n)Θ(log(n))Θ(n2)Θ(n)Θ(n)Θ(log(n))Θ(nlog(n))

Θ(nlog(n))

क्विकॉर्ट के लिए कार्यान्वयन संकेत:

Θ(n)Θ(log(n))Θ(nlogk(k1))

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

विलय के लिए कार्यान्वयन संकेत:

1) बॉटम-अप मर्जोर्ट हमेशा टॉप-डाउन मर्ज़ोर्ट की तुलना में तेज़ होता है, क्योंकि इसके लिए किसी रिकर्सन कॉल्स की आवश्यकता नहीं होती है।

2) बहुत ही भोले मर्जर्ट को एक डबल बफर का उपयोग करके ऊपर उठाया जा सकता है और प्रत्येक चरण के बाद लौकिक सरणी से डेटा वापस कॉपी करने के बजाय बफर को स्विच कर सकता है।

3) कई वास्तविक दुनिया के आंकड़ों के लिए, एक निश्चित आकार के मर्जोर्ट की तुलना में अनुकूली मर्जोर्ट बहुत तेज है।

Θ(k)Θ(log(k))Θ(1)Θ(n)

मैंने जो लिखा है, उससे यह स्पष्ट है कि क्विकॉर्ट अक्सर सबसे तेज़ एल्गोरिथ्म नहीं है, सिवाय इसके कि जब निम्नलिखित स्थितियाँ लागू हों:

1) "कुछ" संभावित मूल्यों से अधिक हैं

2) अंतर्निहित डेटा संरचना लिंक नहीं है

3) हमें एक स्थिर आदेश की आवश्यकता नहीं है

4) डेटा काफी बड़ा है कि एक बिटोनिक सॉर्टर या बैचर ऑड-इय मर्जर्ट्स किक में मामूली उप-इष्टतम एसिम्प्टोटिक रन-टाइम

5) डेटा लगभग सॉर्ट नहीं किया जाता है और इसमें पहले से ही सॉर्ट किए गए बड़े हिस्से शामिल नहीं होते हैं

6) हम एक साथ कई स्थानों से डेटा अनुक्रम तक पहुँच सकते हैं

Θ(log(n))Θ(n)

ps: किसी को पाठ के प्रारूपण के साथ मेरी मदद करने की आवश्यकता है।


(५): Apple का सॉर्ट इम्प्लीमेंट आरोही या अवरोही क्रम में पहले और अंत में आरेंज दोनों में एक रन बनाता है। यह बहुत जल्दी है अगर ऐसे कई तत्व नहीं हैं, और इन तत्वों को बहुत प्रभावी ढंग से संभाल सकते हैं अगर उनमें से n / ln n से अधिक हो। कॉनटेननेट दो सॉर्ट किए गए सरणियों और परिणाम को क्रमबद्ध करें, और आपको एक मर्ज मिलता है
gnasher729

8

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

ab


5
Quicksort बनाम मर्ज सॉर्ट के बारे में आपका तर्क पानी को पकड़ नहीं पाता है। Quicksort एक बड़े कदम से शुरू होता है, फिर छोटी और छोटी चाल बनाता है (प्रत्येक चरण पर लगभग आधा)। मर्ज सॉर्ट एक छोटी चाल से शुरू होता है, फिर बड़ी और बड़ी चाल बनाता है (प्रत्येक चरण पर लगभग दो बार बड़ा)। यह एक को दूसरे की तुलना में अधिक कुशल होने की ओर इशारा नहीं करता है।
गिल्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.