एक बड़े शब्द अनुक्रम में शीर्ष कश्मीर आवृत्ति शब्द खोजने के लिए सबसे कुशल तरीका है


85

इनपुट: एक सकारात्मक पूर्णांक K और एक बड़ा पाठ। पाठ को वास्तव में शब्द अनुक्रम के रूप में देखा जा सकता है। इसलिए हमें इस बारे में चिंता करने की ज़रूरत नहीं है कि इसे शब्द क्रम में कैसे तोड़ा जाए।
आउटपुट: पाठ में सबसे अधिक बार K शब्द।

मेरी सोच इस तरह की है।

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

  2. सॉर्ट (शब्द, शब्द-आवृत्ति) जोड़ी; और कुंजी "शब्द-आवृत्ति" है। यह सामान्य छँटाई एल्गोरिथ्म के साथ O (n * lg (n)) समय लेता है।

  3. सॉर्ट करने के बाद, हम केवल पहले K शब्द लेते हैं। इसमें O (K) समय लगता है।

संक्षेप में, कुल समय हे (n + n lg (n) + K) the चूँकि K निश्चित रूप से N से छोटा है, इसलिए यह वास्तव में O (n lg (n)) है।

हम इसमें सुधार कर सकते हैं। दरअसल, हम सिर्फ शीर्ष K शब्द चाहते हैं। दूसरे शब्दों की आवृत्ति हमारे लिए चिंता का विषय नहीं है। तो, हम "आंशिक हीप छँटाई" का उपयोग कर सकते हैं। चरण 2) और 3) के लिए, हम सिर्फ छँटाई नहीं करते हैं। इसके बजाय, हम इसे होने के लिए बदल देते हैं

2 ') कुंजी के रूप में "शब्द-आवृत्ति" के साथ (शब्द, शब्द-आवृत्ति) जोड़ी का एक ढेर बनाएँ। ढेर बनाने में O (n) समय लगता है;

3 ') ढेर से शीर्ष कश्मीर शब्द निकालें। प्रत्येक निष्कर्षण ओ (lg (n)) है। तो, कुल समय हे (k * lg (n)) है।

संक्षेप में, यह समाधान लागत समय O (n + k * lg (n)) है।

यह सिर्फ मेरा विचार है। मुझे चरण 1 में सुधार करने का तरीका नहीं मिला है)।
मुझे आशा है कि कुछ सूचना पुनर्प्राप्ति विशेषज्ञ इस प्रश्न पर अधिक प्रकाश डाल सकते हैं।


क्या आप O (n * logn) सॉर्ट के लिए मर्ज सॉर्ट या क्विकसॉर्ट का उपयोग करेंगे?
committedandroider

1
व्यावहारिक उपयोगों के लिए, एक नमूने पर हारून मेनपा का जवाब सबसे अच्छा है। यह ऐसा नहीं है कि आपके नमूने से सबसे लगातार शब्द छिपेंगे। आप जटिलता geeks के लिए, यह ओ (1) के बाद से नमूना का आकार तय हो गया है। आप सटीक मायने नहीं रखते हैं, लेकिन आप उनके लिए भी नहीं पूछ रहे हैं।
निकाना रेक्लवैक्स

यदि आप चाहते हैं कि आपके जटिलता विश्लेषण की समीक्षा हो, तो मैं बेहतर उल्लेख करूंगा: यदि n आपके पाठ में शब्दों की संख्या है और m विभिन्न शब्दों की संख्या है (प्रकार, हम उन्हें कहते हैं), चरण 1 O है ( n ), लेकिन चरण 2 O ( m .lg ( m )) है, और m << n (आपके पास अरबों शब्द हो सकते हैं और एक लाख प्रकार तक नहीं पहुंच सकते, इसे आज़माएं)। तो डमी एल्गोरिथ्म के साथ भी, यह अभी भी O ( n + m lg ( m )) = O ( n ) है।
निकाना रेक्लाविक्स

1
Pls इस सवाल पर एक धारणा जोड़ें कि हमारे पास बड़े पाठ के सभी शब्दों को रखने के लिए पर्याप्त मुख्य मेमोरी है। यह देखना दिलचस्प होगा कि 10GB फ़ाइल से k = 100 शब्द खोजने के लिए दृष्टिकोण (यानी सभी शब्द 4 जीबी रैम में फिट नहीं होंगे) !!
केज़हतक

@Khatak अगर यह RAM आकार से अधिक हो तो हम इसे कैसे करेंगे?
user7098526

जवाबों:


66

यह O (n) समय में किया जा सकता है

समाधान 1:

कदम:

  1. शब्दों की गणना करें और इसे हैश करें, जो इस तरह से संरचना में समाप्त हो जाएगा

    var hash = {
      "I" : 13,
      "like" : 3,
      "meow" : 3,
      "geek" : 3,
      "burger" : 2,
      "cat" : 1,
      "foo" : 100,
      ...
      ...
    
  2. हैश के माध्यम से आगे बढ़ें और सबसे अधिक इस्तेमाल किया जाने वाला शब्द ढूंढें (इस मामले में "फू" 100), फिर उस आकार का सरणी बनाएं

  3. फिर हम हैश को फिर से आगे बढ़ा सकते हैं और शब्दों की संख्या का उपयोग सरणी इंडेक्स के रूप में कर सकते हैं, अगर इंडेक्स में कुछ भी नहीं है, तो एक सरणी बनाएं और इसे सरणी में जोड़ दें। फिर हम एक सरणी जैसे समाप्त करते हैं:

      0   1      2            3                  100
    [[ ],[cat],[burger],[like, meow, geek],[]...[foo]]
    
  4. फिर बस अंत से सरणी को पार करें, और कश्मीर शब्दों को इकट्ठा करें।

समाधान 2:

कदम:

  1. ऊपर की तरह
  2. मिनट के ढेर का उपयोग करें और मिनट के आकार को k पर रखें, और हैश में प्रत्येक शब्द के लिए हम मिनट के साथ शब्दों की घटनाओं की तुलना करते हैं, 1) यदि यह न्यूनतम मान से अधिक है, तो मिनट को हटा दें (यदि मिनट का आकार हीप k के बराबर है) और मिन हीप में नंबर डालें। 2) आराम की सामान्य स्थिति।
  3. सरणी के माध्यम से ट्रैवर्स करने के बाद, हम सिर्फ मिन हीप को एरे में बदलते हैं और एरे को वापस करते हैं।

16
आपका समाधान (1) एक O (n) बाल्टी सॉर्ट है जो एक मानक O (n lg n) तुलना सॉर्ट की जगह ले रहा है। आपके दृष्टिकोण को बाल्टी संरचना के लिए अतिरिक्त स्थान की आवश्यकता होती है, लेकिन तुलनात्मक रूप से जगह में किया जा सकता है। आपका समाधान (2) समय में चलता है O (n lg k) - अर्थात, O (n) सभी शब्दों पर पुनरावृति करने के लिए और O (lg k) प्रत्येक को ढेर में जोड़ने के लिए।
stackoverflowuser2010

4
पहले समाधान के लिए अधिक स्थान की आवश्यकता होती है, लेकिन यह जोर देना महत्वपूर्ण है कि यह समय में वास्तव में ओ (एन) है। 1: शब्द, ओ (n) द्वारा बंदी आवृत्तियों; 2: ट्रैवर्स फ़्रीक्वेंसी हैश, फ़्रीक्वेंसी के द्वारा दूसरा हैश बनाया जाए। यह उस आवृत्ति पर शब्दों की सूची में एक शब्द जोड़ने के लिए हैश और हे (1) को पार करने के लिए ओ (एन) है। 3: जब तक आप k को हिट नहीं करते तब तक अधिकतम आवृत्ति से नीचे हैश। अधिकतम पर, O (n)। कुल = 3 * ओ (एन) = ओ (एन)।
5M14CackBakeBack

3
आमतौर पर शब्दों की गिनती करते समय, समाधान 1 में आपकी बाल्टी की संख्या व्यापक रूप से अधिक होती है (क्योंकि नंबर एक सबसे लगातार शब्द दूसरे और तीसरे सर्वश्रेष्ठ की तुलना में बहुत अधिक है), इसलिए आपकी सरणी विरल और अक्षम है।
निकाना रेक्लाविक्स

आपका समाधान # 1 तब काम नहीं करता है जब k (लगातार शब्दों की संख्या) सबसे अधिक बार होने वाले शब्द की संख्या से कम है (यानी, इस मामले में 100), बेशक, व्यवहार में ऐसा नहीं हो सकता है, लेकिन एक। नहीं मान रहा!
एक दो तीन

@OneTwoThree प्रस्तावित समाधान केवल एक उदाहरण है। संख्या मांग के आधार पर होगी।
चिहुंग यू

22

आपके द्वारा वर्णित समाधान की तुलना में आप आमतौर पर बेहतर रनटाइम प्राप्त नहीं करेंगे। आपको सभी शब्दों का मूल्यांकन करने के लिए कम से कम O (n) कार्य करना होगा, और फिर O (k) अतिरिक्त k शब्द खोजने के लिए अतिरिक्त कार्य करना होगा।

यदि आपका समस्या सेट वास्तव में बड़ा है, तो आप वितरित समाधान का उपयोग कर सकते हैं जैसे कि नक्शा / कमी। N मानचित्र कार्यकर्ता प्रत्येक पाठ के 1 / nth पर आवृत्तियों की गणना करते हैं, और प्रत्येक शब्द के लिए, इसे शब्द के हैश के आधार पर गणना किए गए मीटर reducer श्रमिकों में से एक को भेजते हैं। Reducers तब मायने रखता है। Reducers के आउटपुट पर मर्ज सॉर्ट आपको लोकप्रियता के क्रम में सबसे लोकप्रिय शब्द देगा।


13

यदि हम शीर्ष K, और O (n + k * lg (k)) समाधान की रैंकिंग नहीं करते हैं, तो आपके समाधान पर एक छोटी सी विविधता एक O (n) एल्गोरिथ्म उत्पन्न करती है। मेरा मानना ​​है कि ये दोनों सीमाएं एक स्थिर कारक के भीतर इष्टतम हैं।

सूची के माध्यम से चलने के बाद यहाँ अनुकूलन फिर से आता है, हैश तालिका में प्रविष्ट करना। हम सूची में Kth सबसे बड़े तत्व का चयन करने के लिए मंझला एल्गोरिदम के माध्यिका का उपयोग कर सकते हैं । यह एल्गोरिथ्म O (n) है।

Kth सबसे छोटे तत्व का चयन करने के बाद, हम उस तत्व के चारों ओर की सूची को क्विकॉर्ट में विभाजित करते हैं। यह स्पष्ट रूप से O (n) भी है। धुरी के "बाएं" किनारे पर कुछ भी हमारे K तत्वों के समूह में है, इसलिए हम काम कर रहे हैं (जैसा कि हम साथ चलते हैं, हम बस बाकी सब फेंक सकते हैं)।

तो यह रणनीति है:

  1. प्रत्येक शब्द के माध्यम से जाओ और एक हैश तालिका में डालें: O (n)
  2. Kth सबसे छोटा तत्व चुनें: O (n)
  3. उस तत्व के चारों ओर विभाजन: O (n)

यदि आप K तत्वों को रैंक करना चाहते हैं, तो उन्हें O (k * lg (k)) समय में किसी भी कुशल तुलना प्रकार के साथ सॉर्ट करें, कुल रन समय O (n + k * lg (k))।

O (n) टाइम बाउंड एक स्थिर फैक्टर के भीतर इष्टतम है क्योंकि हमें प्रत्येक शब्द को कम से कम एक बार जांचना चाहिए।

O (n + k * lg (k)) टाइम बाउंड भी इष्टतम है क्योंकि k * lg (k) समय से कम के तत्वों को सॉर्ट करने के लिए कोई तुलना-आधारित तरीका नहीं है।


जब हम Kth सबसे छोटे तत्व का चयन करते हैं, जो चुना जाता है वह Kth सबसे छोटा हैश-की है। यह आवश्यक नहीं है कि चरण 3 के बाएं भाग में बिल्कुल K शब्द हों
प्रकाश मुरली

2
आप हैश टेबल पर "मेडियंस ऑफ मेडियंस" नहीं चला पाएंगे क्योंकि यह स्वैप करता है। आपको हैश तालिका से अस्थायी सारणी में डेटा को कॉपी करना होगा। तो, O (n) स्टोरेज reqd होगा।
user674669

मुझे समझ नहीं आ रहा है कि आप O (n) में Kth सबसे छोटे तत्व का चयन कैसे कर सकते हैं?
माइकल हो चुम

O (n) में Kth सबसे छोटे तत्व को खोजने के लिए एल्गोरिथ्म के लिए इसे देखें - wikiwand.com/en/Median_of_medians
पीयूष

यदि आप हैश तालिका + मिनट हीप का उपयोग करते हैं तो भी जटिलता समान है। मुझे कोई अनुकूलन नहीं दिखता।
विनय

8

यदि आपकी "बड़ी शब्द सूची" काफी बड़ी है, तो आप बस नमूना ले सकते हैं और अनुमान प्राप्त कर सकते हैं। अन्यथा, मुझे हैश एकत्रीकरण पसंद है।

संपादित करें :

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

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


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

1
एक व्यावहारिक उत्तर के लिए +1 जो अप्रासंगिक जटिलता के मुद्दों को स्वीकार नहीं करता है। @itsadok: प्रत्येक रन के लिए: यदि यह काफी बड़ा है, तो इसका नमूना लें; यदि यह नहीं है, तो एक लॉग फैक्टर प्राप्त करना अप्रासंगिक है।
निकाना रेक्लवैक्स

2

आप शब्दों के पहले अक्षर का उपयोग करके विभाजन को आगे काट सकते हैं, फिर अगले चरित्र का उपयोग करते हुए सबसे बड़े बहु-शब्द सेट को तब तक विभाजित कर सकते हैं जब तक कि आपके पास एकल-शब्द सेट न हों। आप लीफ़्स पर आंशिक / पूर्ण शब्दों की सूचियों के साथ एक सॉर्टो 256-वे ट्री का उपयोग करेंगे। आपको हर जगह स्ट्रिंग प्रतियां न करने के लिए बहुत सावधान रहने की आवश्यकता होगी।

यह एल्गोरिथ्म O (m) है, जहाँ m वर्णों की संख्या है। यह k पर निर्भरता से बचता है, जो कि बड़े कश्मीर के लिए बहुत अच्छा है [वैसे आपके पोस्ट किए गए रनिंग टाइम गलत है, यह O (n * lg (k)) होना चाहिए, और मुझे यकीन नहीं है कि इसके संदर्भ में क्या है म]।

यदि आप दोनों एल्गोरिदम को साथ-साथ चलाते हैं, तो आपको मिलेगा कि मुझे पूरा यकीन है कि एक विषम रूप से इष्टतम O (min (m, n * lg (k))) एल्गोरिथ्म है, लेकिन मेरा औसतन तेज़ होना चाहिए क्योंकि इसमें शामिल नहीं है हैशिंग या सॉर्टिंग।


7
आप जो वर्णन कर रहे हैं उसे 'ट्राई' कहते हैं।
निक जॉनसन

हाय स्ट्रिल्लैंक। क्या आप विवरण में विभाजन की प्रक्रिया समझा सकते हैं?
मॉर्गन चेंग

1
यह कैसे छँटाई शामिल नहीं है ?? एक बार जब आप ट्राइ करते हैं, तो आप सबसे बड़े आवृत्तियों के साथ k शब्दों को कैसे निकालते हैं। कोई मतलब नहीं है
साधारण

2

आपके विवरण में एक बग है: काउंटिंग में O (n) समय लगता है, लेकिन सॉर्टिंग में O (m * lg (m)) लगता है, जहाँ m अद्वितीय शब्दों की संख्या है । यह आमतौर पर शब्दों की कुल संख्या की तुलना में बहुत छोटा है, इसलिए संभवत: केवल यह अनुकूलित करना चाहिए कि हैश कैसे बनाया जाता है।



2

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

बस नमूना , कहते हैं, आपके पाठ से कुछ मिलियन शब्द, प्रक्रिया है कि सेकंड के एक मामले में किसी भी एल्गोरिथ्म के साथ , और सबसे लगातार गणना बहुत सटीक होगी।

एक साइड नोट के रूप में, डमी एल्गोरिथ्म की जटिलता (1. सभी को गिनें। 2. गणनाओं को छाँटें। सबसे अच्छा लें) O (n + m * log (m)) है, जहाँ m आपके विभिन्न शब्दों की संख्या है पाठ। log (m), (n / m) की तुलना में बहुत छोटा है, इसलिए यह O (n) बना हुआ है।

व्यावहारिक रूप से, लंबा कदम गिनती है।


2
  1. शब्दों को संग्रहीत करने के लिए मेमोरी कुशल डेटा संरचना का उपयोग करें
  2. शीर्ष K लगातार शब्द खोजने के लिए, MaxHeap का उपयोग करें।

यहाँ कोड है

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;

import com.nadeem.app.dsa.adt.Trie;
import com.nadeem.app.dsa.adt.Trie.TrieEntry;
import com.nadeem.app.dsa.adt.impl.TrieImpl;

public class TopKFrequentItems {

private int maxSize;

private Trie trie = new TrieImpl();
private PriorityQueue<TrieEntry> maxHeap;

public TopKFrequentItems(int k) {
    this.maxSize = k;
    this.maxHeap = new PriorityQueue<TrieEntry>(k, maxHeapComparator());
}

private Comparator<TrieEntry> maxHeapComparator() {
    return new Comparator<TrieEntry>() {
        @Override
        public int compare(TrieEntry o1, TrieEntry o2) {
            return o1.frequency - o2.frequency;
        }           
    };
}

public void add(String word) {
    this.trie.insert(word);
}

public List<TopK> getItems() {

    for (TrieEntry trieEntry : this.trie.getAll()) {
        if (this.maxHeap.size() < this.maxSize) {
            this.maxHeap.add(trieEntry);
        } else if (this.maxHeap.peek().frequency < trieEntry.frequency) {
            this.maxHeap.remove();
            this.maxHeap.add(trieEntry);
        }
    }
    List<TopK> result = new ArrayList<TopK>();
    for (TrieEntry entry : this.maxHeap) {
        result.add(new TopK(entry));
    }       
    return result;
}

public static class TopK {
    public String item;
    public int frequency;

    public TopK(String item, int frequency) {
        this.item = item;
        this.frequency = frequency;
    }
    public TopK(TrieEntry entry) {
        this(entry.word, entry.frequency);
    }
    @Override
    public String toString() {
        return String.format("TopK [item=%s, frequency=%s]", item, frequency);
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + frequency;
        result = prime * result + ((item == null) ? 0 : item.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        TopK other = (TopK) obj;
        if (frequency != other.frequency)
            return false;
        if (item == null) {
            if (other.item != null)
                return false;
        } else if (!item.equals(other.item))
            return false;
        return true;
    }

}   

}

यहाँ इकाई परीक्षण है

@Test
public void test() {
    TopKFrequentItems stream = new TopKFrequentItems(2);

    stream.add("hell");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("hero");
    stream.add("hero");
    stream.add("hero");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("home");
    stream.add("go");
    stream.add("go");
    assertThat(stream.getItems()).hasSize(2).contains(new TopK("hero", 3), new TopK("hello", 8));
}

अधिक जानकारी के लिए इस परीक्षण मामले को देखें


1
  1. पूरे शब्द अनुक्रम को पार करते हुए सभी शब्दों की आवृत्ति रिकॉर्ड करने के लिए एक हैश टेबल का उपयोग करें। इस चरण में, कुंजी "शब्द" है और मूल्य "शब्द-आवृत्ति" है। इसमें O (n) समय लगता है। यह वही है जो ऊपर बताया गया है

  2. हैशमैप में खुद को सम्मिलित करते समय, शीर्ष 10 लगातार शब्दों को रखने के लिए ट्रीसेट (आकार के लिए विशिष्ट, हर भाषा में कार्यान्वयन हैं) 10 (k = 10) रखें। तक का आकार 10 से कम है, इसे जोड़ते रहें। यदि आकार 10 के बराबर है, यदि डाला गया तत्व न्यूनतम तत्व यानी पहले तत्व से अधिक है। यदि हाँ, इसे हटा दें और नया तत्व डालें

ट्रीसेट के आकार को प्रतिबंधित करने के लिए इस लिंक को देखें


0

मान लें कि हमारे पास एक शब्द अनुक्रम "विज्ञापन" "विज्ञापन" "लड़का" "बड़ा" "बुरा" "कॉम" "आना" "ठंडा" है। और के = 2। जैसा कि आपने "शब्दों के पहले अक्षर का उपयोग करते हुए विभाजन" का उल्लेख किया है, हमें मिला ("विज्ञापन", "विज्ञापन") ("लड़का", "बड़ा", "बुरा") ("कॉम" "आना" "ठंडा") तब अगले चरित्र का उपयोग करते हुए सबसे बड़े बहु-शब्द सेट को तब तक विभाजित करें जब तक कि आपके पास एकल-शब्द सेट न हों। " यह विभाजन ("लड़का", "बड़ा", "बुरा") ("कॉम" "आना" "ठंडा") होगा, पहला विभाजन ("विज्ञापन", "विज्ञापन") छूट गया है, जबकि "विज्ञापन" वास्तव में है सबसे लगातार शब्द।

शायद मैं आपकी बात को गलत समझ रहा हूं। क्या आप कृपया विभाजन के बारे में अपनी प्रक्रिया का विवरण दे सकते हैं?


0

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


यह आम तौर पर एक अच्छी दिशा है - लेकिन इसमें एक दोष है। जब गिनती बढ़ जाती है, तो हम "इसके पूर्ववर्ती" की जाँच नहीं करेंगे, लेकिन हमें "पूर्ववर्तियों" की जाँच करनी होगी। उदाहरण के लिए, एक बड़ा मौका है कि सरणी [4,3,1,1,1,1,1,1,1,1,1,1] होगी - 1 की संख्या कई हो सकती है - जो इसे कम कुशल बनाएगी चूँकि हमें सभी पूर्ववर्तियों के माध्यम से पीछे मुड़कर देखना होगा ताकि उचित स्वैप हो सके।
शॉन

यह वास्तव में ओ (एन) से भी बदतर नहीं होगा? O (n ^ 2) की तरह अधिक है क्योंकि यह अनिवार्य रूप से एक अक्षम प्रकार है?
dcarr622

हाय शॉन। हाँ, मैं आपसे सहमत हूँ। लेकिन मुझे संदेह है कि आपके द्वारा बताई गई समस्या समस्या के लिए मूलभूत है। वास्तव में, यदि मानों को केवल एक क्रमबद्ध सरणी रखने के बजाय, हम आगे (वैल्यू, इंडेक्स) जोड़े की एक सरणी रख सकते हैं, जहां सूचकांक दोहराया तत्व की पहली घटना को इंगित करता है, तो समस्या ओ में हल होनी चाहिए। (n) समय। उदाहरण के लिए, [4,3,1,1,1,1,1,1,1,1,1] [(4,0), (3,1), (1,2), (1) की तरह दिखेगा , 2), (1,2, ..., (1,2)]; सूचकांकों की शुरुआत 0. से होती है
Aly Farahat

0

मैं इसके साथ ही संघर्ष कर रहा था और @aly से प्रेरित हूं। बाद में छाँटने के बजाय, हम केवल शब्दों की एक पूर्व निर्धारित सूची ( List<Set<String>>) को बनाए रख सकते हैं और शब्द उस स्थिति में सेट हो जाएगा जब X शब्द के वर्तमान गणना में X है। आम तौर पर, यहां बताया गया है कि यह कैसे काम करता है:

  1. प्रत्येक शब्द के लिए, इसे होने वाली घटना के नक्शे के हिस्से के रूप में संग्रहीत करें Map<String, Integer>:।
  2. फिर, गिनती के आधार पर, इसे पिछले गिनती सेट से हटा दें, और इसे नए गिनती सेट में जोड़ें।

इसका दोष यह है कि सूची शायद बड़ी है - का उपयोग करके अनुकूलित किया जा सकता है TreeMap<Integer, Set<String>>- लेकिन यह कुछ ओवरहेड जोड़ देगा। अंततः हम HashMap या हमारे अपने डेटा संरचना के मिश्रण का उपयोग कर सकते हैं।

कोड

public class WordFrequencyCounter {
    private static final int WORD_SEPARATOR_MAX = 32; // UNICODE 0000-001F: control chars
    Map<String, MutableCounter> counters = new HashMap<String, MutableCounter>();
    List<Set<String>> reverseCounters = new ArrayList<Set<String>>();

    private static class MutableCounter {
        int i = 1;
    }

    public List<String> countMostFrequentWords(String text, int max) {
        int lastPosition = 0;
        int length = text.length();
        for (int i = 0; i < length; i++) {
            char c = text.charAt(i);
            if (c <= WORD_SEPARATOR_MAX) {
                if (i != lastPosition) {
                    String word = text.substring(lastPosition, i);
                    MutableCounter counter = counters.get(word);
                    if (counter == null) {
                        counter = new MutableCounter();
                        counters.put(word, counter);
                    } else {
                        Set<String> strings = reverseCounters.get(counter.i);
                        strings.remove(word);
                        counter.i ++;
                    }
                    addToReverseLookup(counter.i, word);
                }
                lastPosition = i + 1;
            }
        }

        List<String> ret = new ArrayList<String>();
        int count = 0;
        for (int i = reverseCounters.size() - 1; i >= 0; i--) {
            Set<String> strings = reverseCounters.get(i);
            for (String s : strings) {
                ret.add(s);
                System.out.print(s + ":" + i);
                count++;
                if (count == max) break;
            }
            if (count == max) break;
        }
        return ret;
    }

    private void addToReverseLookup(int count, String word) {
        while (count >= reverseCounters.size()) {
            reverseCounters.add(new HashSet<String>());
        }
        Set<String> strings = reverseCounters.get(count);
        strings.add(word);
    }

}

0

मैं अभी इस समस्या का दूसरा हल खोजता हूं। लेकिन मुझे यकीन नहीं है कि यह सही है। समाधान:

  1. सभी शब्दों की आवृत्ति T (n) = O (n) रिकॉर्ड करने के लिए एक हैश टेबल का उपयोग करें
  2. हैश तालिका के पहले k तत्वों को चुनें, और उन्हें एक बफर (जिसका स्थान = k) में पुनर्स्थापित करें। टी (एन) = ओ (के)
  3. हर बार, सबसे पहले हमें बफर के वर्तमान न्यूनतम तत्व को खोजने की आवश्यकता होती है, और बस बफर के न्यूनतम तत्व की तुलना एक (हैसियत से) हैश टेबल के तत्वों को एक-एक करके करना चाहिए। यदि हैश तालिका का तत्व बफर के इस न्यूनतम तत्व से अधिक है, तो वर्तमान बफर के मिनट को छोड़ दें, और हैश तालिका के तत्व को जोड़ दें। इसलिए हर बार जब हम बफर को T (n) = O (k) की आवश्यकता में एक पाते हैं, और पूरे हैश टेबल को T (n) = O (n - k) की आवश्यकता होती है। तो इस प्रक्रिया के लिए पूरे समय की जटिलता T (n) = O ((nk) * k) है।
  4. पूरे हैश तालिका के पार करने के बाद, परिणाम इस बफर में है।
  5. पूरे समय की जटिलता: T (n) = O (n) + O (k) + O (kn - k ^ 2) = O (kn + n - k ^ 2 + k)। चूंकि, k वास्तव में n से सामान्य से छोटा है। तो इस समाधान के लिए, समय जटिलता T (n) = O (kn) है । यह रैखिक समय है, जब k वास्तव में छोटा है। क्या यह सही है? मैं वास्तव में निश्चित नहीं हूं।

0

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

http://en.wikipedia.org/wiki/Trie



0

सबसे अधिक इस्तेमाल होने वाले शब्द की घटना को प्राप्त करने के लिए सबसे सरल कोड।

 function strOccurence(str){
    var arr = str.split(" ");
    var length = arr.length,temp = {},max; 
    while(length--){
    if(temp[arr[length]] == undefined && arr[length].trim().length > 0)
    {
        temp[arr[length]] = 1;
    }
    else if(arr[length].trim().length > 0)
    {
        temp[arr[length]] = temp[arr[length]] + 1;

    }
}
    console.log(temp);
    var max = [];
    for(i in temp)
    {
        max[temp[i]] = i;
    }
    console.log(max[max.length])
   //if you want second highest
   console.log(max[max.length - 2])
}

0

इन स्थितियों में, मैं जावा अंतर्निहित सुविधाओं का उपयोग करने की सलाह देता हूं। चूंकि, वे पहले से ही अच्छी तरह से परीक्षण और स्थिर हैं। इस समस्या में, मुझे HashMap डेटा संरचना का उपयोग करके शब्दों की पुनरावृत्ति लगती है। फिर, मैं परिणामों को ऑब्जेक्ट की एक सरणी पर धकेलता हूं। मैं Arrays.sort () द्वारा ऑब्जेक्ट को सॉर्ट करता हूं और शीर्ष k शब्दों और उनके पुनरावृत्तियों को प्रिंट करता हूं।

import java.io.*;
import java.lang.reflect.Array;
import java.util.*;

public class TopKWordsTextFile {

    static class SortObject implements Comparable<SortObject>{

        private String key;
        private int value;

        public SortObject(String key, int value) {
            super();
            this.key = key;
            this.value = value;
        }

        @Override
        public int compareTo(SortObject o) {
            //descending order
            return o.value - this.value;
        }
    }


    public static void main(String[] args) {
        HashMap<String,Integer> hm = new HashMap<>();
        int k = 1;
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("words.in")));

            String line;
            while ((line = br.readLine()) != null) {
                // process the line.
                //System.out.println(line);
                String[] tokens = line.split(" ");
                for(int i=0; i<tokens.length; i++){
                    if(hm.containsKey(tokens[i])){
                        //If the key already exists
                        Integer prev = hm.get(tokens[i]);
                        hm.put(tokens[i],prev+1);
                    }else{
                        //If the key doesn't exist
                        hm.put(tokens[i],1);
                    }
                }
            }
            //Close the input
            br.close();
            //Print all words with their repetitions. You can use 3 for printing top 3 words.
            k = hm.size();
            // Get a set of the entries
            Set set = hm.entrySet();
            // Get an iterator
            Iterator i = set.iterator();
            int index = 0;
            // Display elements
            SortObject[] objects = new SortObject[hm.size()];
            while(i.hasNext()) {
                Map.Entry e = (Map.Entry)i.next();
                //System.out.print("Key: "+e.getKey() + ": ");
                //System.out.println(" Value: "+e.getValue());
                String tempS = (String) e.getKey();
                int tempI = (int) e.getValue();
                objects[index] = new SortObject(tempS,tempI);
                index++;
            }
            System.out.println();
            //Sort the array
            Arrays.sort(objects);
            //Print top k
            for(int j=0; j<k; j++){
                System.out.println(objects[j].key+":"+objects[j].value);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

अधिक जानकारी के लिए, कृपया https://github.com/m-vahidalizadeh/foundations/blob/master/src/algorithms/TopKatalogTextFile.java पर जाएं । मुझे उम्मीद है यह मदद करेगा।


प्रश्न में स्केच किए गए दृष्टिकोण पर यह किस तरह से सुधार करता है? (कृपया एसई पर प्रस्तुत कोड से टिप्पणी नहीं छोड़ें।) ( I recommend to use Java built-in featuresजैसे कि फूटी लूप और धारा प्रसंस्करण )?
ग्रेबर्ड

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

0
**

C ++ 11 उपरोक्त विचार का कार्यान्वयन

**

class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {

    unordered_map<int,int> map;
    for(int num : nums){
        map[num]++;
    }

    vector<int> res;
    // we use the priority queue, like the max-heap , we will keep (size-k) smallest elements in the queue
    // pair<first, second>: first is frequency,  second is number 
    priority_queue<pair<int,int>> pq; 
    for(auto it = map.begin(); it != map.end(); it++){
        pq.push(make_pair(it->second, it->first));

        // onece the size bigger than size-k, we will pop the value, which is the top k frequent element value 

        if(pq.size() > (int)map.size() - k){
            res.push_back(pq.top().second);
            pq.pop();
        }
    }
    return res;

}

};

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