टाइल्स के एक सेट की सीमा के लिए कुशल एल्गोरिदम


12

मेरे पास एक ज्ञात परिमित आकार की टाइलों का एक ग्रिड है जो एक मानचित्र बनाता है। नक्शे के अंदर की कुछ टाइलों को एक सेट में रखा जाता है जिसे एक क्षेत्र के रूप में जाना जाता है। यह क्षेत्र जुड़ा हुआ है, लेकिन इसके आकार के बारे में कुछ भी ज्ञात नहीं है। ज्यादातर समय यह काफी नियमित रूप से बूँद होती है, लेकिन यह एक दिशा में बहुत लम्बी हो सकती है, और संभवतः इसमें छेद भी हो सकते हैं। मुझे इस क्षेत्र की बाहरी (बाहरी) सीमा को खोजने में दिलचस्पी है।

यही है, मैं उन सभी टाइलों की एक सूची चाहता हूं जो क्षेत्र में स्वयं के बिना टाइलों में से एक को छूते हैं। इसे खोजने का एक कुशल तरीका क्या है?

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

def find_border_of_territory(territory):
    border = []
    for tile in territory:
        for neighbor in tile.neighbors():
            if neighbor not in territory and neighbor not in border:
                border.add(neighbor)

हालांकि यह धीमा है और मैं कुछ बेहतर करना चाहूंगा। मेरे पास क्षेत्र में एक ओ (एन) लूप है, सभी पड़ोसियों पर एक और लूप (एक छोटा, लेकिन अभी भी) है और फिर मुझे दो सूचियों पर सदस्यता की जांच करनी है, जिनमें से एक का आकार n है। यह O (n ^ 2) का एक भयानक स्केलिंग देता है। मैं सीमा और क्षेत्र के लिए सूचियों के बजाय सेटों का उपयोग करके ओ (एन) को कम कर सकता हूं ताकि सदस्यता की जांच तेजी से हो, लेकिन यह अभी भी महान नहीं है। मुझे उम्मीद है कि ऐसे कई मामले होंगे जहां क्षेत्र बड़ा है, लेकिन एक साधारण क्षेत्र बनाम लाइन स्केलिंग के कारण सीमा छोटी है। उदाहरण के लिए यदि क्षेत्र 5 त्रिज्या का एक हेक्स है, तो यह आकार 91 का है लेकिन सीमा केवल आकार 36 की है।

किसी को भी कुछ बेहतर प्रस्तावित कर सकते हैं?

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

नीचे दिए गए कुछ सवालों के जवाब देने के लिए। इस क्षेत्र का आकार लगभग 20 से 100 तक हो सकता है। क्षेत्र बनाने वाली टाइलों का सेट एक वस्तु का एक गुण है, और यह वह वस्तु है जिसे सभी सीमा टाइलों के एक सेट की आवश्यकता होती है।

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

मैं अब इस बात पर विचार कर रहा हूं कि एक सरल सीमा-खोज एल्गोरिथ्म करना सबसे अच्छा समाधान है। एकमात्र अतिरिक्त जटिलता यह उठती है कि यह सुनिश्चित करने के लिए कि सीमा हर बार पुनर्गणना की जाती है, जिसकी आवश्यकता हो सकती है, लेकिन इससे अधिक नहीं। मुझे पूरा विश्वास है कि यह मेरे वर्तमान ढांचे में मज़बूती से किया जा सकता है।

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


10
मुझे लगता है कि अगर आकृति के बारे में कुछ भी नहीं पता है, तो एक O (N) एल्गोरिथ्म उचित लगता है। कुछ भी तेजी से क्षेत्र के प्रत्येक तत्व को देखने की आवश्यकता नहीं होगी, जो केवल तभी काम करेगा जब आप आकार के बारे में कुछ जानते हैं, मुझे लगता है।
19-19

3
आपको शायद ऐसा करने की आवश्यकता नहीं है। इसके अलावा n बहुत बड़ी नहीं है, कुल टाइलों की तुलना में बहुत कम है।
प्रातः

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

2
महत्वपूर्ण: क्या यह एक वास्तविक निदान और कुशल प्रदर्शन समस्या है? एक समस्या के साथ कि छोटे (बस कुछ सौ तत्व, वास्तव में?) मुझे वास्तव में यह नहीं लगता है कि ओ (एन ^ 2) या ओ (एन) एक मुद्दा होना चाहिए। एक सिस्टम पर समय से पहले अनुकूलन की तरह लगता है जो हर फ्रेम को चलाने वाला नहीं है।
19 1719

1
सरल एल्गोरिथ्म O (n) है क्योंकि अधिकांश 6 पड़ोसी हैं।
एरिक

जवाबों:


11

एल्गोरिथ्म खोजना आमतौर पर एक डेटा संरचना के साथ सबसे अच्छा होता है जो एल्गोरिथ्म को आसान बनाता है।

इस मामले में, आपका क्षेत्र।

क्षेत्र सीमाओं और तत्वों का एक अव्यवस्थित (O (1) हैश) सेट होना चाहिए।

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

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

जब भी आप किसी क्षेत्र से या किसी टाइल को जोड़ते या हटाते हैं, तो यह O (1) कार्य करता है। सीमा पर जाने में O (सीमा की लंबाई) लगती है। जब तक आप जानना चाहते हैं कि "सीमा क्या है" क्षेत्र से तत्वों को जोड़ने / हटाने की तुलना में काफी अधिक है, तो यह जीतना चाहिए।


9

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

लेकिन अगर आप केवल बाहरी सीमा (आंतरिक छेद नहीं) खोजने से संबंधित हैं, तो हम इसे और अधिक कुशलता से कर सकते हैं:

  1. अपने क्षेत्र को अलग करने वाले एक किनारे का पता लगाएं। आप ऐसा कर सकते हैं ...

    • (यदि आप कम से कम एक क्षेत्र टाइल जानते हैं, और जानते हैं कि आपके नक्शे पर आपके पास एक जुड़ा हुआ क्षेत्र बूँद है)

      ... अपने क्षेत्र में एक मनमानी टाइल पर शुरू, और अपने नक्शे के निकटतम किनारे की ओर बढ़ रहा है। जैसा कि आप ऐसा करते हैं, अंतिम छोर को याद रखें जहां आपने एक क्षेत्र टाइल से गैर-क्षेत्र टाइल पर संक्रमण किया था। एक बार जब आप मानचित्र के किनारे से टकराते हैं, तो यह याद किया गया किनारा आपका शुरुआती किनारा होता है।

      यह स्कैन मानचित्र के व्यास में रैखिक है।

    • या (यदि आप नहीं जानते हैं कि आपके क्षेत्र की कोई टाइल पहले से कहां है, या आपके नक्शे में कई डिस्कनेक्ट किए गए क्षेत्र हो सकते हैं)

      ... अपने नक्शे के एक किनारे पर शुरुआत करें, प्रत्येक पंक्ति के साथ स्कैन करें जब तक कि आप एक भू-भाग टाइल नहीं मारते। आखिरी किनारा जिसे आपने गैर-भू-भाग से भू-भाग तक पार किया था, वह आपका प्रारंभिक किनारा है।

      यह स्कैन मानचित्र के क्षेत्र में सबसे खराब रेखीय हो सकता है (इसके व्यास में द्विघात), लेकिन अगर आपको अपनी खोज को बाधित करने के लिए कोई सीमा है (कहते हैं, आपको पता है कि क्षेत्र लगभग हमेशा मध्य पंक्तियों को पार करता है) तो आप इस सबसे खराब में सुधार कर सकते हैं- मामला व्यवहार।

  2. चरण 1 में पाए जाने वाले अपने शुरुआती किनारे पर शुरुआत करते हुए, इसे अपने इलाके की परिधि के आसपास का पालन करें, बाहर की ओर अपने गैर-सीमा टाइल को अपने सीमा संग्रह में जोड़ते हुए, जब तक कि आप शुरुआती किनारे पर वापस नहीं आते।

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

यदि आपके उदाहरण आपके वास्तविक डेटा आकार के परिमाण के एक जोड़े के आदेश के भीतर हैं, तो अपने आप को मैं भोले क्षेत्र की खोज के लिए जाऊंगा - यह अभी भी इतनी कम संख्या में टाइलों पर तेजी से तेज हो जाएगा, और लिखने के लिए काफी सरल है , समझने और बनाए रखने (आमतौर पर कम कीड़े के लिए अग्रणी!)


7

सूचना : सीमा पर एक टाइल है या नहीं यह केवल उस पर और उसके पड़ोसियों पर निर्भर करता है।

उसके कारण:

  • इस क्वेरी को lazily चलाना आसान है। उदाहरण के लिए: आपको पूरे मानचित्र पर सीमा की खोज करने की आवश्यकता नहीं है, केवल उस पर जो दिखाई दे रहा है।

  • इस क्वेरी को समानांतर में चलाना आसान है। वास्तव में, मैं ऐसा करने वाले कुछ shader कोड की छवि बना सकता हूं। और अगर आपको किसी अन्य दृश्य के लिए इसकी आवश्यकता है, तो आप एक बनावट को प्रस्तुत कर सकते हैं और इसका उपयोग कर सकते हैं।

  • यदि एक टाइल बदलती है, तो सीमा केवल स्थानीय रूप से बदलती है, जिसका अर्थ है कि आपको पूरी चीज़ को फिर से गणना करने की आवश्यकता नहीं है।

आप सीमा की गणना भी कर सकते हैं। यही है, यदि आप हेक्स को आबाद कर रहे हैं, तो आप तय कर सकते हैं कि उस समय एक टाइल सीमा है या नहीं। इसका मतलब है कि:

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

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


2

अपने क्षेत्र को एक टाइल ऊपर ले जाएं, फिर ऊपर-दाएं, फिर नीचे दाएं, आदि .. बाद में मूल क्षेत्र को हटा दें।

सभी छह सेटों को मिलाते हुए ओ (n), सॉर्टिंग O (n.log (n)), सेट अंतर O (n) होना चाहिए। यदि मूल टाइलें किसी क्रमबद्ध प्रारूप में संग्रहीत की जाती हैं, तो मर्ज किए गए सेट को O (n) में भी सॉर्ट किया जा सकता है।

मुझे नहीं लगता कि ओ (एन) से कम के साथ एक एल्गोरिथ्म है, क्योंकि आपको प्रत्येक टाइल को कम से कम एक बार एक्सेस करने की आवश्यकता है।


1

मैंने बस एक ब्लॉग पोस्ट लिखा कि यह कैसे करना है। यह पहली विधि का उपयोग करता है जो @DMGregory का उल्लेख एक बढ़त सेल से शुरू होता है और परिधि के चारों ओर मार्च करता है। यह पायथन के बजाय C # में है लेकिन इसे अनुकूलित करना बहुत आसान होना चाहिए।

https://dillonshook.com/hex-city-borders/


0

मूल पोस्ट:

मैं इस साइट पर टिप्पणी नहीं कर सकता, इसलिए मैं एक pseudocode एल्गोरिथ्म के साथ उत्तर देने का प्रयास करूंगा।

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

EDIT सरल पुनरावृत्ति की तुलना में बहुत अधिक प्रभावी तरीके हैं। जैसा कि मैंने नीचे मेरे (अब हटाए गए) उत्तर में बताने की कोशिश की, आप सबसे अच्छे मामलों में ओ (1) और सबसे खराब स्थिति में ओ (एन) प्राप्त कर सकते हैं।

किसी क्षेत्र O (1) में एक टाइल जोड़ना - O (N):

पड़ोसी न होने की स्थिति में, आप बस एक नया क्षेत्र बनाते हैं।

एक पड़ोसी के मामले में आप मौजूदा क्षेत्र में नई टाइल जोड़ते हैं।

5 या 6 पड़ोसियों के मामले में आप जानते हैं कि यह सब जुड़ा हुआ है, इसलिए आप नए टाइल को मौजूदा क्षेत्र में जोड़ते हैं। ये सभी O (1) ऑपरेशन हैं, और नए बॉर्डर प्रदेशों को अपडेट करना O (1) भी है, क्योंकि यह एक सेट का दूसरे के साथ एक सरल मर्ज है।

2, 3, या 4 पड़ोसी प्रदेशों के मामले में आपको 3 अद्वितीय प्रदेशों में विलय करना पड़ सकता है। यह संयुक्त क्षेत्र आकार पर O (N) है।

किसी क्षेत्र O (1) से एक टाइल निकालना - O (N):

शून्य पड़ोसियों के साथ क्षेत्र को मिटा देते हैं। हे (1)

एक पड़ोसी के साथ क्षेत्र से टाइल हटा दें। हे (1)

दो या अधिक पड़ोसियों के साथ, 3 नए प्रदेश बनाए जा सकते हैं। यह ओ (एन) है।

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

आप यहां ( .7z प्रारूप में) hex.7z खेल डाउनलोड कर सकते हैं

सरल माउस नियंत्रण LMB एक टाइल रखता है (केवल वही स्थान हो सकता है जहां होवर से हाइलाइट किया गया हो)। शीर्ष पर स्कोर, तल पर आय। देखें कि क्या आप एक प्रभावी रणनीति के साथ आ सकते हैं।

कोड यहाँ पाया जा सकता है:

ईगल / EagleTest

सोर्स कोड से निर्माण करने के लिए आपको ईगल और एल्लेग्रो की आवश्यकता होती है। 5. दोनों सेमीके साथ बनाते हैं। हेक्स गेम वर्तमान में सीबी परियोजना के साथ बनाता है।

उन चढ़ावों को उल्टा कर दें। :)


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

यह मूल रूप से एक ही है, लेकिन यदि आप इसे अधिक कुशल होने के बाद केवल एक बार ही घटाते हैं
BugSquasher

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