2010-08-06 13 views
10

मैं "संपीड़ित सरणी"/"संपीड़ित वेक्टर" वर्ग (नीचे विवरण) बनाना चाहता हूं, जो यादृच्छिक डेटा को अधिक या कम स्थिर समय के साथ एक्सेस करने की अनुमति देता है।यादृच्छिक डेटा एक्सेस के साथ संपीड़ित वेक्टर/सरणी वर्ग

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

मेरे विकल्प क्या हैं (उपयुक्त एल्गोरिदम/पहले से उपलब्ध कंटेनर - यदि कोई हैं तो)?

कंटेनर विवरण:

  1. कंटेनर (जैसे std :: वेक्टर के रूप में)
  2. एक बार कंटेनर आरंभ नहीं हो जाता समान तत्वों की एक रेखीय सरणी के रूप में कार्य करता है, डेटा स्थिर है और कभी नहीं बदलता। कंटेनर को केवल पढ़ने के लिए पहुंच प्रदान करने की आवश्यकता है।
  3. कंटेनर को सरणी/std :: vector - i.e. ऑपरेटर के माध्यम से उपयोग किए जाने वाले मानों का व्यवहार करना चाहिए [], वहाँ .size(), आदि
  4. यह अच्छा होगा अगर मैं इसे टेम्पलेट क्लास के रूप में बना सकता हूं।
  5. डेटा तक पहुंच निरंतर समय-समय पर होनी चाहिए। मुझे हर तत्व के लिए एक ही एक्सेस समय की आवश्यकता नहीं है, लेकिन मुझे अंतिम तत्व प्राप्त करने के लिए सब कुछ डिकंप्रेस नहीं करना चाहिए।

उपयोग उदाहरण:
डेटा पर बाइनरी खोज।

डेटा विवरण:
1. डेटा स्ट्रक्चर ज्यादातर फ्लोट्स और कुछ चींटियों से युक्त होता है। चींटियों की तुलना में अधिक तैरते हैं। कोई संबंध नही।
2. यह संभावना नहीं है कि सरणी में कई समान तत्व हैं, इसलिए डेटा को अनुक्रमणित करना संभव नहीं होगा।
3. एक तत्व का आकार 100 बाइट से कम है।
4. प्रति कंटेनर का कुल डेटा आकार कुछ किलोबाइट्स और कुछ मेगाबाइट्स के बीच है।
5. डेटा स्पैस नहीं है - यह तत्वों का निरंतर ब्लॉक है, उनमें से सभी को असाइन किया गया है, कोई "खाली स्लॉट" नहीं हैं।

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

विचार/राय?

+1

"अधिक या कम" निरंतर समय क्या है? या तो यह स्थिर है, या तो यह नहीं है। अन्यथा दिलचस्प सवाल। क्या आप वाकई कई मौजूदा कंटेनर कक्षाओं के साथ जो कुछ भी चाहते हैं वह नहीं कर सकते हैं? – ereOn

+3

"संपीड़ित" भाग इसमें प्रवेश करता है? आपने उस हिस्से को कभी समझाया नहीं है। क्या आप सिर्फ gzipped blobs, या ऐसा कुछ करने के लिए पॉइंटर्स के वेक्टर का उपयोग कर सकते हैं? या क्या आपका मतलब संकुचित है क्योंकि आपके पास एक स्पैससेट डेटासेट है, इसलिए एक बेवकूफ वेक्टर में बहुत सारे खाली स्लॉट होंगे? – jalf

+0

इसके अलावा आप कहते हैं कि तत्व केवल फ्लोट्स और इनट्स हैं, और यह कि एक तत्व 100 बाइट से अधिक नहीं है। जब तक आप कुछ 800 बिट्स आर्किटेक्चर पर काम नहीं करते हैं, तो आप आखिरी आवश्यकता को छोड़ सकते हैं। – ereOn

उत्तर

10

मैं इसे ले कि आप एक सरणी जिसका तत्वों संग्रहीत नहीं हैं वेनिला चाहते हैं, लेकिन संकुचित, स्मृति के उपयोग को कम करने के।

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

एक समाधान एक सूचकांक तालिका के साथ एक साथ Huffmann coding उपयोग करने के लिए है।

हफमान कोडिंग प्रत्येक इनपुट प्रतीक (उदाहरण के लिए, एक ASCII बाइट) को प्रतिस्थापित करके परिवर्तनीय बिट लंबाई के दूसरे प्रतीक के साथ काम करता है, पूरे स्ट्रीम में अवसर की आवृत्ति के आधार पर।उदाहरण के लिए, चरित्र E अक्सर प्रकट होता है, इसलिए इसे थोड़ा सा अनुक्रम मिलता है, जबकि 'डब्ल्यू' शायद ही कभी होता है और एक लंबा अनुक्रम मिलता है।

E -> 0b10 
W -> 0b11110 

अब, इस विधि के साथ अपने पूरे सरणी को संपीड़ित करें। दुर्भाग्यवश, चूंकि आउटपुट प्रतीकों में परिवर्तनीय लंबाई होती है, इसलिए अब आप अपने डेटा को पहले से इंडेक्स नहीं कर सकते: आइटम नंबर 15 अब stream[15*sizeof(item)] पर नहीं है।

सौभाग्य से, इस समस्या को अतिरिक्त अनुक्रमणिका तालिकाindex का उपयोग करके हल किया जा सकता है जो स्टोर करता है जहां प्रत्येक आइटम संपीड़ित स्ट्रीम में शुरू होता है। दूसरे शब्दों में, आइटम 15 के लिए संपीड़ित डेटा stream[index[15]] पर पाया जा सकता है; सूचकांक तालिका परिवर्तनीय आउटपुट लंबाई जमा करता है।

तो, मद 15 प्राप्त करने के लिए, तो आप बस stream[index[15]] पर बाइट्स decompressing शुरू करते हैं। यह काम करता है क्योंकि हफमान कोडिंग आउटपुट के लिए कुछ भी नहीं करता है, यह सिर्फ नए कोड शब्दों को जोड़ता है, और आप स्ट्रीम के अंदर सभी पिछली वस्तुओं को डीकोड किए बिना डीकोडिंग शुरू कर सकते हैं।

बेशक, सूचकांक तालिका कुछ ओवरहेड जोड़ती है; आप ग्रैन्युलरिटी को ट्विक करना चाहते हैं ताकि compressed data + index tableoriginal data से भी छोटा हो।

+1

संशोधन के लिए (तत्वों के स्वयं, वेक्टर की लंबाई नहीं), सूचकांक तालिका एक फेनविक पेड़ हो सकती है। यह न्यूनतम परिवर्तनों के साथ फ्लाई पर इंडेक्स को पुन: सम्मिलित करने की अनुमति देगा। –

0

ठीक है, मेरी समझ में से सबसे अच्छा, आप जो चाहते हैं वह किसी प्रकार का एक्सेसर टेम्पलेट है।

const T &operator->(void) const; 

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

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

4

क्या आप एक एम्बेडेड सिस्टम के लिए कोडिंग कर रहे हैं और/या आपके पास इनमें से सैकड़ों या हजारों कंटेनर हैं? यदि नहीं, जबकि मुझे लगता है कि यह एक दिलचस्प सैद्धांतिक प्रश्न (+1) है, मुझे संदेह है कि डिकंप्रेशन करने के परिणामस्वरूप मंदी गैर-तुच्छ होगी और std::vector का उपयोग करना बेहतर होगा।

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

यदि आप तय करते हैं कि संपीड़न करना अभी भी उचित है, तो कम से कम कुछ संभावनाएं हैं, हालांकि कोई भी पूर्व-लिखित नहीं है। आप प्रत्येक व्यक्तिगत तत्व को संपीड़ित कर सकते हैं, संपीड़ित डेटा खंड में पॉइंटर संग्रहीत कर सकते हैं। फिर सूचकांक पहुंच अभी भी स्थिर है, केवल वास्तविक डेटा को कम करने की आवश्यकता है। संभावित रूप से प्रॉक्सी ऑब्जेक्ट का उपयोग करने से वास्तविक डेटा डिकंप्रेशन आसान और अधिक पारदर्शी हो जाएगा (और हो सकता है कि आप अंतर्निहित कंटेनर के रूप में std::vector का उपयोग भी कर सकें)।

वैकल्पिक रूप से, std::deque अपने डेटा को पहले से ही भागों में संग्रहीत करता है, ताकि आप यहां एक समान दृष्टिकोण का उपयोग कर सकें। उदाहरण के लिए std::vector<compressed_data_chunk> जहां प्रत्येक खंड में आपके अंतर्निहित कंटेनर के रूप में एक साथ संपीड़ित 10 आइटम कहते हैं। फिर भी आप सीधे उस हिस्से को अनुक्रमित कर सकते हैं जिसकी आपको आवश्यकता है, इसे डिकंप्रेस करें, और आइटम को डिकंप्रेस्ड डेटा से वापस कर दें। यदि आप चाहते हैं, तो आपकी युक्त ऑब्जेक्ट (जिसमें vector है) लगातार पहुंच पर अतिरिक्त प्रदर्शन के लिए हाल ही में डिकंप्रेस्ड चंक या दो को कैश कर सकता है (हालांकि यह बाइनरी खोज को बहुत अधिक मदद नहीं करेगा)।

+0

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

3

मैं थोड़ी देर के लिए इस बारे में सोच रहा हूं। ,

  • फ्लायवेट क्योंकि पुनरावृत्ति इस पैटर्न के साथ कम किया जा सकता है: देखने के एक सैद्धांतिक दृष्टिकोण से मैं 2 संभावनाओं की पहचान की।
  • क्रमबद्धता (संपीड़न क्रमबद्धता के कुछ फार्म है)

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

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

मैं कुछ "Huffman कोडिंग" समाधान देखा है, लेकिन इसका मतलब है कि प्रत्येक संरचना के लिए एक एक compressing एल्गोरिथ्म प्रदान करने के लिए की जरूरत है। सामान्य बनाना आसान नहीं है।

तो मैं नहीं बल्कि दूसरे रास्ते जाने के लिए और 'zlib' की तरह एक संपीड़ित करने लाइब्रेरी का उपयोग, उदाहरण के लिए lzo की तरह एक तेजी से एल्गोरिथ्म उठा था।

  • बी * पेड़ (या एक प्रकार) प्रति नोड मदों की बड़ी संख्या के साथ (क्योंकि यह कदम नहीं करता है) की तरह कहते हैं कि 1001 प्रत्येक नोड वस्तुओं की सरणी के एक संपीड़ित प्रतिनिधित्व होता है। सूचकांक संपीड़ित नहीं हैं।
  • संभवत: पिछले 5 (या तो) decompressed नोड्स या कुछ संग्रहित करते हुए cache_view कंटेनर का उपयोग करने की। एक और संस्करण संदर्भ गिनती को कार्यान्वित करना है और डेटा को तब तक असम्पीडित रखना है जब तक कि किसी को नोड में किसी एक आइटम को संभाल नहीं लेता है।

कुछ टिप्पणी:

  • आप आइटम की एक बड़ी संख्या/प्रति नोड चाबियाँ आप निरंतर उपयोग समय के पास होना चाहिए अगर, 1001 के साथ उदाहरण के लिए इसका मतलब है कि वहाँ अविवेक रूप में लंबे समय का केवल 2 स्तर हैं कि चूंकि आप एक लाख से भी कम वस्तुओं को स्टोर करते हैं, एक अरब आदि के लिए संकेत के 3 स्तर ...
  • आप ऐसी संरचना के साथ एक पठनीय/लिखने योग्य कंटेनर बना सकते हैं। मैं इसे ऐसा कर दूंगा ताकि मैं नोड लिखने के बाद ही पुनः कंप्रेस कर सकूं।
संबंधित मुद्दे