फास्ट एल्गोरिथ्म एक इनपुट मूल्य ब्रैकेटिंग फ़्लोट की जोड़ी को खोजने के लिए फ़्लोट्स के एक क्रमबद्ध सरणी की खोज करने के लिए


10

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

एक भोली दृष्टिकोण सरणी के माध्यम से एक सरल रैखिक खोज करना होगा। यह इस तरह लग सकता है:

void FindClosestFloatsInArray( float input, std::vector<float> array, 
                               float *min_out, float *max_out )
{
    assert( input >= array[0] && input < array[ array.size()-1 ] );
    for( int i = 1; i < array.size(); i++ )
    {
        if ( array[i] >= input )
        {
            *min = array[i-1];
            *max = array[i];
        }
    }
}

लेकिन जाहिर है जैसे-जैसे यह सरणी बड़ी होती जाएगी, यह धीमी और धीमी होती जाएगी।

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

अधिक जानकारी: सरणी में फ़्लोटिंग पॉइंट मान आवश्यक रूप से समान रूप से वितरित नहीं किए गए हैं (अर्थात, सरणी में मान शामिल हो सकते हैं "1.f, 2.f, 3.f, 4.f, 100.f, 1200.f , 1203.f, 1400.f "।

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


आपको क्या लगता है कि आपकी बाइनरी खोज जल्दी समाप्त नहीं कर सकती है? निश्चित रूप से आप केवल i और i + 1 पर तत्वों का परीक्षण कर सकते हैं यह देखने के लिए कि क्या वे लक्ष्य मान को ब्रैकेट करते हैं, और यदि वे करते हैं तो समाप्त कर देते हैं?
पॉल आर।

वैकल्पिक रूप से, मैं i और i-1 तत्वों को यह देखने के लिए परीक्षण कर सकता हूं कि क्या वे लक्ष्य मान को ब्रैकेट करते हैं। मुझे यह भी परखना होगा कि क्या 'i' था> = array.size () - 1 इसलिए मैं आपका परीक्षण करने से बच सकता था, और क्या यह <= 0 था इसलिए मैं अपना परीक्षण करने से बच सकता था ... यह वास्तव में बहुत कुछ है एक प्रारंभिक चरण के लिए जांच करने के लिए, प्रत्येक चरण पर प्रदर्शन करने के लिए अतिरिक्त सशर्तियां। मुझे लगता है कि वे एल्गोरिथ्म को बहुत धीमा कर देंगे, हालांकि मैं यह मानूंगा कि मैंने वास्तव में अभी तक ऐसा नहीं किया है।
ट्रेवर पॉवेल

3
यह इतना जटिल होने की आवश्यकता नहीं है - यदि आपका सरणी आकार N का है, तो आपको बस इसका इलाज करने की आवश्यकता है जैसे कि यह आकार N का था - 1. इस तरह से i + 1 पर हमेशा एक मान्य तत्व होता है। आप एक करते हैं तत्व i के लिए n - 1 तत्व पर द्विआधारी खोज जो आपके लक्ष्य मान से कम है, तत्व i + 1 से अधिक है तो लक्ष्य मान।
पॉल आर।

जवाबों:


11

प्रश्न में कोड (एक रेखीय खोज), जैसा कि आप सही ढंग से इंगित करते हैं, बड़े फ्लोट सरणियों के लिए धीमा होने वाला है। तकनीकी रूप से यह O (n) है जहाँ n आपके सरणी में फ्लोट मानों की संख्या है।

सामान्य तौर पर, एक ऑर्डर किए गए एरे में एक वैल्यू खोजने के लिए आप जो सबसे अच्छा कर सकते हैं वह है किसी प्रकार का एक रीक्रसिव ट्री सर्च (जैसे बाइनरी सर्च), जिस स्थिति में आप तत्वों की संख्या में O (लॉग एन) लुकअप समय प्राप्त कर सकते हैं। अपने सरणी में। O (log n) n के बड़े मानों के लिए O (n) से बहुत बेहतर है।

इसलिए मेरा सुझाया गया दृष्टिकोण सरणी का एक सरल द्विआधारी खोज होगा , अर्थात:

  1. अपने पूरे फ्लोट सरणी को कवर करने के लिए न्यूनतम / अधिकतम पूर्णांक अनुक्रमित सेट करें
  2. सूचकांक के मध्य में मान के मध्य में परीक्षण करें = खोज मान x के विरुद्ध (न्यूनतम + अधिकतम / 2)
  3. यदि x इस मान से कम है, तो अधिकतम से मध्य तक सेट करें, अन्यथा मिनट को मध्य में सेट करें
  4. दोहराएं (2-4) जब तक आपको सही मूल्य नहीं मिला

यह एक ओ (लॉग एन) एल्गोरिथ्म है जो लगभग सभी स्थितियों के लिए पर्याप्त तेज़ होना चाहिए। वास्तव में, यह प्रत्येक चरण में खोज की जाने वाली सीमा को आधा करके काम करता है जब तक कि आपको सही मूल्य नहीं मिलता है।

सरल द्विआधारी खोज को जानवर करना वास्तव में कठिन है, इसलिए यदि आपने पहले से ही इसे सही तरीके से लागू किया है तो आप पहले से ही इष्टतम के करीब हो सकते हैं। हालाँकि, यदि आप डेटा के वितरण को जानते हैं और / या लुकअप मानों की सीमित सीमा (x) है, तो अभी भी कुछ और उन्नत तरकीबें हैं जिन्हें आप आज़मा सकते हैं:

  • बकेटिंग - बकेट्स बनाएँ (जैसे दो पूर्णांकों के बीच प्रत्येक अंतराल के लिए), जिनमें से प्रत्येक में दो बाउंडिंग पूर्णांकों के बीच फ्लोट मानों की एक छोटी छँटाई सूची है और प्रत्येक सीमा के ठीक नीचे दो मान और तुरंत ऊपर। फिर आप अपनी खोज (trunc (x) +0.5) पर शुरू कर सकते हैं। यदि आप उचित आकार की बाल्टियों का चयन करते हैं, तो यह आपको एक अच्छा गति प्रदान करता है (यह प्रभावी रूप से पेड़ की शाखाओं को बढ़ाने वाला है .....)। यदि पूर्णांक आपके लिए काम नहीं करते हैं, तो आप कुछ अन्य निश्चित-बिंदु परिशुद्धता (जैसे 1/16 के गुणकों) के बकेट को आज़मा सकते हैं।
  • बिट-मैपिंग - यदि संभव लुकअप मानों की सीमा काफी छोटी है, तो आप x के बिटवाइस मान द्वारा अनुक्रमित एक बड़ी लुकअप तालिका बनाने का प्रयास कर सकते हैं। यह O (1) होगा लेकिन आपको बहुत सारी मेमोरी की आवश्यकता हो सकती है जो आपके कैश पर बहुत ही अमित्र होगा ... इसलिए सावधानी के साथ उपयोग करें। यह विशेष रूप से बुरा है क्योंकि आप फ्लोट मूल्यों को देख रहे हैं, इसलिए आपको कम महत्वपूर्ण बिट्स के सभी के लिए खाते में कई जीबी की आवश्यकता हो सकती है ......
  • गोलाई और हैशिंग - हैश टेबल शायद इस समस्या के लिए सबसे अच्छी डेटा संरचना नहीं हैं, लेकिन यदि आप थोड़ी सटीकता खो सकते हैं तो वे काम कर सकते हैं - बस अपने लुकअप मानों के सबसे कम बिट्स को गोल करें और सीधे देखने के लिए हैशमैप का उपयोग करें सही मूल्य। आपको हैशमैप आकार और सटीक के बीच सही व्यापार-बंद पर प्रयोग करना होगा, और यह भी सुनिश्चित करना होगा कि सभी संभावित हैश मान आबाद हैं, इसलिए यह थोड़ा मुश्किल हो सकता है ......
  • ट्री-बैलेंसिंग - आपके आदर्श पेड़ में बाएं या दाएं जाने का 50% मौका होना चाहिए। इसलिए यदि आप लुकअप मान (x) के वितरण के आधार पर एक पेड़ बनाते हैं, तो आप न्यूनतम परीक्षणों के साथ उत्तर देने के लिए पेड़ को अनुकूलित कर सकते हैं। यह एक अच्छा समाधान होने की संभावना है यदि आपके फ्लोट सरणी में बहुत सारे मूल्य एक साथ बहुत करीब हैं, क्योंकि यह आपको इन शाखाओं को अक्सर खोजने से बचने में सक्षम करेगा।
  • क्रिट-बिट ट्री - ये अभी भी पेड़ हैं (इसलिए अभी भी हे (लॉग एन) ...) लेकिन कुछ मामले: आपको तुलनात्मक कार्य करने के लिए अपने फ़्लोट्स को कुछ निश्चित-बिंदु प्रारूप में बदलने की आवश्यकता होगी

हालाँकि, जब तक आप एक विशेष स्थिति में नहीं होते हैं, तब तक मैं साधारण बाइनरी खोज के साथ चिपके रहने की सलाह दूंगा। कारण:

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

1

यह काफी सरल लगता है:

फ्लोट के लिए एक द्विआधारी खोज करें जिसे आप बाध्य करना चाहते हैं - ओ (लॉग एन) समय।

फिर इसके बाईं ओर का तत्व निचला बाउंड है, और इसके दाईं ओर का तत्व ऊपरी बाउंड है।


0

एक पेड़ में झांकियों को संग्रहित करने का स्पष्ट उत्तर है । एक पेड़ में 'पिछला' और 'अगला' ऑपरेशन सहायक होते हैं। तो बस अपने मूल्य पर एक just अगला ’करें, और फिर पहले चरण में आपको मिलने वाले मूल्य पर एक next पिछला’ करें।


1
यह अनिवार्य रूप से एक बाइनरी खोज के समान है।
केविन क्लाइन

-1

यह पत्र ("गुणन के बिना सबलगैरिथिक खोज") रुचि का हो सकता है; यहां तक ​​कि इसमें कुछ स्रोत कोड भी होते हैं। तुलना के प्रयोजनों के लिए, आप एक ही बिट-पैटर्न के साथ पूर्णांक के रूप में एक फ्लोट नंबर का इलाज कर सकते हैं; यह IEEE फ़्लोटिंग पॉइंट मानक के डिज़ाइन लक्ष्यों में से एक था।

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