कोई भी unordered_set के बजाय सेट का उपयोग क्यों करेगा?


145

C ++ 0x पेश unordered_setकर रहा है जो कि boostऔर कई अन्य जगहों पर उपलब्ध है। मैं समझता हूँ कि लुकअप जटिलता के unordered_setसाथ हैश तालिका O(1)है। दूसरी ओर, लुकअप जटिलता वाला setएक पेड़ के अलावा कुछ भी नहीं log(n)है। पृथ्वी पर क्यों setइसके बजाय किसी का उपयोग करेंगे unordered_set? यानी setअब और जरूरत है ?


22
आपका सवाल मौलिक रूप से पूछ रहा है कि क्या अब किसी पेड़ की आवश्यकता है।
विन्को वर्सालोविच

2
मुझे लगता है कि मैंने पहली पंक्ति में यह स्पष्ट रूप से कहा था, कि यह किसी तरह का बेवकूफ सवाल है। मुझे कुछ याद आ रहा था और अब मुझे जवाब मिला :)
आरा

2
असली कारण यह है कि चीजें B & W के रूप में नहीं हैं जैसा कि वे लगते हैं। बीच में बहुत सारे ग्रेज और अन्य रंग हैं। आपको याद रखना चाहिए कि ये कंटेनर उपकरण हैं। कभी-कभी प्रदर्शन महत्वपूर्ण नहीं होता है और सुविधा कहीं अधिक सार्थक होती है। यदि सभी लोग सबसे कुशल समाधान की तलाश करते हैं, तो हम पहली बार में C ++ (पायथन का उल्लेख नहीं करने के लिए) का उपयोग कभी नहीं करते हैं और मशीन भाषा में कोड को लगातार लिखते और अनुकूलित करते हैं।
AturSams

(क्यों पृथ्वी पर कोई भी उस नाम से परे वादों के साथ कार्यान्वयन / इंटरफ़ेस के लिए एक सामान्य नाम का उपयोग करेगा, बिना लोगों के लिए एक अजीब स्थिति पैदा करेगा?)
ग्रेबर्ड

जवाबों:


219

जब, जो सेट के आइटम पर पुनरावृति करना चाहता है, उसके लिए, ऑर्डर मायने रखता है।


क्या यह सम्मिलन आदेश के अनुसार, या ऑपरेटरों का उपयोग करके वास्तविक तुलना के अनुसार किया जाता है < >?
SomeSomething

2
यह std का उपयोग करने का आदेश दिया गया है :: डिफ़ॉल्ट रूप से कम; आप इसे ओवरराइड कर सकते हैं और अपने स्वयं के तुलना ऑपरेटर की आपूर्ति कर सकते हैं। cplusplus.com/reference/set/set
चांदनी

या कभी-कभी जब आप केवल क्रमबद्ध करना चाहते हैं, भले ही आदेश कोई फर्क न पड़े।
mfnx

319

अनियंत्रित सेटों को कुछ तरीकों से अपने ओ (1) औसत पहुंच समय के लिए भुगतान करना होगा:

  • setसमान संख्या में तत्वों को संग्रहीत करने की तुलना में कम मेमोरी का उपयोग करता है unordered_set
  • तत्वों की एक छोटी संख्या के लिए , setशायद एक में लुकअप की तुलना में लुकअप तेजी से हो सकता है unordered_set
  • हालांकि कई आपरेशनों तेजी में हैं औसत मामले के लिए unordered_set, वे अक्सर के लिए गारंटी है बेहतर सबसे ज्यादा मामले जटिलताओं के लिए set(उदाहरण के लिए insert)।
  • यही कारण है कि set एक तरह तत्वों अगर तुम उन्हें क्रम में उपयोग करने के लिए चाहते हैं उपयोगी है।
  • आप कर सकते हैं कोषगत तुलना विभिन्न setसाथ <, <=, >और >=unordered_setइन कार्यों का समर्थन करने के लिए s की आवश्यकता नहीं है।


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

अच्छे बिंदु, हालाँकि यहाँ ( en.cppreference.com/w/cpp/container/unordered_set/operator_cmp ) यह कहा गया है कि हम unordered_sets की तुलना कर सकते हैं।
मिचेल uit het ब्रोक

5
"तत्वों की एक छोटी संख्या" को परिभाषित करें
संजय वर्मा

4
@SunjayVarma आमतौर पर दोनों के बीच 100 तत्व एक अच्छा कट-ऑफ है। जब संदेह में कुछ भी आपके विशिष्ट उपयोग के मामले में दोनों के परीक्षण प्रदर्शन को बदल नहीं सकता है।
नैट

3
@ मिचिलेयुथेब्रोक केवल समानता की तुलना की गई है, आदेश नहीं ( <)।
lisyarus

26

जब भी आप किसी पेड़ को हैश टेबल पर रखना पसंद करते हैं।

उदाहरण के लिए, हैश टेबल सबसे खराब स्थिति में "O (n)" हैं। ओ (1) औसत मामला है। पेड़ "ओ ( लॉग एन)" सबसे खराब हैं।


18
/ बैलेंस्ड / ट्री सबसे खराब स्थिति में O (ln n) हैं। आप ओ (एन) के पेड़ (अनिवार्य रूप से लिंक की गई सूची) के साथ समाप्त हो सकते हैं।
स्ट्रैजर

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

6
दांव: हाँ, पांडित्य होना। हालाँकि, हम C ++ में सेट के बारे में बात कर रहे हैं जो आमतौर पर एक संतुलित बाइनरी सर्च ट्री के रूप में लागू किया जाता है । हमें जटिलता के बारे में बात करने के लिए वास्तविक ऑपरेशन को निर्दिष्ट करना चाहिए। इस संदर्भ में यह स्पष्ट है कि हम लुकअप के बारे में बात कर रहे हैं।
मेहरदाद अफश्री २

1
जस्टिन एल: यह सिर्फ एक कारण है कि आप एक पेड़ पसंद कर सकते हैं। मेरे उत्तर का मूल पहली पंक्ति है। जब भी आप हैश टेबल पर ट्री डेटा स्ट्रक्चर पसंद करते हैं। ऐसे बहुत सारे मामले हैं कि पेड़ों को हैश टेबल के लिए पसंद किया जाता है। हैश टेबल विशेष रूप से "रेंज चौराहों" जैसी चीजों को चूसते हैं।
मेहरदाद अफश्री

2
stl के पेड़ लगभग सार्वभौमिक रूप से लाल-काले पेड़, एक उन्नत आत्म संतुलन वाले पेड़ हैं। वास्तव में ऐसे मामले हैं जहां ओ (एन) बदतर स्थिति में दिखता है स्वीकार्य नहीं है। एक वेब सेवा जो उपयोगकर्ता मूल्यों को संग्रहीत करने के लिए इंटरफ़ेस प्रदान करती है, उसे हैश मैप का उपयोग नहीं करना चाहिए, क्योंकि एक दुर्भावनापूर्ण उपयोगकर्ता विशेष रूप से तैयार किए गए मानों को संग्रहीत करके प्रभावी रूप से DoS बना सकता है। क्रिटिकल, टाइम सेंसिटिव सिस्टम O (n) लुकअप, एयर ट्रैफिक कंट्रोल आदि के लिए भी अनुमति नहीं दे सकते हैं। हालांकि सामान्य तौर पर आप सही होते हैं, डिफ़ॉल्ट रूप से हैश मैप्स का उपयोग करते हैं और वास्तविक जरूरत पड़ने पर ही ट्री वर्जन को स्विच करते हैं।
deft_code

14

सेट का उपयोग करें जब:

  1. हमें ऑर्डर किए गए डेटा (अलग-अलग तत्व) चाहिए।
  2. हमें डेटा (छांटे गए क्रम में) को प्रिंट / एक्सेस करना होगा।
  3. हमें तत्वों के पूर्ववर्ती / उत्तराधिकारी की आवश्यकता है।

जब unordered_set का उपयोग करें:

  1. हमें अलग-अलग तत्वों का एक सेट रखने की आवश्यकता है और कोई आदेश देने की आवश्यकता नहीं है।
  2. हमें सिंगल एलिमेंट एक्सेस की आवश्यकता है यानी कोई ट्रैवर्सल नहीं।

उदाहरण:

सेट:

इनपुट: १,,, २, ५, ३, ९

आउटपुट: 1, 2, 3, 5, 8, 9

Unordered_set:

इनपुट: १,,, २, ५, ३, ९

आउटपुट: ९ ३ १ 5 २ ५ (होश समारोह से प्रभावित शायद यह आदेश)

मुख्य रूप से अंतर:

यहां छवि विवरण दर्ज करें

नोट: (कुछ मामले setमें अधिक सुविधाजनक है) उदाहरण के लिए vectorकुंजी के रूप में उपयोग करना

set<vector<int>> s;
s.insert({1, 2});
s.insert({1, 3});
s.insert({1, 2});

for(const auto& vec:s)
    cout<<vec<<endl;   // I have override << for vector
// 1 2
// 1 3 

कारण क्योंकि ओवरराइड vector<int>में महत्वपूर्ण हो सकता है ।setvectoroperator<

लेकिन अगर आप उपयोग unordered_set<vector<int>>करते हैं vector<int>, तो आपको हैश फ़ंक्शन बनाना होगा , क्योंकि वेक्टर में हैश फ़ंक्शन नहीं है, इसलिए आपको किसी एक को परिभाषित करना होगा:

struct VectorHash {
    size_t operator()(const std::vector<int>& v) const {
        std::hash<int> hasher;
        size_t seed = 0;
        for (int i : v) {
            seed ^= hasher(i) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        }
        return seed;
    }
};

vector<vector<int>> two(){
    //unordered_set<vector<int>> s; // error vector<int> doesn't  have hash function
    unordered_set<vector<int>, VectorHash> s;
    s.insert({1, 2});
    s.insert({1, 3});
    s.insert({1, 2});

    for(const auto& vec:s)
        cout<<vec<<endl;
    // 1 2
    // 1 3
}

आप देख सकते हैं कि कुछ मामलों unordered_setमें अधिक जटिल है।

मुख्य रूप से उद्धृत: https://www.geeksforgeeks.org/set-vs-unordered_set-c-stl/ https://stackoverflow.com/a/29855973/6329006


6

क्योंकि std :: set मानक C ++ का हिस्सा है और unordered_set नहीं है। C ++ 0x एक मानक नहीं है, और न ही बूस्ट है। हम में से कई के लिए, पोर्टेबिलिटी आवश्यक है, और इसका मतलब है कि मानक से चिपके रहना।


2
अगर मैं उसे सही ढंग से समझता हूं, तो वह यह नहीं पूछ रहा है कि लोग अभी भी सेट का उपयोग क्यों करते हैं। वह C ++ 0x के बारे में खुद को सूचित कर रहा है।
जोहान्स स्काउब -

2
शायद। मुझे लगा कि सभी को पता है कि हैश टेबल और पेड़ अलग-अलग समस्याओं को हल करते हैं।

21
खैर, अब यह एक मानक है (केवल कुछ साल लगे)
क्लेटन ह्यूजेस

6

स्वीपलाइन एल्गोरिदम पर विचार करें। ये एल्गोरिदम हैश टेबल के साथ पूरी तरह से विफल होंगे, लेकिन संतुलित पेड़ों के साथ खूबसूरती से काम करेंगे। आपको स्वीपलाइन एल्गोरिथ्म का एक ठोस उदाहरण देने के लिए भाग्य के एल्गोरिथ्म पर विचार करें। http://en.wikipedia.org/wiki/Fortune%27s_algorithm


1
मुझे लगता है कि इस तरह का संदर्भ बहुत जटिल है, यह प्रश्न दिया गया है। (मुझे इसे देखना था)
हेक्टरपाल

3

एक और बात, इसके अलावा जो अन्य लोगों ने पहले ही उल्लेख किया है। जबकि एक unordered_set को एक तत्व डालने के लिए उम्मीद से amortized जटिलता हे है (1), हर अब और फिर यह होगा हे (एन) ले क्योंकि हैश तालिका जरूरतों का पुनर्गठन किया जाना है (परिवर्तन करने के लिए बाल्टी जरूरतों की संख्या) - यहां तक कि साथ एक 'अच्छा' हैश फ़ंक्शन। जैसे वेक्टर में एक तत्व डालने से हर बार O (n) लगता है क्योंकि अंतर्निहित सरणी को फिर से विभाजित करने की आवश्यकता होती है।

एक सेट में सम्मिलित करना हमेशा O (लॉग एन) पर होता है। यह कुछ अनुप्रयोगों में बेहतर हो सकता है।


3

मुझे क्षमा करें, छँटाई हुई संपत्ति के बारे में एक और बात ध्यान देने योग्य है:

यदि आप कंटेनर में डेटा की एक श्रेणी चाहते हैं , उदाहरण के लिए: आपने सेट में समय संग्रहीत किया है , और आप 2013-01-01 से 2014-01-01 तक का समय चाहते हैं।

के लिए unordered_set यह असंभव है।

बेशक, यह उदाहरण मानचित्र और unordered_map के बीच उपयोग के मामलों के लिए अधिक ठोस होगा ।


3

g++ 6.4 stdlibc ++ का आदेश दिया गया बनाम अनियंत्रित सेट बेंचमार्क

मैंने अंतर देखने के लिए इस प्रभावी लिनक्स C ++ कार्यान्वयन को बेंचमार्क किया:

यहां छवि विवरण दर्ज करें

पूर्ण बेंचमार्क विवरण और विश्लेषण में दिया गया है: C ++ में सेट किए गए STL की अंतर्निहित डेटा संरचना क्या है? और मैं उन्हें यहां नहीं दोहराऊंगा।

"BST" का अर्थ है "के साथ परीक्षण किया गया std::setऔर" हैश मैप "का अर्थ है" के साथ परीक्षण किया गया std::unordered_set। "हीप" std::priority_queueजिसके लिए मैंने विश्लेषण किया है: हीप बनाम बाइनरी सर्च ट्री (BST)

एक त्वरित सारांश के रूप में:

  • ग्राफ स्पष्ट रूप से दिखाता है कि इन स्थितियों के तहत, हैशमैप प्रविष्टि हमेशा बहुत तेज थी जब 100k से अधिक आइटम होते हैं, और आइटम की संख्या बढ़ने पर अंतर बढ़ता है

    इस गति को बढ़ावा देने की लागत यह है कि आप कुशलता से क्रम में पार करने में सक्षम नहीं हैं।

  • घटता स्पष्ट रूप से सुझाव देता है कि आदेश BST- आधारित है std::setऔर std::unordered_sethashmap आधारित है। संदर्भ उत्तर में, मैंने आगे पुष्टि की कि GDB कदम से कोड डिबगिंग करता है।

mapबनाम के लिए इसी तरह का सवाल unordered_map: क्या तुच्छ कुंजी के मामले में unordered_map पर नक्शे का उपयोग करने का कोई फायदा है?


1

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

यह भी संभव है कि किसी एक के एक्सेस करने में तेज़ हो, उसे बनाने और / या एक्सेस करते समय उपयोग की जाने वाली अनुक्रमणिका या मेमोरी बनाने का समय अधिक हो।


+1, बिग ओह संकेतन निरंतर कारकों को छुपाता है, और विशिष्ट समस्या आकारों के लिए यह अक्सर स्थिर कारक हैं जो सबसे अधिक मायने रखते हैं।
j_random_hacker 8

1

यदि आप चीजों को क्रमबद्ध करना चाहते हैं, तो आप unordered_set के बजाय सेट का उपयोग करेंगे। unordered_set का उपयोग तब सेट पर किया जाता है जब संग्रहीत ऑर्डर करने से कोई फर्क नहीं पड़ता।


1

हालांकि यह उत्तर 10 साल देर से हो सकता है, यह इंगित करने योग्य है कि std::unordered_setइसमें सुरक्षा डाउनसाइड भी है।

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

इसका उपयोग बहुत ही कुशल और सुरुचिपूर्ण इनकार-सेवा के हमलों के लिए किया जा सकता है।

हैश मानचित्रों को आंतरिक रूप से नियोजित करने वाली भाषाओं के कई (सबसे?) कार्यान्वयन इसमें शामिल हैं:

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