사용자 도구

사이트 도구


ps:이론:원

삼각형의 외접원 (Circumscribed circle)

  • Circumscribed circle
  • 삼각형의 세 꼭짓점을 지나는 원은 유일하게 정의된다.
  • 이 원을 찾는 것은, 원의 중심에서 세 꼭짓점까지의 거리가 모두 동일하다는 것을 가지고 연립방정식을 만들어서 풀면 된다.
  • 식 정리가 복잡하긴 한데, 정리된 결과는 위키 링크를 참고하자.
  • 이 식을 그대로 구현한 코드는 다음과 같다
  • def circumcircle_of_triangle(p, q, r):
        px, py = p
        bx, by = q[0] - px, q[1] - py
        cx, cy = r[0] - px, r[1] - py
        B = bx * bx + by * by
        C = cx * cx + cy * cy
        D = 2 * (bx * cy - by * cx)
        x, y = (cy * B - by * C) / D, (bx * C - cx * B) / D
        return px + x, py + y, math.hypot(x, y)

최소 외접원 (Smallest Enclosing Circle)

- 난이도: 플래티넘 1
- 관련 솔브닥 태그: 최소 외접원
- 기본 문제: 13708
- 필요한 사전 지식: 삼각형의 외접원

  • 참고
  • 보통 최소 외접원이라고 번역하는데, 이게 정확한 번역인지는 의문이다. 외접원이면 모든 점과 접하는 원이어야 할텐데, 여기서 구하려는 것은 모든 점을 포함하는 원이기 때문이다. '모든 점을 포함하는 가장 작은 원'이 정확한 번역이긴 한데, 너무 길다보니 그냥 최소 외접원이라고 부르는것 같다. 마음에 드는 것은 아니지만 대안이 없으니 여기서도 그냥 그렇게 부르겠다.
  • 위에서 설명한대로, 2차원 평면상에 주어진 N개의 점을 모두 포함하는 가장 작은 원을 찾는 문제이다
  • 가장 먼 점과의 거리가 최소화되는 위치를 찾는 minimax problem 도 동일한 문제이다. 최소 외접원의 중심이 그 위치에 해당된다.

알고리즘

  • 간단한 방법은 Gradient descent방식으로, 적당한 후보에서 출발해서 수렴할때까지 이터레이션을 도는 방식이다. 오차함수가 컨벡스하기 때문에, 글로벌 최적해를 찾을 수 있다.
  • 좀더 PS적인 방식들은 위키 링크에 언급이 되어있다.
  • Megiddo's algorithm 은 deterministic 하게 O(n)의 시간 복잡도를 갖는 알고리즘이다
    • 하지만 그냥 위키에서 요약한 설명만 봐도 매우 지저분해보인다. 이터레이션을 돌때마다 후보 점의 갯수을 15/16 배로 줄여나간다고 한다;
    • 자세한 설명은 https://greimul.tistory.com/32 에 있다.
  • Welzl's algorithm 이 흔히 쓰이는 알고리즘이다. 랜덤에 기반한 알고리즘이긴 하지만, 평균적으로 O(n)의 성능에 돌아간다.

구현

  • 설명되어있는 대로 구현하면 되긴 하는데. 좀 헷갈릴 여지가 있기는 하니 주의..
  • 대강 이런 식이 된다
  • def _is_in_circle(circle, p):
        *center, radius = circle
        dist = math.dist(center, p)
        return dist <= radius * MULTIPLICATIVE_EPS
    
    
    def _circle_from_pq(points, p, q):
        circle = ((p[0] + q[0]) * 0.5, (p[1] + q[1]) * 0.5, math.dist(p, q) * 0.5)
        cw_circle, ccw_circle = None, None
        ccw_area, cw_area = -INF, INF
        for r in points:
            if _is_in_circle(circle, r):
                continue
            ori = orientation(p, q, r)
            if ori == Orientation.COLLINEAR:
                continue
            *center, radi = circle = circumcircle_of_triangle(p, q, r)
            area = signed_parallelogram_area(p, q, center)
            if ori == Orientation.CCW and area > ccw_area:
                ccw_area, ccw_circle = area, circle
            elif ori == Orientation.CW and area < cw_area:
                cw_area, cw_circle = area, circle
    
        if cw_circle is None and ccw_circle is None:
            return circle
        elif cw_circle is None:
            return ccw_circle
        elif ccw_circle is None:
            return cw_circle
        else:
            return cw_circle if cw_circle[2] < ccw_circle[2] else ccw_circle
    
    
    def smallest_enclosing_circle(orig_points: Iterable[PointF]) -> CircleF:
        points = list(orig_points)
        if len(points) == 1:
            return (*points[0], 0)
        if len(points) == 2:
            p, q = points
            return (p[0] + q[0]) * 0.5, (p[1] + q[1]) * 0.5, math.dist(p, q) * 0.5
    
        random.shuffle(points)
    
        circle = (0, 0, 0)
        for i, p in enumerate(points):
            if not _is_in_circle(circle, p):
                circle = (0, 0, 0)
                for j, q in enumerate(points[:i]):
                    if not _is_in_circle(circle, q):
                        circle = _circle_from_pq(points[:j], p, q)
        return circle
    • 코드를 실행시키면, 실수값을 계속 사용하게 된다.
  • 실제 teflib에 구현된 코드는 위의 코드를 살짝 더 최적화한 버전이다

관련 문제

  • 아래 세 문제는 주어지는 점의 갯수만 다를뿐 다 기본적인 최소외접원 문제이다

최대 내접원

토론

댓글을 입력하세요:
N​ X E K F
 
ps/이론/원.txt · 마지막으로 수정됨: 2023/04/28 16:02 저자 teferi