2013-08-09 10 views
6

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

String[] targetWords; // contains "inventory", "finished", and "goods" 
ArrayList<String> extractedStrings = new ArrayList<String>(); 

for (int i = 0; i < listOfWords.size(); i++) { 
    String[] words = listOfWords.get(i).split(" "); 
    outerloop: 
    for (int j = 0; j < words.length; j++) { 
     for (int k = 0; k < targetWords.length; k++) { 
      if (words[j].equalsIgnoreCase(targetWords[k])) { 
       extractedStrings.add(listOfWords.get(i)); 
       break outerloop; 
      } 
     } 
    } 
} 

सूची 100k से अधिक शब्द हैं, और इस के साथ प्रत्येक लक्ष्य वाक्यांश के लिए कार्य को पूरा करने rounghly .4 .8 के लिए सेकंड लेता है: अभी, मैं विचार इस प्रकार से लागू किया। चीजें हैं, मेरे पास प्रक्रिया के लिए इनमें से बहुत से लक्ष्य वाक्यांश हैं, और सेकंड वास्तव में जोड़ते हैं। इस प्रकार, मैं सोच रहा था कि क्या कोई इस कार्य को पूरा करने के लिए एक अधिक कुशल तरीका के बारे में जानता था? अग्रिम में मदद के लिए धन्यवाद!

+2

यह ओ (एन^3) है। आप आंतरिक लूप के बजाय हैश मैप का उपयोग कर इसे ओ (एन^2) में काट सकते हैं। लेकिन मैं 'जे' पर लूप द्वारा परेशान हूँ। शब्दों की आपकी सूची पहले से ही शब्दों की सूची क्यों नहीं है? आपको प्रत्येक आइटम को फिर से विभाजित क्यों करना है? – EJP

+0

क्षमा करें, मुझे वैरिएबल को बेहतर नामित करना चाहिए - सूचीऑफैड्स में वास्तव में वाक्यांश होते हैं, इसलिए मैंने प्रत्येक वाक्यांश में प्रत्येक शब्द को प्राप्त करने के लिए वाक्यांशों को विभाजित किया। – myrocks2

उत्तर

1

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

मैं सुझाव है कि आप गठबंधन अपने targetWords में एक (संकलित) regular expression:

(?xi) # turn on comments, use case insensitive matching 
\b  # word boundary, i.e. start/end of string, whitespace 
(  # begin of group containing 'inventory' or 'finished' or 'goods' 
inventory|finished|goods # bar separates alternatives 
)  # end of group 
\b  # word boundary 

मत भूलना दोहरे-उद्धरण के लिए अपने नियमित अभिव्यक्ति स्ट्रिंग में backspaces।

import java.util.regex.*; 
... 
Pattern targetPattern = Pattern.compile("(?xi)\\b(inventory|finished|goods)\\b"); 
for (String singleString : listOfWords) { 
    if (targetPattern.matcher(singleString).find()) { 
    extractedStrings.add(singleString); 
    } 
} 

आप नियमित अभिव्यक्ति की गति के साथ संतुष्ट नहीं हैं - हालांकि नियमित अभिव्यक्ति इंजन आमतौर पर प्रदर्शन के लिए अनुकूलित कर रहे हैं - आप अपने खुद के उच्च गति बहु स्ट्रिंग खोज रोल करने की जरूरत है। Aho–Corasick string matching algorithm को पाठ में कई निश्चित तारों को खोजने के लिए अनुकूलित किया गया है, लेकिन निश्चित रूप से इस एल्गोरिदम को कार्यान्वित करना केवल पैटर्न बनाने की तुलना में काफी प्रयास है।

+0

यह वास्तव में वास्तव में चालाक है। मुझें यह पसंद है! +1 – myrocks2

+0

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

+0

@denov युद्ध और शांति के साथ परीक्षण किया गया http://www.gutenberg.org/ebooks/2600 जिसमें 65007 लाइनें हैं। लक्ष्य के रूप में targetWords थे। वर्तमान टाइममिलिस की जांच करके इसे समय देने पर, मुझे हैश मैप-आधारित समाधान के लिए 350ms मिलता है, नियमित अभिव्यक्ति समाधान के लिए 200ms, पहले रेगेक्स के साथ (इसलिए वीएम अभी भी गर्म हो रहा है)। Regex से पहले हैश मैप स्विच करते समय, इसके 390ms हैश मैप बनाम 160 एमएमएस regex। मैंने मेमोरी पदचिह्न को माप नहीं लिया (जो हैश मैप समाधान के लिए भी अधिक होना चाहिए)। –

0

मैं प्रत्येक शब्द के लिए समानांतरता के लिए इसे ExecutorService के साथ कार्यान्वित करने का प्रयास करूंगा। http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html

निश्चित थ्रेड पूल के आकार के साथ उदाहरण के लिए:

Executors.newFixedThreadPool(20); 
6

100k शब्द की आपकी सूची (एक बार) एक HashSet के लिए जोड़ा जा सकता है। अपनी सूची के माध्यम से पुनरावृत्ति करने के बजाय, wordSet.contains() का उपयोग करें - एक हैशसेट इसके लिए निरंतर समय का प्रदर्शन देता है, इसलिए सूची के आकार से प्रभावित नहीं होता है।

+0

मुझे लगता है कि उनके शब्द वाक्यांश हैं और शब्दों में ऐसा नहीं है जो स्ट्रिंग में स्ट्रिंग को खोजने के लिए काम नहीं करेगा। – denov

+1

@denov OK, शायद एक और जटिल संरचना की आवश्यकता हो सकती है जैसे 'हैश मैप <स्ट्रिंग, सेट >' - कुंजी एक बार प्री-प्रोसेसिंग करना है (प्रत्येक लूप में निरंतर डेटा को विभाजित करने के बजाय) और पुनरावृत्ति से बचने का प्रयास करें। – MattR

+0

- मेरा उत्तर – denov

2

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

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

1

यदि आप संपूर्ण वाक्यांश या सूची से केवल एक शब्द चाहते हैं तो मैं थोड़ा उलझन में हूं। यदि आप सूची से स्ट्रिंग प्राप्त करने का प्रयास कर रहे हैं तो अगर आपके लक्षित शब्दों में से एक स्ट्रिंग में है तो यह आपके लिए काम करना चाहिए।

String[] targetWords= new String[]{"inventory", "finished", "goods"}; 
    List<String> listOfWords = new ArrayList<String>(); 

    // build lookup map 
    Map<String, ArrayList<String>> lookupMap = new HashMap<String, ArrayList<String>>(); 
    for(String words : listOfWords) { 
     for(String word : words.split(" ")) { 
      if(lookupMap.get(word) == null) lookupMap.put(word, new ArrayList<String>()); 
      lookupMap.get(word).add(words); 
     } 
    } 

    // find phrases 
    Set<String> extractedStrings = new HashSet<String>(); 
    for(String target : targetWords) { 
     if(lookupMap.containsKey(target)) extractedStrings.addAll(lookupMap.get(target)); 
    } 
+0

भ्रम के लिए खेद है, सूचीऑफWords में वाक्यांश हैं और मैंने अलग-अलग शब्दों को प्राप्त करने के लिए उन्हें विभाजित किया ताकि मैं उन्हें लक्ष्य वाक्यांश में शब्दों के विरुद्ध तुलना कर सकूं। यदि मुझे गलत नहीं लगता है, तो क्या आपका समाधान संभावित रूप से उन वाक्यांशों के लिए डुप्लिकेट नहीं बनाएगा जिनमें एक से अधिक शब्द मेल खाते हैं? उदाहरण के लिए, लक्ष्य शब्द मानना ​​वही है, अगर मैं "माल की सूची" वाक्यांश में आया हूं, तो निकाले गए वाक्यांशों में दो बार "वाक्यांश" और "सामान" शब्द लुकअप मैप में से दो बार समाप्त हो जाएंगे? क्या मुझे बाद में सभी डुप्लिकेट को फिर से हटा देना चाहिए और हटा देना चाहिए? – myrocks2

+0

मैंने अपना कोड अपडेट किया है इसलिए निकाला गया हैट्रिंग्स एक सेट है ताकि आपके पास डुप्लिकेट न हो। – denov

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