पूर्णांकों की एक धारा से चल मंझला का पता लगाएं


223

संभव डुप्लिकेट:
सी में रोलिंग मंझला एल्गोरिदम

यह देखते हुए कि पूर्णांक एक डेटा स्ट्रीम से पढ़े जाते हैं। कुशल तरीके से अब तक पढ़े गए तत्वों का माध्य खोजें।

समाधान मैंने पढ़ा है: हम उन तत्वों का प्रतिनिधित्व करने के लिए बाईं ओर एक अधिकतम ढेर का उपयोग कर सकते हैं जो प्रभावी मंझले से कम हैं, और प्रभावी मंझले से अधिक होने वाले तत्वों का प्रतिनिधित्व करने के लिए दाईं ओर एक न्यूनतम ढेर है।

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

लेकिन हम एक अधिकतम हीप और मिन हीप का निर्माण कैसे करेंगे अर्थात हम यहाँ प्रभावी माध्यिका को कैसे जान पाएंगे? मुझे लगता है कि हम 1 तत्व को अधिकतम-ढेर में और फिर अगले 1 तत्व को min-heap में, और इसी तरह सभी तत्वों के लिए सम्मिलित करेंगे। मुझे सही करें अगर मैं यहां गलत हूं।


10
चालाक एल्गोरिथ्म, ढेर का उपयोग कर। शीर्षक से मैं तुरंत समाधान के बारे में नहीं सोच सकता था।
मूइंग डक

1
vizier का समाधान मुझे अच्छा लग रहा है, सिवाय इसके कि मैं मान रहा था (हालाँकि आप राज्य नहीं थे) कि यह धारा मनमाने ढंग से लंबी हो सकती है, इसलिए आप सब कुछ स्मृति में नहीं रख सकते। क्या यह मामला है?
वाइल्ड

2
@RunningWild मनमाने ढंग से लंबी धाराओं के लिए, आप पिछले N तत्वों के माध्यिका को फाइबोनैचि हीप्स का उपयोग करके प्राप्त कर सकते हैं (ताकि आपको लॉग (N) डिलीट हो जाए) और सम्मिलित किए गए तत्वों को क्रम में (जैसे कि एक deque) में संचयित करें, फिर सबसे पुराना हटा दें प्रत्येक चरण में तत्व एक बार ढेर भर जाते हैं (हो सकता है कि चीजें एक ढेर से दूसरे तक जा रही हों)। आप दोहराया तत्वों की संख्या को संग्रहीत करके एन से कुछ हद तक बेहतर हो सकते हैं (यदि बहुत सारे दोहराव हैं), लेकिन सामान्य तौर पर, मुझे लगता है कि यदि आप संपूर्ण स्ट्रीम का माध्य चाहते हैं तो आपको कुछ प्रकार की वितरण संबंधी धारणाएं बनानी होंगी।
डगल

2
आप दोनों ढेर खाली के साथ शुरू कर सकते हैं। पहला इंट एक हीप में जाता है; दूसरा या तो दूसरे में जाता है, या आप पहले आइटम को दूसरे ढेर में ले जाते हैं और फिर सम्मिलित करते हैं। यह "सामान्य रूप से एक हीप को दूसरे +1 से बड़ा नहीं होने देता है" और इसके लिए किसी विशेष आवरण की आवश्यकता नहीं होती है (खाली ढेर के "मूल मान" को 0 के रूप में परिभाषित किया जा सकता है)
जॉन वाट ने

मैं बस एक MSFT साक्षात्कार पर यह सवाल है। पोस्ट करने के लिए धन्यवाद
R Claven

जवाबों:


383

स्ट्रीम किए गए डेटा से मंझला खोजने के लिए कई अलग-अलग समाधान हैं, मैं उत्तर के बहुत अंत में उनके बारे में संक्षेप में बात करूंगा।

प्रश्न एक विशिष्ट समाधान (अधिकतम हीप / मिनट हीप समाधान) के विवरण के बारे में है, और हीप आधारित समाधान कैसे काम करता है:

पहले दो तत्वों के लिए बाईं ओर के मैक्सहैप में एक को छोटा करें, और बड़े को दाईं ओर के मिनिएप में बड़ा करें। फिर स्ट्रीम डेटा को एक-एक करके प्रोसेस करें,

Step 1: Add next item to one of the heaps

   if next item is smaller than maxHeap root add it to maxHeap,
   else add it to minHeap

Step 2: Balance the heaps (after this step heaps will be either balanced or
   one of them will contain 1 more item)

   if number of elements in one of the heaps is greater than the other by
   more than 1, remove the root element from the one containing more elements and
   add to the other one

फिर किसी भी समय आप इस तरह से माध्य की गणना कर सकते हैं:

   If the heaps contain equal amount of elements;
     median = (root of maxHeap + root of minHeap)/2
   Else
     median = root of the heap with more elements

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


6
ये ढेर बिना बंधे बढ़ते हैं (यानी 10 मिलियन तत्वों पर फिसलने वाली 100 एलिमेंट विंडो के लिए 10 मिलियन एलिमेंट्स को मेमोरी में स्टोर करना होगा)। इंडेक्सेबल स्किप्लिस्ट्स का उपयोग करके एक और उत्तर के लिए नीचे देखें कि केवल हाल ही में देखे गए 100 तत्वों को स्मृति में रखने की आवश्यकता है।
रेमंड हेटिंगर

1
आप हीप्स का उपयोग करके एक बाउंड मेमोरी सॉल्यूशन रख सकते हैं, जैसा कि प्रश्नों में से एक में ही बताया गया है।
हकन सेरेस


1
वाह इससे मुझे न केवल इस विशिष्ट समस्या को हल करने में मदद मिली, बल्कि मुझे यह जानने में भी मदद मिली कि यहाँ अजगर में मेरा मूल कार्यान्वयन है: github.com/PythonAlgo/DataStruct
swati saoji

2
@HakanSerce क्या आप बता सकते हैं कि हमने जो किया वह क्यों किया? मेरा मतलब है कि मैं इस काम को देख सकता हूं, लेकिन मैं इसे सहज रूप से समझने में सक्षम नहीं हूं।
शिव

51

यदि आप एक बार में सभी आइटम मेमोरी में नहीं रख सकते हैं, तो यह समस्या बहुत कठिन हो जाती है। ढेर समाधान आपको एक बार में सभी तत्वों को स्मृति में रखने की आवश्यकता है। इस समस्या के अधिकांश वास्तविक विश्व अनुप्रयोगों में यह संभव नहीं है।

इसके बजाय, के रूप में आप संख्या को देखते हैं, का ट्रैक रखने गिनती बार आप प्रत्येक पूर्णांक को देखने की संख्या का। 4 बाइट पूर्णांकों को मानते हुए, यह 2 ^ 32 बकेट, या अधिकतम 2 ^ 33 पूर्णांक (प्रत्येक इंट के लिए कुंजी और गणना) है, जो 2 ^ 35 बाइट्स या 32 जीबी है। यह संभवतः इसकी तुलना में बहुत कम होगा क्योंकि आपको उन प्रविष्टियों को संग्रहीत करने या गिनने की आवश्यकता नहीं है जो 0 हैं (जैसे कि अजगर में एक डिफ़ॉल्ट की तरह)। प्रत्येक नए पूर्णांक को सम्मिलित करने के लिए निरंतर समय लगता है।

फिर किसी भी बिंदु पर, माध्यिका को खोजने के लिए, केवल यह निर्धारित करने के लिए कि कौन सा पूर्णांक मध्य तत्व है, का उपयोग करें। यह निरंतर समय लेता है (यद्यपि एक बड़ा स्थिर, लेकिन फिर भी निरंतर)।


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

1
एक विरल सूची के लिए, मैं सहमत हूं, यह स्मृति के मामले में बदतर है। हालाँकि यदि पूर्णांक को बेतरतीब ढंग से वितरित किया जाता है, तो आप अंतर्ज्ञान से बहुत जल्द डुप्लिकेट प्राप्त करना शुरू कर देंगे। Mathworld.wolfram.com/BirthdayProblem.html देखें । इसलिए मुझे पूरा यकीन है कि जैसे ही आपके पास कुछ जीबी डेटा होगा, यह प्रभावी हो जाएगा।
एंड्रयू सी

4
@AndrewC क्या आप pls को समझा सकते हैं कि माध्यिका को खोजने में लगातार समय कैसे लगेगा। अगर मैंने एन के विभिन्न प्रकार के पूर्णांक देखे हैं तो सबसे खराब स्थिति में अंतिम तत्व हो सकता है। यह ओ (एन) गतिविधि खोजने के लिए मध्य बनाता है।
shshnk

@shshnn इस मामले में कुल तत्वों की संख्या n नहीं है >>> 2 ^ 35?
विशमदिनी

@ शशांक आप सही कह रहे हैं कि यह अभी भी अलग-अलग पूर्णांकों की संख्या में रैखिक है, जैसा कि आप देख रहे हैं, जैसा कि विश्मदी ने कहा, इस समाधान के लिए मैं जो धारणा बना रहा हूं वह यह है कि n आपके द्वारा देखी गई संख्याओं की संख्या है, जो बहुत अधिक है 2 ^ 33 से बड़ा। यदि आप यह नहीं देख रहे हैं कि कई नंबर, तो निश्चित रूप से बेहतर समाधान है।
एंड्रयू सी

49

यदि इनपुट के विचरण को सांख्यिकीय रूप से वितरित किया जाता है (उदाहरण के लिए सामान्य, लॉग-सामान्य, आदि) तो जलाशय का नमूना संख्याओं की मनमानी लंबी धारा से प्रतिशत / मंझले का आकलन करने का एक उचित तरीका है।

int n = 0;  // Running count of elements observed so far  
#define SIZE 10000
int reservoir[SIZE];  

while(streamHasData())
{
  int x = readNumberFromStream();

  if (n < SIZE)
  {
       reservoir[n++] = x;
  }         
  else 
  {
      int p = random(++n); // Choose a random number 0 >= p < n
      if (p < SIZE)
      {
           reservoir[p] = x;
      }
  }
}

"जलाशय" तब एक चल रहा है, वर्दी (निष्पक्ष), सभी इनपुट का नमूना - आकार की परवाह किए बिना। माध्यिका (या किसी भी प्रतिशत) को खोजना जलाशय को छांटने और दिलचस्प बिंदु को मतदान करने का सीधा-सीधा मामला है।

चूंकि जलाशय निश्चित आकार है, इसलिए सॉर्ट को प्रभावी रूप से ओ (1) माना जा सकता है - और यह विधि निरंतर समय और मेमोरी खपत दोनों के साथ चलती है।


जिज्ञासा से बाहर, आपको विचरण की आवश्यकता क्यों है?
LazyCat

स्ट्रीम SIZE तत्वों से कम वापस आ सकता है जिससे जलाशय आधा खाली हो जाता है। यह विचार किया जाना चाहिए जब कंप्यूटिंग मीडियन।
एलेक्स

क्या माध्यिका के बजाय अंतर की गणना करके इसे तेजी से बनाने का एक तरीका है? क्या हटाए गए और जोड़े गए नमूने और पिछले माध्यिका के लिए पर्याप्त जानकारी है?
inf3rno

30

सबसे प्रभावी तरीका है कि मैंने पाया है कि स्ट्रीम के प्रतिशतक की गणना करने के लिए P: एल्गोरिथ्म है: राज जैन, इमरिक क्लैमटैक: क्वांटिल्स और हिस्टोग्राम के गतिशील गणना के लिए Pac एल्गोरिथम बिना भंडारण टिप्पणियों के। Commun। एसीएम 28 (10): 1076-1085 (1985)

एल्गोरिथ्म लागू करने के लिए सीधे आगे है और बहुत अच्छी तरह से काम करता है। हालांकि, यह एक अनुमान है, इसलिए इसे ध्यान में रखें। अमूर्त से:

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


2
काउंट-मिन स्केच P ^ 2 से बेहतर है कि यह त्रुटि को बाध्य करता है जबकि उत्तरार्द्ध नहीं करता है।
चीनिनिटी

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

1
इसके अलावा, एक संभाव्य दृष्टिकोण के लिए, इस ब्लॉग पोस्ट को देखें: research.neustar.biz/2013/09/16/… और जिस पेपर को संदर्भित करता है वह यहां है: arxiv.org/pdf/1407.1121v1.pdf इसे "फ्रुगल" कहा जाता है। स्ट्रीमिंग "
पॉल चेरोच

27

हम की औसत जानना चाहते हैं तो एन सबसे हाल ही में देखा तत्वों, इस समस्या को एक सटीक समाधान है कि केवल जरूरत है n सबसे हाल ही में देखा तत्वों स्मृति में रखा जाना चाहिए। यह तेज है और अच्छी तरह से तराजू है।

एक इंडेक्सेबल skiplist का समर्थन करता है ओ (ln एन) प्रविष्टि, हटाने, और मनमाने ढंग से तत्वों के अनुक्रमित खोज जबकि क्रमबद्ध व्यवस्था बनाए रखने। जब एक FIFO कतार के साथ युग्मित किया जाता है जो n-वें सबसे पुराने प्रविष्टि को ट्रैक करता है, तो समाधान सरल है:

class RunningMedian:
    'Fast running median with O(lg n) updates where n is the window size'

    def __init__(self, n, iterable):
        self.it = iter(iterable)
        self.queue = deque(islice(self.it, n))
        self.skiplist = IndexableSkiplist(n)
        for elem in self.queue:
            self.skiplist.insert(elem)

    def __iter__(self):
        queue = self.queue
        skiplist = self.skiplist
        midpoint = len(queue) // 2
        yield skiplist[midpoint]
        for newelem in self.it:
            oldelem = queue.popleft()
            skiplist.remove(oldelem)
            queue.append(newelem)
            skiplist.insert(newelem)
            yield skiplist[midpoint]

यहां काम करने वाले कोड को पूरा करने के लिए लिंक हैं (एक आसान-से-समझने वाला क्लास संस्करण और इंडेक्सेबल स्किलिस्ट कोड के साथ एक अनुकूलित जनरेटर संस्करण इनबिल्ट):


7
अगर मैं इसे सही ढंग से समझ रहा हूं, लेकिन यह आपको केवल अंतिम एन तत्वों का एक मध्यमान देता है, न कि उस बिंदु तक सभी तत्व। यह उस ऑपरेशन के लिए वास्तव में एक चालाक समाधान की तरह प्रतीत होता है।
एंड्रयू सी

16
सही। उत्तर ऐसा लगता है जैसे स्मृति में केवल अंतिम n तत्वों को रखकर सभी तत्वों के मध्य का पता लगाना संभव था - यह सामान्य रूप से असंभव है। एल्गोरिथ्म सिर्फ अंतिम एन तत्वों के माध्यिका को ढूंढता है।
हंस-पीटर स्टॉर

8
"रनिंग माध्यिका" शब्द का उपयोग आम तौर पर डेटा के सबसेट के माध्य को संदर्भित करने के लिए किया जाता है । ओपी का उपयोग गैर-मानक तरीके से एक सामान्य शब्द किया जाता है।
राहेल हेटिंगर

18

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

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

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


आप उसे कैसे करने जा रहे हैं? "हम सही पेड़ के न्यूनतम तत्व को हटा देते हैं"
हेंगामेह

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

7

कुशल एक शब्द है जो संदर्भ पर निर्भर करता है। इस समस्या का समाधान सम्मिलन की मात्रा के सापेक्ष किए गए प्रश्नों की मात्रा पर निर्भर करता है। मान लीजिए कि आप मंझले में रुचि रखते थे और अंत में N नंबर और K बार सम्मिलित कर रहे हैं। हीप आधारित एल्गोरिथ्म की जटिलता ओ (एन लॉग एन + के) होगी।

निम्नलिखित विकल्प पर विचार करें। एक सरणी में संख्याओं को डुबोएं, और प्रत्येक प्रश्न के लिए, रैखिक चयन एल्गोरिथ्म (क्विकॉर्ट पिवट का उपयोग करके, कहें) चलाएं। अब आपके पास रनिंग टाइम O (KN) वाला एल्गोरिदम है।

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


1
ढेर उदाहरण में, खोज निरंतर समय है, इसलिए मुझे लगता है कि यह ओ (एन लॉग एन + के) होना चाहिए, लेकिन आपकी बात अभी भी रखती है।
एंड्रयू सी

हाँ, अच्छा बिंदु, इसे संपादित करेगा। आप सही हैं एन लॉग एन अभी भी अग्रणी शब्द है।
पीटरिस

-2

तुम सिर्फ एक हीप के साथ ऐसा नहीं कर सकते? अद्यतन: नहीं। टिप्पणी देखें।

अपरिवर्तनीय: 2*nइनपुट पढ़ने के बाद , न्यूनतम-ढेर nउनमें सबसे बड़ा है।

लूप: 2 इनपुट पढ़ें। उन दोनों को ढेर में जोड़ें, और ढेर के मिनट को हटा दें। यह आक्रमणकारी को पुन: स्थापित करता है।

इसलिए जब 2nइनपुट पढ़े गए हैं, तो ढेर का न्यूनतम nth सबसे बड़ा है। औसत दर्जे के इनपुट के बाद दोनों तत्वों को औसत करने और प्रश्नों को संभालने के लिए थोड़ी अतिरिक्त जटिलता की आवश्यकता होगी।


1
काम नहीं करता है: आप उन चीजों को छोड़ सकते हैं जो बाद में शीर्ष के पास निकलती हैं। उदाहरण के लिए, 1 से 100 की संख्या के साथ अपने एल्गोरिथ्म का प्रयास करें, लेकिन रिवर्स ऑर्डर में: 100, 99, ..., 1.
zellyn

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