2013-04-12 8 views
5

मैं this problem from acm.timus.ru को हल करने की कोशिश कर रहा था जो मूल रूप से मुझे एक दिए गए स्ट्रिंग (अधिकतम लंबाई 5000) के विभिन्न सबस्ट्रिंग्स की संख्या आउटपुट करना चाहता है।std :: std :: map से धीमा सेट कैसे है?

समाधान मैं पेश करने के लिए के बारे में हूँ सख्त अक्षम और समय सीमा पार कर की कमी को देखते हुए फैसले के लिए बर्बाद कर रहे हैं। हालांकि, एक ही रास्ता है, जिसमें इन दो समाधान अलग (कम से कम जहाँ तक मैं देख सकता हूँ/समझता हूँ), एक std::map<long long, bool> का उपयोग करता है, अन्य का उपयोग करता है, जबकि std::set <long long> (पाश के लिए पिछले की शुरुआत देखते हैं। बाकी समान है आप किसी भी diff उपकरण द्वारा जांच सकते हैं)। नक्शा समाधान के परिणामस्वरूप "टेस्ट 3 पर समय सीमा समाप्त हो गई", जबकि सेट समाधान का परिणाम "परीक्षण सीमा पर समाप्त समय सीमा" में होता है, जिसका अर्थ है कि टेस्ट 2 ऐसा है कि नक्शा समाधान सेट समाधान की तुलना में तेज़ी से काम करता है। यह मामला है अगर मैं माइक्रोसॉफ्ट विजुअल स्टूडियो 2010 कंपाइलर चुनता हूं। अगर मैं जीसीसी चुनते हैं, तो दोनों समाधान परीक्षण 3.

पर टीएलई में परिणाम मैं कैसे कुशलतापूर्वक समस्या को हल करने के लिए नहीं कह रहा हूँ। मैं क्या पूछ रहा हूं यह है कि कोई कैसे समझा सकता है कि std::map का उपयोग std::set का उपयोग करने से अधिक कुशल हो सकता है। मैं बस इस घटना के यांत्रिकी को देखने में असफल रहा हूं और उम्मीद करता हूं कि किसी के पास कोई अंतर्दृष्टि हो सकती है।

#include <iostream> 
#include <map> 
#include <string> 
#include <vector> 

using namespace std; 

int main() 
{ 
    string s; 
    cin >> s; 
    vector <long long> p; 
    p.push_back(1); 
    for (int i = 1; i < s.size(); i++) 
     p.push_back(31 * p[i - 1]); 
    vector <long long> hash_temp; 
    hash_temp.push_back((s[0] - 'a' + 1) * p[0]); 
    for (int i = 1; i < s.size(); i++) 
     hash_temp.push_back((s[i] - 'a' + 1) * p[i] + hash_temp[i - 1]); 
    int n = s.size(); 
    int answer = 0; 
    for (int i = 1; i <= n; i++) 
    { 
     map <long long, bool> hash_ans; 
     for (int j = 0; j < n - i + 1; j++) 
     { 
     if (j == 0) 
      hash_ans[hash_temp[j + i - 1] * p[n - j - 1]] = true; 
     else 
      hash_ans[(hash_temp[j + i - 1] - hash_temp[j - 1]) * p[n - j - 1]] = true; 
     } 
     answer += hash_ans.size(); 
    } 
    cout << answer; 
} 

Code2 (, सेट का उपयोग करता टीएलई 2):

#include <iostream> 
#include <string> 
#include <vector> 
#include <set> 

using namespace std; 

int main() 
{ 
    string s; 
    cin >> s; 
    vector <long long> p; 
    p.push_back(1); 
    for (int i = 1; i < s.size(); i++) 
     p.push_back(31 * p[i - 1]); 
    vector <long long> hash_temp; 
    hash_temp.push_back((s[0] - 'a' + 1) * p[0]); 
    for (int i = 1; i < s.size(); i++) 
     hash_temp.push_back((s[i] - 'a' + 1) * p[i] + hash_temp[i - 1]); 
    int n = s.size(); 
    int answer = 0; 
    for (int i = 1; i <= n; i++) 
    { 
     set <long long> hash_ans; 
     for (int j = 0; j < n - i + 1; j++) 
     { 
     if (j == 0) 
      hash_ans.insert(hash_temp[j + i - 1] * p[n - j - 1]); 
     else 
      hash_ans.insert((hash_temp[j + i - 1] - hash_temp[j - 1]) * p[n - j - 1]); 
     } 
     answer += hash_ans.size(); 
    } 
    cout << answer; 
} 
+0

क्या आपने स्वयं को कुछ समय की तरह खुद की कोशिश की है? या यहां तक ​​कि प्रोफाइलिंग भी? – PlasmaHH

+2

@PlasmaHH: मेरा मानना ​​है कि मैंने पर्याप्त सबूत दिया है कि एक दूसरे की तुलना में धीमा है। मुझे दिलचस्पी है कि यह कैसे संभव है –

+1

@PlasmaHH: मेरा मानना ​​है कि यह एक बिल्कुल पर्याप्त सवाल है। –

उत्तर

2

वास्तविक अंतर मैं देख रहा हूँ (मुझे बताओ अगर मैं कुछ भी याद किया) है कि नक्शे मामले में तुम क्या कर रहे हैं

hash_ans[key] = true; 

जबकि सेट मामले में आप

hash_ans.insert(key); 

कर दोनों मामलों में, एक तत्व डाला जाता है, जब तक कि यह पहले से मौजूद न हो, जिसमें यह कुछ भी नहीं करता है। दोनों स्थितियों में, लुकअप को अनुसार तत्व का पता लगाने और विफलता पर डालने की आवश्यकता होती है। वहां प्रभावी ढंग से हर कार्यान्वयन में, कंटेनर एक पेड़ का उपयोग करेंगे, जिससे लुकअप समान रूप से महंगा हो जाएगा। इससे भी अधिक, सी ++ मानक में जटिलता में ओ (लॉग एन) होने के लिए वास्तव में set::insert() और map::operator[]() की आवश्यकता होती है, इसलिए दोनों कार्यान्वयन की जटिलता समान होनी चाहिए।

अब, क्या कारण है कि एक बेहतर प्रदर्शन करती है हो सकता है? एक अंतर यह है कि एक मामले में अंतर्निहित पेड़ के नोड में string होता है, जबकि दूसरे में यह pair<string const, bool> है। चूंकि जोड़ी में एक स्ट्रिंग होती है, इसलिए यह बड़ी होनी चाहिए और मशीन के रैम इंटरफेस पर अधिक दबाव डालना चाहिए, इसलिए यह स्पीडअप की व्याख्या नहीं करता है। यह क्या कर सकता है नोड आकार को बड़ा करता है ताकि अन्य नोड्स को कैश लाइन से धक्का दिया जा सके, जो मल्टी-कोर सिस्टम में प्रदर्शन के लिए खराब हो सकता है।

सारांश में, वहाँ कुछ चीजें है मैं कोशिश करता हूँ:

  1. सेट में एक ही डेटा का उपयोग
    मैं struct data: string {bool b}; यानी के साथ ऐसा होता है एक struct में स्ट्रिंग है कि एक समान होना चाहिए बंडल मानचित्र के तत्वों के रूप में बाइनरी लेआउट। तुलनित्र के रूप में, less<string> का उपयोग करें, ताकि केवल स्ट्रिंग वास्तव में तुलना में भाग लेती है।

  2. उपयोग डालने() मानचित्र
    पर मुझे नहीं लगता कि यह कोई मुद्दा होना चाहिए है, लेकिन डालने तर्क की एक प्रति लग सकते हैं, भले ही कोई डालने अंत में जगह लेता है। मुझे उम्मीद है कि यह नहीं है, इसलिए मुझे विश्वास नहीं है कि यह कुछ भी बदलेगा।

  3. डीबगिंग बंद करें
    अधिकांश कार्यान्वयन में नैदानिक ​​मोड होता है, जहां इटरेटर मान्य होते हैं। आप त्रुटियों को पकड़ने के लिए इसका उपयोग कर सकते हैं जहां सी ++ केवल "अपरिभाषित व्यवहार" कहता है, आपके कंधे और दुर्घटनाओं को झुकाता है। यह मोड अक्सर जटिलता गारंटी को पूरा नहीं करता है और यह हमेशा कुछ ओवरहेड होता है।

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

1

एक सेट केवल इस मामले में एक नक्शे से एक छोटा सा तेजी से होता है

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

यह विशेष रूप से समस्या मैं एक Ukonen SUFIX पेड़ का उपयोग कर हल किया।

+0

यही समस्या है। सेट तेज नहीं है, नक्शा है !! –

+0

@ArmenTsirunyan कृपया मेरे बाकी उत्तर पढ़ें। –

+0

मैंने –

1

यह प्रयुक्त कार्यान्वयन एल्गोरिदम पर निर्भर करता है। आमतौर पर सेट केवल मुख्य फ़ील्ड का उपयोग करके नक्शे का उपयोग करके लागू किए जाते हैं। ऐसे मामले में मानचित्र के विरोध में सेट का उपयोग करने के लिए बहुत ही मामूली ओवरहेड होगा।

+0

मुझे याद है कि एसटीएलपोर्ट में, सेट और मैप दोनों ही अंतर्निहित पेड़ कंटेनर के शीर्ष पर बनाए गए थे, इसलिए उनका प्रदर्शन समान होना चाहिए। यहां तक ​​कि यदि नहीं, तो मुझे वहां के ऊपर की ओर नहीं दिख रहा है जिसे इनलाइनिंग द्वारा हटाया नहीं जा सकता है, इसलिए मैं इस समय आपसे असहमत हूं। –

+0

@ डूमस्टर मैंने "बहुत मामूली" कहा था :) चूंकि ओपी वास्तव में निष्पादन समय में डेल्टा का उल्लेख नहीं करता है, "नक्शा परीक्षण 2 विफल रहता है, सेट परीक्षण 3 विफल रहता है", यह कहना मुश्किल है। दी गई जानकारी के साथ, एक ही एल्गोरिदम का उपयोग करने के लिए जीसीसी कार्यान्वयन पर विश्वास करेगा। जैसा कि मैं अपने जवाब में (निस्संदेह) कहता हूं, माइक्रोस्कोफ्ट विभिन्न कार्यान्वयन का उपयोग कर सकता है। – OlivierD

संबंधित मुद्दे