나는 최근에 4 개의 원 (중간 점과 반지름)이 있고이 원들의 결합 면적을 계산해야하는 문제를 발견했습니다.
예시 이미지 :
두 개의 원의 경우 매우 쉽습니다.
삼각형 안에 있지 않은 각 원 영역의 비율을 계산 한 다음 삼각형의 영역을 계산할 수 있습니다.
그러나 두 개 이상의 원이있을 때 사용할 수있는 영리한 알고리즘이 있습니까?
나는 최근에 4 개의 원 (중간 점과 반지름)이 있고이 원들의 결합 면적을 계산해야하는 문제를 발견했습니다.
예시 이미지 :
두 개의 원의 경우 매우 쉽습니다.
삼각형 안에 있지 않은 각 원 영역의 비율을 계산 한 다음 삼각형의 영역을 계산할 수 있습니다.
그러나 두 개 이상의 원이있을 때 사용할 수있는 영리한 알고리즘이 있습니까?
답변:
바깥 둘레의 모든 원 교차점을 찾으십시오 (예 : 다음 다이어그램의 B, D, F, H). 해당 원의 중심과 함께 연결하여 다각형을 형성하십시오. 원의 결합 영역은 다각형 영역 + 연속 교차점과 그 사이의 원 중심으로 정의 된 원 슬라이스 영역입니다. 또한 모든 구멍을 고려해야합니다.
나는 영리한 알고리즘이 있다고 확신하지만, 그것을 찾아야하는 것을 저장하는 멍청한 알고리즘이있다.
물론 멍청하지만 :
Ants Aasma의 답변은 기본 아이디어를 제공했지만 좀 더 구체적으로 만들고 싶었습니다. 아래에있는 5 개의 원과 분해 된 방식을 살펴보세요.
이 세 가지 유형의 점을 식별하는 것은 쉽습니다. 이제 노드가 파란색 점이고 내부가 흰색 인 빨간색 점인 그래프 데이터 구조를 구성하십시오. 모든 원에 대해 경계의 원 가운데 (파란색 점)와 교차점 (내부 흰색이 흰색 인 빨간색 점) 사이에 가장자리를 배치합니다.
이렇게하면 원 결합이 쌍으로 분리되고 원래 결합 (즉, 파티션)을 덮는 다각형 (파란색 음영)과 원형 원형 조각 (녹색 음영) 집합으로 분해됩니다. 여기의 각 조각은 면적을 계산하기 쉬운 것이기 때문에 조각의 면적을 합산하여 합집합 면적을 계산할 수 있습니다.
이전 솔루션과 다른 솔루션의 경우 쿼드 트리를 사용하여 임의 정밀도로 추정을 생성 할 수 있습니다.
사각형이 내부 또는 외부에 있는지 또는 모양과 교차하는지 알 수있는 경우 모든 모양 결합에도 적용됩니다.
각 셀에는 비어 있음, 전체, 부분 상태 중 하나가 있습니다.
알고리즘은 낮은 해상도 (예를 들어 비어있는 것으로 표시된 4 개의 셀)로 시작하는 쿼드 트리의 원을 "그리는"것으로 구성됩니다. 각 셀은 다음 중 하나입니다.
완료되면 면적 추정치를 계산할 수 있습니다. 전체 셀은 하한, 빈 셀은 상한, 부분 셀은 최대 면적 오류를 제공합니다.
오류가 너무 크면 정확한 정밀도를 얻을 때까지 부분 셀을 다듬습니다.
많은 특수한 경우를 처리해야하는 기하학적 방법보다 구현하기가 더 쉬울 것이라고 생각합니다.
나는 두 개의 교차하는 원의 경우에 대한 접근 방식을 좋아합니다. 여기 더 복잡한 예제에 대해 동일한 접근 방식의 약간의 변형을 사용하는 방법이 있습니다.
더 많은 수의 반 겹치는 원에 대한 알고리즘을 일반화하는 데 더 나은 통찰력을 줄 수 있습니다.
여기서 차이점은 중심을 연결하는 것으로 시작한다는 것입니다 (원이 교차하는 곳이 아닌 원의 중심 사이에 정점이 있음). 이것이 더 잘 일반화되도록 해준다고 생각합니다.
(실제로 monte-carlo 방법이 가치가있을 수 있습니다)
(출처 : secretGeek.net )
연속적인 답이 아닌 불연속적인 답을 원한다면 픽셀 페인팅 알고리즘과 비슷한 것을 할 수 있습니다.
그리드에 원을 그린 다음 대부분이 원 안에 포함 된 경우 그리드의 각 셀에 색상을 지정합니다 (즉, 영역의 50 % 이상이 원 중 하나 안에 있음). 전체 그리드 (그리드가 원으로 덮인 모든 영역에 걸쳐 있음)에 대해이 작업을 수행 한 다음 그리드에서 색상이 지정된 셀 수를 계산합니다.
흠, 매우 흥미로운 문제입니다. 내 접근 방식은 아마도 다음과 같은 것입니다.
(이것은 원형이든 아니든 모든 모양에 해당됩니다)
area(A∪B) = area(A) + area(B) - area(A∩B)
Where A ∪ B
는 A 유니온 B를 A ∩ B
의미하고 A는 B와 교차 함을 의미합니다 (첫 번째 단계에서이 문제를 해결할 수 있습니다.
(위에서 A
로 대체 된 것과 동일 A∪B
)
area((A∪B)∪C) = area(A∪B) + area(C) - area((A∪B)∩C)
어디 area(A∪B)
우리는 그냥 일을하고 area((A∪B)∩C)
찾을 수 있습니다 :
area((A∪B)nC) = area((A∩C)∪(B∩C)) = area(A∩C) + area(A∩B) - area((A∩C)∩(B∩C)) = area(A∩C) + area(A∩B) - area(A∩B∩C)
위에서 다시 지역 (A∩B∩C)을 찾을 수 있습니다.
까다로운 부분은 마지막 단계입니다. 더 많은 원이 추가 될수록 더 복잡해집니다. 유한 합집합을 사용하여 교차 영역을 작업하는 데 확장이 있다고 믿습니다. 그렇지 않으면 재귀 적으로 작업 할 수 있습니다.
또한 Monte-Carlo를 사용하여 반복 영역을 근사화하는 것과 관련하여 임의의 수의 원의 교차점을 정확히 계산할 수있는 4 개의 원의 교차점으로 줄일 수 있다고 생각합니다 (이 작업을 수행하는 방법을 모릅니다 하나).
이 작업을 수행하는 더 좋은 방법이있을 것입니다. 추가 된 각 추가 원에 대해 복잡성이 상당히 증가합니다 (아마도 기하 급수적으로 증가하지만 확실하지 않습니다).
나는 더 큰 밝은 별이 희미한 별을 가릴 수있는 조밀 한 필드의 실제 디스크 영역에서 실제 별 수를 추정하려고 시도하면서 겹치는 별 필드를 시뮬레이션하는 문제를 연구 해 왔습니다. 저도 엄격한 공식 분석을 통해이를 수행 할 수 있기를 바랐지만 작업에 대한 알고리즘을 찾을 수 없었습니다. 파란색 배경에 별 필드를 녹색 디스크로 생성하여 지름이 확률 알고리즘에 의해 결정되었습니다. 간단한 루틴을 통해 겹치는 부분이 있는지 확인하기 위해 페어링 할 수 있습니다 (별표 쌍이 노란색으로 바뀜). 그런 다음 색상의 픽셀 수는 이론적 영역과 비교할 관찰 영역을 생성합니다. 그러면 실제 개수에 대한 확률 곡선이 생성됩니다. 무차별 대입 일 수도 있지만 괜찮은 것 같습니다. (출처 : 2from.com )
다음은 실제로 구현하기 쉽고 임의의 작은 오류를 생성하도록 조정할 수있는 알고리즘입니다.
2 단계와 3 단계는 계산 기하학에서 찾기 쉬운 표준 알고리즘을 사용하여 수행 할 수 있습니다.
분명히 각 근사 다각형에 더 많은면을 사용할수록 정확한 답에 더 가깝습니다. 내접 다각형과 외접 다각형을 사용하여 정확한 답에 대한 경계를 얻을 수 있습니다.
전력 다이어그램이라고하는 것을 사용하여이 문제에 대한 효율적인 솔루션이 있습니다. 이것은 정말 무거운 수학이며 내가 직접 다루고 싶지 않은 것이 아닙니다. "쉬운"솔루션을 위해 라인 스윕 알고리즘을 찾아보십시오. 여기서 기본 원칙은 그림을 스트립으로 나누는 것입니다. 여기서 각 스트립의 면적을 계산하는 것은 비교적 쉽습니다.
따라서 문지르지 않은 모든 원을 포함하는 그림에서 원의 상단, 원의 하단 또는 두 원의 교차점 인 각 위치에 수평선을 그립니다. 이 스트립 내부에서 계산해야하는 모든 영역은 동일하게 보입니다. 두면이 원형 세그먼트로 대체 된 "사다리꼴"입니다. 따라서 이러한 모양을 계산하는 방법을 알아낼 수 있다면 모든 개별 모양에 대해 수행하고 함께 추가하면됩니다. 이 순진한 접근 방식의 복잡성은 O (N ^ 3)이며, 여기서 N은 그림에서 원의 수입니다. 데이터 구조를 영리하게 사용하면이 라인 스윕 방법을 O (N ^ 2 * log (N))로 개선 할 수 있지만 실제로 필요한 경우가 아니면 문제가되지 않을 것입니다.
유용한 링크를 찾았습니다. 그러나 확실한 답은없는 것 같습니다. Google이 대답 합니다. 세 개의 원에 대한 또 다른 참조는 Haruki의 정리 입니다. 거기에 종이도 있습니다.
해결하려는 문제에 따라 상한과 하한을 얻는 데 충분할 수 있습니다. 상한은 간단합니다. 모든 원의 합입니다. 하한의 경우 원이 겹치지 않도록 단일 반경을 선택할 수 있습니다. 겹치지 않도록 각 원에 대해 가장 큰 반경 (실제 반경까지)을 찾는 것이 좋습니다. 완전히 겹친 원을 제거하는 것도 매우 간단해야합니다 (이러한 모든 원은 | P_a-P_b | <= r_a를 충족 함). 여기서 P_a는 원 A의 중심, P_b는 원 B의 중심, r_a는 A의 반경입니다. ) 그리고 이것은 상한과 하한 모두를 향상시킵니다. 모든 원의 합계 대신 임의의 쌍에 쌍 공식을 사용하면 더 나은 상한을 얻을 수도 있습니다. "최고"를 선택하는 좋은 방법이있을 수 있습니다.
상한과 하한이 주어지면 Monte-carlo 접근 방식을 더 잘 조정할 수 있지만 구체적인 것은 생각 나지 않습니다. 또 다른 옵션 (응용 프로그램에 따라 다름)은 원을 래스터 화하고 픽셀을 계산하는 것입니다. 기본적으로 고정 분포를 사용하는 Monte-carlo 접근 방식입니다.
이것은 Green 's Theorem을 사용하여 n ^ 2log (n)의 복잡성을 사용하여 풀 수 있습니다 . Green 's Theorem에 익숙하지 않고 더 알고 싶다면 여기 칸 아카데미 의 비디오 와 메모가 있습니다. 그러나 우리 문제를 위해 내 설명으로 충분할 것이라고 생각합니다.
이미지를 게시 할 수 없어서 사진 링크에 대해 죄송합니다. (평판 포인트 부족)
나는 넣으면 L 과 M은 그와 같은
RHS는 단순히 영역 R 의 영역이며 닫힌 적분 또는 LHS를 풀면 얻을 수 있습니다. 이것이 바로 우리가하려는 일입니다.
모든 결합은 서로 교차하는 원의 분리 된 집합으로 나눌 수 있습니다.
따라서 반 시계 방향으로 경로를 따라 적분하면 영역 의 면적 이 제공되고 시계 방향으로 적분하면 면적의 음수가 됩니다. 그래서
AreaOfUnion = (시계 반대 방향으로 빨간색 호를 따라 적분 + 시계 방향으로 파란색 호를 따라 적분)
그러나 멋진 트릭은 각 원에 대해 다른 원 안에없는 호를 통합하면 필요한 영역을 얻습니다. 즉, 모든 빨간색 호를 따라 시계 반대 방향으로 통합하고 시계 방향을 따라 모든 파란색 호를 따라 통합합니다. 작업 완료 !!!
원이 다른 원과 교차하지 않는 경우도 처리됩니다.
다음은 내 C ++ 코드에 대한 GitHub 링크입니다.
픽셀 페인팅 방식 (@Loadmaster에서 제안한대로)은 다양한 방식에서 수학적 솔루션보다 우수합니다.
픽셀 페인팅의 한 가지 단점은 솔루션의 유한 한 정확도입니다. 그러나 상황에 따라 더 크거나 더 작은 캔버스로 간단히 렌더링하여 조정할 수 있습니다. 참고도, 그 안티 앨리어싱 코드를 렌더링 2D에서 (보통 기본적으로 켜져)는보다 좋은 픽셀 수준 얻을 것 정도. 예를 들어, 100x100 그림을 같은 크기의 캔버스로 렌더링하면 1 / (100 x 100 x 255) = .000039 % 정도의 정확도를 얻을 수 있습니다. 가장 까다로운 문제를 제외한 모든 문제를 해결합니다.
<p>Area computation of arbitrary figures as done thru pixel-painting, in which a complex shape is drawn into an HTML5 canvas and the area determined by comparing the number of white pixels found in the resulting bitmap. See javascript source for details.</p>
<canvas id="canvas" width="80" height="100"></canvas>
<p>Area = <span id="result"></span></p>
// Get HTML canvas element (and context) to draw into
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// Lil' circle drawing utility
function circle(x,y,r) {
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2);
ctx.fill();
}
// Clear canvas (to black)
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Fill shape (in white)
ctx.fillStyle = 'white';
circle(40, 50, 40);
circle(40, 10, 10);
circle(25, 15, 12);
circle(35, 90, 10);
// Get bitmap data
var id = ctx.getImageData(0, 0, canvas.width, canvas.height);
var pixels = id.data; // Flat array of RGBA bytes
// Determine area by counting the white pixels
for (var i = 0, area = 0; i < pixels.length; i += 4) {
area += pixels[i]; // Red channel (same as green and blue channels)
}
// Normalize by the max white value of 255
area /= 255;
// Output result
document.getElementById('result').innerHTML = area.toFixed(2);