2

곡선에 100000 포인트가 있다고 가정하십시오. y = x^2. 이 점들의 볼록한 선체를 찾고 싶습니다. 모든 좌표는 부동 소수점 숫자입니다.부동 정밀도 문제가있을 때 어떻게 convex hull을 실제로 풀 수 있습니까?

내 그레이엄 스캔 구현에서 내가 부동 소수점에서 작동하는 유일한 위치는 처음에 모든 점을 좌표로 정렬 한 다음 세 점이 왼쪽 또는 오른쪽으로 회전하는지 여부를 결정하는 한 가지 기능이 있습니다.

포인트 :

struct point { 
    double x; 
    double y; 
}; 

정렬 비교 :

inline bool operator() (const point &p1, const point &p2) { 
    return (p1.x < p2.x) || (p1.x == p2.x && p1.y > p2.y); 
} 

왼쪽/오른쪽 회전 :

inline int ccw(point *p1, point *p2, point *p3) { 
    double left = (p1->x - p3->x)*(p2->y - p3->y); 
    double right = (p1->y - p3->y)*(p2->x - p3->x); 
    double res = left - right; 
    return res > 0; 
} 

내 프로그램 (100 개) 000 점에서 단지 68,894의 일부임을 말한다 볼록한 선체. 그러나 그들이 곡선에 있기 때문에, 그것들 모두는 볼록한 선체의 일부가되어야합니다.

눈에 그것은 별 차이가 없습니다. 아래 그림을 참조하십시오. 빨간색 점은 볼록 선체의 일부입니다.

image http://oi57.tinypic.com/t8lsvs.jpg

하지만 당신은 충분히 가까이보고, 지점을 확대, 당신은 그들 중 일부는 파란색, 그래서 그들은이 볼록 선체에 포함되지 않은 것을 확인할 수 있습니다.

image http://oi61.tinypic.com/2eol37a.jpg

이제 내 초기 가정은 부동 소수점 오류가이 문제를 일으키는된다.

나는 부동 소수점 숫자에 대해 임의의 정밀도를 갖는 외부 라이브러리를 사용할 수 있다고 생각하지만, C++에서 예로 든 간단한 데이터 형식에 더 관심이 있습니다.

어떻게 정확도를 높일 수 있습니까? 나는 엡실론에 대해 읽었지만 어떻게 엡실론을 사용하면 도움이 될까요? 나는 여전히 서로 가깝다는 점을 같기 때문에 100 %에 가까운 정확도를 얻지는 못할 것입니다.

이 문제를 해결하는 가장 좋은 방법은 무엇입니까?

+0

'long double'을 사용해 보셨습니까? – mch

+0

볼록 선체 알고리즘이 올바르지 만 처음에 y = x^2를 평가할 때 반올림이 발생합니까? – ajclinto

+1

먼저, 유한 정밀도는 실수를 나타낼 수 없습니다. 두 번째로, 플롯하는 점은 근사값입니다. 셋째, 진정한 건설적 재판 (무한 정밀)은 당신이 그들을 비교하기 원하는 방식으로 비교 될 수 없습니다. 다섯째, 왜 신경 쓰시겠습니까? (따라서 중요한 문제는 선체에 어떤 목적이 있습니까?) – Yakk

답변

0

사실 (x, x^2)의 점수를 사용하는 경우 모든 점이 볼록 선상에 있어야한다는 것이 맞습니다. 그러나 3 점은 동일 선상에있을 수 있습니다. 당신이 그들을 이동 시키거나 다른 이상한 일을한다면, 이것은 창 밖으로 나옵니다.

100000 포인트를 선택하면 [-50000,49999]의 정수를 사용하는 것이 좋습니다. ccw 함수는 leftright이 2.5e14 < 2^53보다 절대 값이 더 작은 정수로 계산되므로 반올림은 수행되지 않습니다.

입력에 관계없이 좌표 기반 정렬이 올바르게 작동합니다.

inline int ccw(point *p1, point *p2, point *p3) { 
    double left = (p1->x - p3->x)*(p2->y - p3->y); 
    double right = (p1->y - p3->y)*(p2->x - p3->x); 
    double res = left - right; 
    return res > 0; 
} 

은 뺄셈과 곱셈에 모두 반올림있을 수 있습니다 : 일반 입력의

다음 ccw 술어는 버그입니다. 모든 점이 H * W 경계 상자에있는 경우 x 좌표의 차이는 H * eps/2 주변의 절대 오차로 계산되며 y 좌표의 차이는 W * eps/2. 따라서 제품은 H * W * eps/2 주변의 절대 오차로 계산됩니다. fabs(left - right) < 3*H*W*eps/2 인 경우 leftright을 더 정확하게 평가해야합니다. 여기서 eps은 2 -52입니다.

double 비교에서 아무 것도 말해주지 않으면 MPFR을 사용하는 것이 좋습니다. 그러나 당신은 그것없이 할 수 있습니다. Kahan 합계의 트릭은 차이점에서 낮은 비트를 얻을 수 있으며 2 +1 트릭을 사용하면 제품을 정확하게 계산하는 데 도움이 될 수 있습니다.

0

매우 자주 부동 소수점 연산을 사용하면 "허용 오차"개념을 도입해야 할 때가 있습니다. 때로는 엡실론이라고도합니다. 귀하의 경우 ccw() 기능을 true/false/indeterminate의 세 가지 값으로 만들 수 있습니다. 그런 다음 새로운 점이 볼록 선체의 일부가 될 수 있는지 알아 내려고 할 때 "ccw = true 또는 indeterminate입니까?"라고 물어 보면 그 점을 받아들입니다. 불확정 한 결과는 경사가 결정될 직선에 너무 가까울 때 발생합니다.