ใน 2D ฉันจะหาวัตถุที่ใกล้ที่สุดถึงจุดได้อย่างมีประสิทธิภาพได้อย่างไร


35

ฉันมีเอ็นจิ้นเกมขนาดใหญ่และฉันต้องการคุณสมบัติในการค้นหาจุดที่ใกล้ที่สุด

ฉันสามารถใช้ทฤษฎีบทพีทาโกรัสในการค้นหาแต่ละระยะทางและเลือกหนึ่งขั้นต่ำ แต่นั่นต้องใช้การวนซ้ำทั้งหมด

ฉันยังมีระบบการชนกันโดยที่ฉันจะเปลี่ยนวัตถุให้เป็นวัตถุขนาดเล็กบนกริดที่เล็กกว่า (เช่น minimap) และถ้าวัตถุมีอยู่ในกริดสเปซเดียวกันฉันจะตรวจสอบการชนกัน ฉันสามารถทำได้เพียงทำให้ระยะห่างของกริดใหญ่ขึ้นเพื่อตรวจสอบความใกล้ชิด (แทนที่จะตรวจสอบทุก ๆ ออบเจ็กต์) อย่างไรก็ตามมันจะทำการเซ็ตอัพเพิ่มเติมในคลาสฐานของฉัน มันคุ้มหรือไม่

มีบางสิ่งที่มีประสิทธิภาพและแม่นยำฉันสามารถใช้เพื่อตรวจจับวัตถุใดที่อยู่ใกล้ที่สุดโดยอิงจากรายการคะแนนและขนาด


จัดเก็บตำแหน่ง x และ y ที่ยกกำลังสองเพื่อให้คุณสามารถทำทฤษฎีบทพีทาโกรัสได้โดยไม่ต้องทำ sqrt แพง ๆ ในตอนท้าย
Jonathan Connell

3
นี้เรียกว่าการค้นหาเพื่อนบ้านที่ใกล้ที่สุด มีการเขียนบนอินเทอร์เน็ตมากมายเกี่ยวกับมัน วิธีการแก้ปัญหาตามปกติคือการใช้การจัดเรียงของบางพื้นที่แบ่งต้นไม้
BlueRaja - Danny Pflughoeft

คำถามที่เกี่ยวข้องเกี่ยวกับการเพิ่มประสิทธิภาพการตรวจสอบการชนทั่วไป
Anko

คำตอบ:


38

ปัญหาของquad / octreeในการค้นหาเพื่อนบ้านที่ใกล้ที่สุดคือวัตถุที่อยู่ใกล้ที่สุดอาจจะอยู่ตรงข้ามส่วนระหว่างโหนด สำหรับการชนนี่ไม่เป็นไรเพราะถ้ามันไม่อยู่ในโหนดเราไม่สนใจเลย แต่ลองพิจารณาตัวอย่าง 2D นี้ด้วยควอดทรี

ตัวอย่าง Quadtree

ที่นี่แม้ว่ารายการสีดำและรายการสีเขียวอยู่ในโหนดเดียวกัน แต่รายการสีดำอยู่ใกล้กับรายการสีน้ำเงินมากที่สุด คำตอบ ultifinitusสามารถรับประกันได้เพียงเพื่อนบ้านที่ใกล้ที่สุดเท่านั้นทุกรายการในต้นไม้ของคุณจะถูกวางไว้ในโหนดที่เล็กที่สุดที่สามารถมีได้หรือในโหนดที่ไม่ซ้ำกัน - ซึ่งนำไปสู่ควอดทรีที่ไม่มีประสิทธิภาพมากขึ้น (โปรดทราบว่ามีหลายวิธีในการปรับใช้โครงสร้างซึ่งอาจเรียกว่า quad / octree - การใช้งานที่เข้มงวดมากขึ้นอาจทำงานได้ดีขึ้นในแอปพลิเคชันนี้)

ตัวเลือกที่ดีจะเป็นต้นไม้ kd Kd-trees มีอัลกอริธึมการค้นหาเพื่อนบ้านที่ใกล้ที่สุดที่มีประสิทธิภาพมากซึ่งคุณสามารถนำไปใช้ได้และสามารถมีมิติข้อมูลจำนวนเท่าใดก็ได้ (ด้วยเหตุนี้ขนาด "k")

ภาพเคลื่อนไหวที่ยอดเยี่ยมและให้ข้อมูลจาก Wikipedia: การค้นหา kd-tree ใกล้เคียงที่สุด

ปัญหาที่ใหญ่ที่สุดในการใช้ kd-trees ถ้าฉันจำได้อย่างถูกต้องก็คือพวกมันยากที่จะแทรก / ลบรายการออกจากในขณะที่รักษาสมดุล ดังนั้นฉันขอแนะนำให้ใช้ต้นไม้ kd หนึ่งต้นสำหรับวัตถุคงที่เช่นบ้านและต้นไม้ที่มีความสมดุลสูงและอีกอันหนึ่งซึ่งมีผู้เล่นและยานพาหนะซึ่งต้องการความสมดุลอย่างสม่ำเสมอ ค้นหาวัตถุคงที่ใกล้ที่สุดและวัตถุเคลื่อนที่ที่ใกล้ที่สุดและเปรียบเทียบทั้งสอง

สุดท้ายนี้ kd-trees นั้นค่อนข้างง่ายต่อการนำไปใช้และฉันแน่ใจว่าคุณสามารถหาไลบรารี่ C ++ ที่หลากหลายได้ จากสิ่งที่ฉันจำได้ต้นไม้ R มีความซับซ้อนมากขึ้นและอาจ overkill ถ้าสิ่งที่คุณต้องการคือการค้นหาเพื่อนบ้านที่ใกล้ที่สุด


1
คำตอบที่ยอดเยี่ยมรายละเอียดเล็ก ๆ "รับประกันได้เพียงเพื่อนบ้านที่ใกล้ที่สุดเท่านั้นทุกรายการในต้นไม้ของคุณจะถูกวางไว้ในโหนดที่เล็กที่สุดเท่าที่จะเป็นไปได้" ฉันหมายถึงคำตอบของฉันวนซ้ำทุกรายการในโหนดเดียวกันและเพื่อนบ้าน 10.000
รอยต.

1
จริงมาก - ฉันคิดว่า "เท่านั้น" เป็นคำที่ค่อนข้างรุนแรง มีวิธีที่แน่นอนที่จะเกลี้ยกล่อมควอดทรีในการค้นหาเพื่อนบ้านที่ใกล้ที่สุดขึ้นอยู่กับว่าคุณใช้งานอย่างไร แต่ถ้าคุณไม่ได้ใช้มันด้วยเหตุผลอื่นแล้ว (เช่นการตรวจจับการชน) ฉันจะติดกับต้นไม้ kd ที่เหมาะสมที่สุด
dlras2

ฉันต้องการที่จะทราบว่าฉันได้ดำเนินการที่เกี่ยวข้องกับปัญหาสีน้ำเงินสีดำสีเขียว ตรวจสอบด้านล่าง
clankill3r

18

sqrt() เป็นแบบโมโนโทนิกหรือการรักษาคำสั่งซื้อสำหรับอาร์กิวเมนต์ที่ไม่เป็นลบดังนั้น:

sqrt(x) < sqrt(y) iff x < y

และในทางกลับกัน.

ดังนั้นหากคุณต้องการเปรียบเทียบสองระยะทาง แต่ไม่สนใจค่าที่แท้จริงของพวกเขาคุณสามารถตัดsqrt()-step จาก Pythagoras-stuff ของคุณ:

pseudoDistanceB = (A.x - B.x + (A.y - B.y
pseudoDistanceC = (A.x - C.x + (A.y - C.y
if (pseudoDistanceB < pseudoDistanceC)
{
    A is closest to B!
}
else
{
    A is closest to C!
}

มันไม่ได้มีประสิทธิภาพเท่ากับต้นไม้ตุลาคม แต่มันง่ายกว่าที่จะใช้และทำให้ความเร็วเพิ่มขึ้นอย่างน้อยนิดหน่อย


1
ว่าการวัดก็จะเรียกว่าเป็นระยะทางยุคลิดสแควร์
moooeeeep

10

คุณต้องทำการแบ่งพาร์ติชันในกรณีนี้คุณต้องจัดทำโครงสร้างข้อมูลที่มีประสิทธิภาพ ในกรณีนี้แต่ละวัตถุจะอยู่ภายในหนึ่งช่องว่างหรือมากกว่า (ลูกบาศก์) และถ้าคุณรู้ว่าช่องว่างคุณอยู่ที่ไหนคุณสามารถค้นหา O (1) ช่องว่างที่เป็นเพื่อนบ้านของคุณ

ในกรณีนี้วัตถุที่ใกล้ที่สุดสามารถพบได้โดยการวนซ้ำวัตถุทั้งหมดในพื้นที่ของคุณเองเพื่อดูว่าวัตถุใดที่อยู่ใกล้ที่สุด หากไม่มีใครอยู่ที่นั่นคุณสามารถตรวจสอบเพื่อนบ้านคนแรกของคุณได้หากไม่มีใครอยู่ที่นั่นคุณสามารถตรวจสอบเพื่อนบ้านของคุณได้ ฯลฯ

วิธีนี้คุณสามารถค้นหาวัตถุที่ใกล้ที่สุดได้ง่ายโดยไม่ต้องวนซ้ำวัตถุทั้งหมดในโลกของคุณ ตามปกติการเพิ่มความเร็วนี้จะต้องมีการทำบัญชีเล็กน้อย แต่มันมีประโยชน์สำหรับทุกสิ่งดังนั้นถ้าคุณมีโลกที่ยิ่งใหญ่

ตามปกติดูบทความวิกิพีเดีย: http://en.wikipedia.org/wiki/Octree


7
@ultifinitus หากต้องการเพิ่มสิ่งนี้: หากเกมของคุณเป็นแบบ 2D คุณสามารถใช้ QuadTrees แทน Octrees
TravisG

1

อาจลองจัดระเบียบข้อมูลเชิงพื้นที่ของคุณใน RTree ซึ่งเป็นเหมือน btree สำหรับสิ่งต่าง ๆ ในพื้นที่และช่วยให้คำสั่งเช่น "เพื่อนบ้าน N ที่ใกล้ที่สุด" ฯลฯ ... http://en.wikipedia.org/wiki/Rtree


0

นี่คือการใช้งานจาวาของฉันเพื่อรับสิ่งที่ใกล้เคียงที่สุดจาก quadTree มันเกี่ยวข้องกับปัญหา dlras2 อธิบาย:

ป้อนคำอธิบายรูปภาพที่นี่

ฉันคิดว่าการดำเนินการนั้นมีประสิทธิภาพจริงๆ มันขึ้นอยู่กับระยะทางไปยังรูปสี่เหลี่ยมเพื่อหลีกเลี่ยงการค้นหาในล่ามทางไกลแล้วปัจจุบันที่ใกล้ที่สุด

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

public T getClosest(float x, float y) {

    Closest closest = new Closest();
    getClosest(x, y, closest);

    return closest.item;
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

protected void getClosest(float x, float y, Closest closestInfo) {


    if (hasQuads) {

        // we have no starting point yet
        // so get one
        if (closestInfo.item == null) {
            // check all 4 cause there could be a empty one
            for (int i = 0; i < 4; i++) {
                quads[i].getClosest(x, y, closestInfo);
                if (closestInfo.item != null) {
                    // now we have a starting point
                    getClosest(x, y, closestInfo);
                    return;
                }

            }
        }
        else {

            // we have a item set as closest
            // we should check if this quad is
            // closer then the current closest distance
            // let's start with the closest from index

            int closestIndex = getIndex(x, y);

            float d = quads[closestIndex].bounds.distToPointSQ(x, y);

            if (d < closestInfo.dist) {
                quads[closestIndex].getClosest(x, y, closestInfo);
            }

            // check the others
            for (int i = 0; i < 4; i++) {
                if (i == closestIndex) continue;

                d = quads[i].bounds.distToPointSQ(x, y);

                if (d < closestInfo.dist) {
                    quads[i].getClosest(x, y, closestInfo);
                }

            }

        }

    }
    else {

        for (int i = 0; i < items.size(); i++) {

            T item = items.get(i);

            float dist = distSQ(x, y, getXY.x(item), getXY.y(item));

            if (dist < closestInfo.dist) {
                closestInfo.dist = dist;
                closestInfo.item = item;
                closestInfo.tree = this;
            }

        }
    }

}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


class Closest {

    QuadTree<T> tree;
    T item;
    float dist = Float.MAX_VALUE;

}

ป.ล. ฉันยังคิดว่ามันจะดีกว่าถ้าใช้ kd-tree หรืออะไรบางอย่าง แต่นี่อาจช่วยคนได้
clankill3r

ดูที่นี้: bl.ocks.org/llb4ll/8709363
clankill3r
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.