मेरा एल्गोरिथ्म जो सबसे बड़े बॉक्स को निकालता है जो छोटे बक्से से बनाया जा सकता है, बहुत धीमा है


9

एक क्यूब आधारित दुनिया (जैसे कि Minecraft, Trove, या Cube World) की कल्पना करें जहां सब कुछ पहचान के आकार के क्यूब्स से बना है और सभी क्यूब्स एक ही तरह के हैं

लक्ष्य दुनिया को आयताकार बक्से की सबसे कम संख्या के साथ प्रतिनिधित्व करना है (क्यूब्स को विलय करके लेकिन उत्तल आकार (उर्फ, एक आयताकार बॉक्स आकार) को बनाए रखना)। मेरा एल्गोरिथ्म इस पर सफल होता है, लेकिन इसका प्रदर्शन अपने इच्छित उद्देश्य के लिए बहुत धीमा है। क्या तेज एल्गोरिदम हैं?

मेरे एल्गोरिथ्म के लिए छद्म-सी # -कोड मूल रूप से है:

struct Coordinate { int x,y,z; }; //<-- integer based grid
HashSet<Coordinate> world; // <-- contains all the cubes

//width, height, and length represent how many cubes it spans
struct RectangularBox { Coordinate coord; int width,height,length; }

void Begin()
{
    List<RectangularBox> fewestBoxes = new List<RectangularBox>();
    while(world.Count > 0)
    {
         RectangularBox currentLargest = ExtractLargest();
         fewestBoxes.Add(currentLargest);
         world.RemoveRange(currentLargest.ContainedCubes());
    }
    //done; `fewestBoxes` contains the fewest rectangular boxes needed.
}

private RectangularBox ExtractLargest()
{
    RectangularBox largestBox = new RectangularBox();
    foreach (Coordinate origin in world)
    {
        RectangularBox box = FindMaximumSpan(origin);
        if (box.CalculateVolume() >= largestBox.CalculateVolume())
            largestBox = box;
    }
    return largestBox;
}

private RectangularBox FindMaximumSpan(Coordinate origin)
{
    int maxX, maxY,maxZ;
    while (world.Contains(origin.Offset(maxX, 0, 0))) maxX++;
    while (world.Contains(origin.Offset(0, maxY, 0))) maxY++;
    while (world.Contains(origin.Offset(0, 0, maxZ))) maxZ++;

    RectangularBox largestBox;
    for (int extentX = 0; extentX <= maxX; extentX++)
        for (int extentY = 0; extentY <= maxY; extentY++)
            for (int extentZ = 0; extentZ <= maxZ; extentZ++)
            {
                int lengthX = extentX + 1;
                int lengthY = extentY + 1;
                int lengthZ = extentZ + 1;
                if (BoxIsFilledWithCubes(origin, lengthX, lengthY, lengthZ))
                {
                    int totalVolume = lengthX * lengthY * lengthZ;
                    if (totalVolume >= largestBox.ComputeVolume())
                        largestBox = new RectangularBox(origin, lengthX, lengthY, lengthZ);
                }
                else
                    break;
            }
    return largestBox;
}

private bool BoxIsFilledWithCubes(Coordinate coord, 
    int lengthX, int lengthY, int lengthZ)
{
    for (int gX = 0; gX < lengthX; gX++)
        for (int gY = 0; gY < lengthY; gY++)
            for (int gZ = 0; gZ < lengthZ; gZ++)
                if (!world.Contains(coord.Offset(gX, gY, gZ)))
                    return false;
    return true;
}

अनिवार्य रूप से, दुनिया के हर ब्लॉक के लिए, यह पहली बार पता चलता है कि तीन सकारात्मक आयामों (+ X, + Y, + Z) में से प्रत्येक में कितने सन्निहित ब्लॉक हैं। और फिर यह थोड़े से बाढ़ को भरता है जो कि मात्रा और जांच करता है जो सबसे बड़ा भरने वाला है जो किसी भी ब्लॉक को याद नहीं कर रहा है।


अद्यतन: जब से मुझे लगा कि यह एक गेम रेंडरिंग इंजन के लिए है, मैं सिर्फ स्पष्ट करना चाहता हूं, यह गेम रेंडरिंग इंजन के लिए नहीं है; यह एक फ़ाइल-कनवर्टर के लिए है; जीयूआई नहीं।


2
शायद codereview.stackexchange.com के लिए अधिक उपयुक्त है
Rotem

4
@ रॉटम शायद, लेकिन मैं वास्तव में अपने कोड की समीक्षा के बजाय वैकल्पिक एल्गोरिदम की तलाश कर रहा हूं। मैंने अपना कोड केवल आदत के बल के रूप में प्रदान किया है।
मि। स्मिथ

ज़रूर, समझ में आता है।
रोटेम

एल्गोरिथ्म प्रश्न कंप्यूटर विज्ञान जैसी एसई साइटों के लिए अधिक अनुकूल हैं ...
बकुरीउल

यह इस बात पर भी निर्भर करता है कि आप कितनी बार विधि कहते हैं। यदि आप इसे हर फ्रेम कहते हैं या केवल जब कोई ब्लॉक बदलता है। इन खेलों में आम तौर पर हिस्सा होता है (विशिष्ट आकार का आयत पूर्व: 64x64x32 ब्लॉक), जितना संभव हो उतना कैश मान और केवल चंक की गणना करें। और केवल दिखाई देने वाले ब्लॉकों पर इन मूल्यों की गणना करें।
the_lotus

जवाबों:


6

आप इस तथ्य का उपयोग कर सकते हैं कि कब

 BoxIsFilledWithCubes(c,x,y,z)

सच लौटाता है, तो BoxIsFilledWithCubes(c,x+1,y,z)उन सभी क्यूब्स को समन्वय सीमा में "(सी, एक्स, वाई, जेड)" फिर से जांचना आवश्यक नहीं है । आपको केवल नए एक्स-समन्वय के साथ उन क्यूब्स की जांच करने की आवश्यकता है c.x + (x+1)। (उसी के लिए सच है y+1, या z+1)। आम तौर पर, एक बॉक्स को दो छोटे बक्से में विभाजित करके (जिसके लिए आपको पहले से ही पता चल सकता है कि वे दोनों क्यूब्स से भरे हुए हैं, या दोनों भरे हुए नहीं हैं), आप यहां एक डिवाइड-एंड-कॉनक्रेक्ट तकनीक को लागू कर सकते हैं, जो आपके से तेज हो जाता है मूल दृष्टिकोण जब आप मध्यवर्ती परिणाम कैश करते हैं।

इसे पूरा करने के लिए, आप BoxIsFilledWithCubes(c,x,y,z)पुनरावर्ती रूप से लागू करना शुरू करते हैं , उदाहरण के लिए:

 bool BoxIsFilledWithCubes(coord,lx,ly,lz)
 {
     if(lx==0|| ly==0 || lz==0)
        return true;
     if(lx==1 && ly==1 && lz==1)
          return world.Contains(coord);
     if(lx>=ly && lx>=lz)  // if lx is the maximum of lx,ly,lz ....
         return BoxIsFilledWithCubes(coord,lx/2,ly,lz) 
             && BoxIsFilledWithCubes(coord.Offset(lx/2,0,0), lx-lx/2, ly, lz);
     else if(ly>=lz && ly>=lx)  
         // ... analogously when ly or lz is the maximum

 }

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


5

एक बनाएँ octree अपने बॉक्स के आकार की एक पत्ती नोड aabb आकार के साथ। ऑक्ट्री को पार करते समय आप सस्ते में नोड्स मर्ज कर सकते हैं। पूर्ण रूप से भरे हुए नोड्स मर्ज करने के लिए तुच्छ हैं (नए बॉक्स = पैरेंट एबब), जबकि आंशिक रूप से भरे नोड्स के लिए, आप मर्ज-क्षमता की जांच करने के लिए अपने वर्तमान एल्गोरिथ्म के एक संस्करण का उपयोग कर सकते हैं।


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

पूर्ण नोड्स को इस अर्थ में मिलाया जा सकता है कि सभी 8 बच्चे एक ही बड़े बॉक्स का हिस्सा बन सकते हैं। इस बड़े बॉक्स को बाद में विभाजित नहीं करना पड़ेगा, लेकिन यह विलय हो सकता है। पदानुक्रम आपको बहुत सारे बक्से को जल्दी से मर्ज करने की अनुमति देता है, पूरी तरह से भरा नोड्स आपको आगे के परीक्षण के बिना एक स्तर ऊपर कूदने की अनुमति देता है।
विल्बर्ट

1

आपको लगता है कि कम से कम O (n ^ 2) ( बड़े O संकेतन देखें ) जैसा कि आप "शुरुआत ()" में दुनिया के सभी बॉक्स पर लूप करते हैं, फिर प्रत्येक बॉक्स के लिए, आप ExtractLargest () में दुनिया के सभी बॉक्स पर लूप करते हैं )। तो 10 असंबंधित बक्से वाली दुनिया 5 असंबंधित बक्से वाले दुनिया की तुलना में 4 गुना अधिक समय लेगी।

इसलिए आपको उन बॉक्सों की संख्या को सीमित करने की आवश्यकता है जिन्हें ExtractLargest () को देखना है, ऐसा करने के लिए आपको कुछ प्रकार की स्थानिक खोज का उपयोग करने की आवश्यकता है , जैसा कि आप 3 डी में काम कर रहे हैं, आपको 3 डी स्थानिक खोज की आवश्यकता हो सकती है। हालाँकि पहले 2d स्थानिक खोज को समझकर शुरू करें।

फिर विचार करें कि क्या आपके पास कभी भी एक दूसरे के शीर्ष पर कई बक्से होंगे, यदि नहीं तो एक 2 डी स्थानिक खोज जो सिर्फ एक्स को कवर करती है, वाई आपके लूपिंग को काटने के लिए पर्याप्त हो सकती है।

Octree / quadtree एक विकल्प है, लेकिन अंतरिक्ष विभाजन के लिए कई अन्य विकल्प हैं ,

लेकिन आप केवल 2 आयामी सरणी सूची ( ग्रिड स्थानिक सूचकांक ) का उपयोग करने में सक्षम हो सकते हैं , जहां बिंदु (ए, बी) को कवर करने वाले सभी बक्से सरणी [ए, बी] में हैं। लेकिन सबसे अधिक यह कि बहुत बड़ी सरणी के लिए नेतृत्व करेंगे, तो सरणी [mod (a, 100), mob (b, 100)] के बारे में क्या है। यह सब इस बात पर निर्भर करता है कि आपका डेटा कैसा है

(मैंने वास्तविक जीवन में ग्रिड समाधान को बहुत अच्छे से देखा है।)

या विल्बर्ट अपने बॉक्स के आकार के एक पत्ती नोड एएबीबी आकार के साथ एक ओक्ट्री के साथ क्या कहता है, लेकिन बाद में आपको उस बॉक्स को खोजने की संभावना है जो उपयोगकर्ता के माउस को इंगित कर रहा है, एक बार फिर स्थानिक खोज का मामला।

( आपको यह तय करना चाहिए कि क्या आप इस सॉफ़्टवेयर को काम करने के लिए बस कोशिश कर रहे हैं, या यदि आप यह समझने की कोशिश कर रहे हैं कि एक बेहतर प्रोग्रामर कैसे होना चाहिए और इसलिए सीखने में अधिक रुचि है तो एक त्वरित समाधान। )

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