यूनिकोड स्ट्रिंग्स के लिए कुशल ट्राई कार्यान्वयन


12

मैं एक कुशल स्ट्रिंग trie कार्यान्वयन की तलाश कर रहा हूं। ज्यादातर मुझे ऐसा कोड मिला है:

जावा में प्रासंगिक कार्यान्वयन (प्रति विकिपीडिया)

मैं इन कार्यान्वयनों को ज्यादातर दो कारणों से नापसंद करता हूं:

  1. वे केवल 256 ASCII वर्णों का समर्थन करते हैं। मुझे सिरिलिक जैसी चीजों को कवर करने की आवश्यकता है।
  2. वे बेहद अक्षम हैं।

प्रत्येक नोड में 256 संदर्भों की एक सरणी होती है, जो जावा में 64 बिट मशीन पर 4096 बाइट्स होती है। इनमें से प्रत्येक नोड में 4096 बाइट्स वाले प्रत्येक के साथ 256 सबनॉड्स तक हो सकते हैं। इसलिए प्रत्येक ASCII 2 वर्ण स्ट्रिंग के लिए एक पूर्ण Trie को 1MB से अधिक की आवश्यकता होगी। तीन चरित्र तार? केवल नोड्स में सरणियों के लिए 256MB। और इसी तरह।

बेशक, मैं अपने ट्राइ में 16 मिलियन तीन कैरेक्टर स्ट्रिंग्स रखने का इरादा नहीं रखता हूं, इसलिए बहुत सारी जगह बर्बाद हो गई है। इनमें से अधिकांश सरणियाँ केवल शून्य संदर्भ हैं क्योंकि उनकी क्षमता सम्मिलित कुंजियों की वास्तविक संख्या से अधिक है। और अगर मैं यूनिकोड जोड़ता हूं, तो सरणियां और भी बड़ी हो जाती हैं (चार्ट में जावा में 256 के बजाय चारक मान हैं)।

क्या तार के लिए एक कुशल तिकड़ी बनाने की कोई उम्मीद है? मैंने इस प्रकार के कार्यान्वयन पर कुछ सुधारों पर विचार किया है:

  • संदर्भों की सरणी का उपयोग करने के बजाय, मैं आदिम पूर्णांक प्रकार की एक सरणी का उपयोग कर सकता हूं, जो संदर्भों की एक सरणी में नोड्स में अनुक्रमित करता है, जिसका आकार वास्तविक नोड्स की संख्या के करीब है।
  • मैं 4 बिट भागों में तारों को तोड़ सकता हूं जो कि एक गहरे पेड़ की कीमत पर आकार 16 के नोड सरणियों के लिए अनुमति देगा।

जवाबों:


2

क्या आप इस trie के लिए उपयोग कर रहे हैं? आपके द्वारा धारण किए जाने वाले शब्दों की कुल संख्या क्या है, और उनके घटक वर्णों की विशिष्टता क्या है? और सबसे महत्वपूर्ण है, क्या एक त्रिक भी उपयुक्त है (शब्दों की सूची के लिए उपसर्ग का एक सरल नक्शा)?

एक इंटरमीडिएट टेबल और इंडेक्स के साथ पॉइंटर्स को बदलने का आपका विचार काम करेगा, बशर्ते आपके पास अपेक्षाकृत छोटे शब्दों का एक छोटा सेट और एक विरल कैरेक्टर सेट हो। अन्यथा आप अपनी मध्यवर्ती तालिका में अंतरिक्ष से बाहर चलने का जोखिम उठाते हैं। और जब तक आप शब्दों के एक बहुत छोटे सेट को नहीं देख रहे हैं, तब तक आप वास्तव में उस स्थान को नहीं बचाएंगे: 32-बिट मशीन पर एक संदर्भ के लिए कम से कम 4 बाइट्स के लिए 2 बाइट्स। यदि आप 64-बिट JVM पर चल रहे हैं, तो बचत अधिक होगी।

वर्णों को 4-बिट विखंडन में तोड़ने के बारे में आपका विचार शायद आपको ज्यादा नहीं बचाएगा, जब तक कि आपके सभी अपेक्षित चरित्र एक अत्यंत सीमित सीमा में न हों (हो सकता है कि शब्दों को US-ASCII तक सीमित शब्दों के लिए ठीक है, सामान्य यूनिकोड कोरस के साथ संभव नहीं है) )।

यदि आपके पास एक विरल चरित्र सेट है, तो HashMap<Character,Map<...>>आपका सबसे अच्छा कार्यान्वयन हो सकता है। हां, प्रत्येक प्रविष्टि बहुत बड़ी होगी, लेकिन यदि आपके पास कई प्रविष्टियां नहीं हैं, तो आपको एक समग्र जीत मिलेगी। (एक साइड नोट के रूप में: मुझे हमेशा लगा कि यह मज़ेदार था कि ट्रिक्स पर विकिपीडिया लेख दिखाया गया है - शायद अभी भी करता है - एक हैशेड डेटा संरचना पर आधारित एक उदाहरण, पूरी तरह से उस पसंद के स्थान / समय के ट्रेडऑफ़्स की अनदेखी करना)

अंत में, आप पूरी तरह से ट्राई से बचना चाह सकते हैं। यदि आप एक मानव भाषा में सामान्य शब्दों के कोष को देख रहे हैं (सक्रिय शब्दों में १०,००० शब्द, ४- characters अक्षर लंबे शब्द के साथ), तो आप संभवतः एक बेहतर के साथ दूर हो जाएंगे HashMap<String,List<String>, जहाँ कुंजी संपूर्ण उपसर्ग है।


- संदर्भ 32-बिट पर 8 बाइट्स, 64-बिट मशीनों पर 16 बाइट्स हैं - यह स्वत: पूर्ण कार्यक्षमता के लिए है - स्ट्रिंग्स में अधिकांश वर्ण ASCII रेंज में हैं, लेकिन इसमें कुछ केंद्रीय यूरोपीय वर्ण फेंके गए हैं। यही कारण है कि मुझे छोटे ब्रांचिंग की आवश्यकता थी। 256 की तुलना में, क्योंकि यह बड़ी संख्या में पात्रों को काट देगा। मैं हाशपैम <स्ट्रिंग, सूची <स्ट्रिंग >> बेहतर या तेज या कम मेमोरी खपत नहीं देखता, यद्यपि वास्तव में लिखना और उपयोग करना आसान है। लेकिन मैं HashMap <चरित्र, मानचित्र> विचार के लिए स्वीकार करूंगा। 128 से अधिक के लिए ठीक होगा (मेरे मामले में दुर्लभ - चीनी पाठ के लिए बुरा होगा)।
रोक्

4

यदि आप UTF8 में स्ट्रिंग्स को एन्कोड करते हैं तो आप मानक 256 ब्रांचिंग ट्राइ का उपयोग कर सकते हैं और फिर भी यूनिकोड संगत हो सकते हैं

आपको यह भी ध्यान देना चाहिए कि संभावित 128 अस्ससी वर्णों में से केवल 70 या उससे अधिक वर्ण (जो कि सभी UTF8 में 1 बाइट को सांकेतिक शब्दों में बदलना है) को सबसे भारी पाया जाएगा जिसे आप इसके लिए अनुकूलित कर सकते हैं (जैसे संयुक्त नियंत्रण वर्णों के स्थान पर सामान्य डिग्राफ शामिल करें )


मुझे पता है कि UTF8 का प्रतिनिधित्व इस तरह किया जा सकता है। हालाँकि यह अभी भी स्मृति खपत को हल नहीं करता है जो अभी भी काफी अधिक है। मूल 256 रेंज में वर्णों को स्वैप करने के लिए बहुत सारे स्विच वाक्यों की आवश्यकता होगी, मुझे संदेह है कि यह इसके लायक होगा। जहां तक ​​UTF-8 जाता है ... यह वास्तव में एक मुद्दा है जो मैं अभी विचार कर रहा हूं। जावा स्ट्रिंग UTF-16 वर्णों का उपयोग करता है, जिसे मैं आसानी से प्राप्त कर सकता हूं, मैं बाइट द्वारा इन बाइट को एनकोड कर सकता हूं। या मैं UTF-8 में परिवर्तित कर सकता हूं और इसका उपयोग कर सकता हूं। इस समय यह मेरे लिए अस्पष्ट है कि UTF-16 से UTF-8 में परिवर्तित होने की लागत निषेधात्मक है या नहीं।
रोक्

अधिकांश समय आप इसका उपयोग करने वाली भाषा क्या है? सब कुछ के लिए अनुकूलन करने की कोशिश करना असंभव है (या यह पहले से ही किया गया होगा) इसलिए सामान्य मामले के लिए अनुकूलन करें
शाफ़्ट फ्रीक

1
यह उन बहुत कम उपयोग मामलों में से एक है जहां CESU-8 UTF-8 के लिए बेहतर होगा: यहाँ यह बहुत बड़ा फायदा है कि यह UTF-8 कोडपॉइंट से संबंधित CESU-8 कोडपॉइंट से प्राप्त करना तुच्छ है (जबकि आपको इसकी आवश्यकता होगी यूटीएफ -16 कोडपॉइंट्स को संबंधित यूटीएफ -8 कोडपॉइंट्स पर लाने के लिए 1-2 डीकोड करना)।
जोआचिम सॉर

1
@ratchetfreak Java हालांकि मुझे लगता है कि इस सवाल को अधिकांश भाषाओं में सामान्यीकृत किया जा सकता है। मुझे लगता है कि सी में आप byte*किसी भी प्रकार को बिटवाइज़ ट्राई में एनकोड करने के लिए बस पॉइंटर कास्ट कर सकते हैं ।
रोक्

@UMAD का मतलब था कि इनपुट स्ट्रिंग्स किस भाषा में होंगी (अंग्रेजी, फ्रेंच, जर्मन, ...)
शाफ़्ट फ्रीक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.