2D에서 포인트까지 가장 가까운 객체를 효율적으로 찾으려면 어떻게해야합니까?


35

나는 상당한 게임 엔진을 가지고 있으며 가장 가까운 포인트 목록을 찾는 기능을 원합니다.

피타고라스 정리 를 사용하여 각 거리를 찾고 최소 거리를 선택할 수는 있지만, 모든 거리를 반복해야합니다.

또한 충돌 시스템이 있습니다. 본질적으로 객체를 작은 그리드 (작은 미니 맵과 같은)에서 작은 객체로 바꾸고 동일한 그리드 공간에 객체가있는 경우에만 충돌을 확인합니다. 나는 그것을 할 수 있었고, 근접성을 확인하기 위해 그리드 간격을 더 크게 만듭니다. (모든 단일 객체를 확인하는 대신) 기본 클래스에서 추가 설정을 수행하고 이미 혼란스러운 객체를 혼란스럽게 만듭니다. 그만한 가치가 있습니까?

점과 크기 목록을 기준으로 가장 가까운 물체를 감지하는 데 사용할 수있는 효율적이고 정확한 것이 있습니까?


마지막에 비싼 sqrt를 수행하지 않고도 피타고라스 정리를 수행 할 수 있도록 x 및 y 위치의 제곱 버전을 저장하십시오.
Jonathan Connell

3
이것을 가장 가까운 이웃 검색 이라고합니다 . 인터넷에 관한 글이 많이 있습니다. 일반적인 해결책은 일종의 공간 분할 트리 를 사용하는 것입니다 .
BlueRaja-대니 Pflughoeft

답변:


38

가장 가까운 이웃 검색에서 쿼드 / 옥트리 의 문제점 은 가장 가까운 객체가 노드 사이의 분할에 바로있을 수 있다는 것입니다. 충돌의 경우 노드에 없으면 신경 쓰지 않기 때문에 괜찮습니다. 그러나 쿼드 트리가있는이 2D 예제를 고려하십시오.

쿼드 트리 예제

여기서 검은 색 항목과 녹색 항목이 같은 노드에 있더라도 검은 색 항목은 파란색 항목에 가장 가깝습니다. ultifinitus의 대답 은 트리의 모든 항목이 포함 할 수있는 가장 작은 노드 또는 고유 한 노드에만 배치되도록 보장합니다. 이로 인해 더 효율적인 쿼드 트리가 생성됩니다. (쿼드 / 옥트리라고하는 구조를 구현하는 방법은 여러 가지가 있습니다.보다 엄격한 구현은이 애플리케이션에서 더 잘 작동 할 수 있습니다.)

더 나은 옵션은 kd-tree 입니다. Kd- 트리에는 구현할 수있는 가장 효율적인 가장 가까운 이웃 검색 알고리즘이 있으며, 여러 차원 ( "k"차원)을 포함 할 수 있습니다.

Wikipedia의 훌륭하고 유익한 애니메이션 : kd- 트리 가장 가까운 이웃 검색

kd-tree를 사용할 때 가장 큰 문제는 올바르게 기억한다면 균형을 유지하면서 항목을 삽입 / 제거하기가 더 어렵다는 것입니다. 따라서 균형이 잘 잡힌 집과 나무와 같은 정적 객체와 규칙적인 균형이 필요한 플레이어와 차량이 포함 된 정적 객체에 하나의 kd-tree를 사용하는 것이 좋습니다. 가장 가까운 정적 객체와 가장 가까운 모바일 객체를 찾아이 둘을 비교합니다.

마지막으로 kd-trees는 구현하기가 비교적 간단하며 많은 C ++ 라이브러리를 찾을 수 있다고 확신합니다. 내가 기억하는 것에서, R-tree는 훨씬 더 복잡하며, 필요한 모든 것이 가장 가까운 이웃 검색이면 과잉입니다.


1
큰 대답, 작은 세부 사항 "당신의 트리의 모든 항목 만 가능한 가장 작은 노드에만 가장 가까운 이웃을 보장합니다."동일하고 인접한 노드의 모든 항목을 반복하는 대답을 의미하므로 대신 10 이상 반복하십시오. 10.000.
Roy T.

1
매우 사실-나는 "단지"가 다소 가혹한 단어라고 생각합니다. 쿼드 트리를 구현 방법에 따라 가장 가까운 이웃 검색에 동축하는 방법은 확실히 있지만, 이미 다른 이유로 (충돌 감지 등) 다른 이유로 사용하지 않는 경우 더 최적화 된 kd-tree를 사용합니다.
dlras2

나는 검은 녹색 파랑 문제를 다루는 구현을 만들었습니다. 바닥을 확인하십시오.
clankill3r

18

sqrt() 음이 아닌 인수의 경우 단조 또는 순서 보존입니다.

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

그 반대.

따라서 두 거리 만 비교하고 실제 값에 관심이 없다면 sqrt()피타고라스 물건에서 단계를 잘라낼 수 있습니다 .

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!
}

oct-tree 일만큼 효율적이지는 않지만 구현하기가 쉽고 속도를 조금 높여줍니다.


1
이 메트릭은 제곱 유클리드 거리 라고도합니다 .
moooeeeep

10

공간 분할을 수행해야합니다.이 경우 효율적인 데이터 구조 (보통 octree)를 만듭니다. 이 경우 각 개체는 하나 이상의 공백 (큐브) 안에 있습니다. 어떤 공간을 알고 있다면 어떤 공간이 이웃인지 O (1)을 찾을 수 있습니다.

이 경우 가장 가까운 객체는 먼저 자신의 공간에있는 모든 객체를 반복하여 가장 가까운 객체를 찾습니다. 아무도 없다면 첫 이웃을 확인할 수 있고, 아무도 없으면 이웃 등을 확인할 수 있습니다.

이렇게하면 세상의 모든 객체를 반복하지 않고도 가장 가까운 객체를 쉽게 찾을 수 있습니다. 평소와 같이이 속도 향상에는 약간의 부기가 필요하지만 모든 종류의 물건에 실제로 유용하므로 큰 세계가있는 경우 공간 분할 및 octree를 구현할 가치가 있습니다.

평소와 같이 wikipedia 기사도 참조하십시오 : http://en.wikipedia.org/wiki/Octree


7
@ultifinitus 이것에 추가하려면 : 게임이 2D 인 경우 Octrees 대신 QuadTrees를 사용할 수 있습니다.
TravisG


0

다음은 quadTree에서 가장 가까운 Java 구현을 얻는 Java 구현입니다. 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.