카테고리 없음

알고리즘 - Convex Hull 문제

미국불여우 2023. 9. 8. 12:47

오늘 다룰 내용은 Convex Hull 이라고 불리우는 알고리즘 문제입니다. 

이 문제는 무작위로 배치된 다수의 점들사이에서 다른 모든점들을 감싸는 직선들을 만들고

그 직선들을 이루는 점들을 찾는 문제입니다.이 점들을 감싸는 직선은 볼록다각형의 형태를 가지고있어야하며

그 외의 형태는 인정되지않습니다.

위 사진 처럼 점들이 나열되어있을때 

이러한 형태로 만드는것이 목표입니다.

 

이를 위한 방법에는 여러가지가 있는데 그중 가장 간단한 두 점을 기준으로 하는방법을 설명하겠습니다.

그라함 스캔이라는 방법도 있는데 이 방법은 추후 설명하겠습니다.

 

우선 두점을 잇는 직선을 하나 그려줍니다.

그 후 그 직선을 기준으로 다른 점들의 위치를 계산해줍니다.

모든점이 한쪽에만 위치한다면 두 점은 컨벡스헐을 이루는 점이되고 그렇지않다면 다른 점으로 넘어갑니다.

이를 반복하면 컨벡스헐을 완성하게 됩니다.

이처럼 모든 경우를 탐색하여 답을 찾는 알고리즘을 브루트 포스라고 지칭합니다.

std::set<int> hullBruteForce ( std::vector< Point > const& points ) {
	int num_points = points.size();
	if ( num_points < 3 ) throw;
	std::set<int> hull_indices;
	std::pair<bool, bool> trigSide;
	std::pair<bool, bool> getSide;
	for (int i = 0; i < num_points - 1; i++)
	{
		for (int j = i; j < num_points; j++)
		{
			trigSide.first = true;
			trigSide.second = true;
			for (int k = 0; k < num_points && (trigSide.first || trigSide.second); k++)
			{
				getSide = get_location(points[i].x, points[i].y, points[j].x, points[j].y, points[k].x, points[k].y);
				if (i != k && j != k)
				{
					trigSide.first = getSide.first && trigSide.first;
					trigSide.second = getSide.second && trigSide.second;
				}
			}
			if (trigSide.first || trigSide.second)
			{
				hull_indices.insert(i);
				hull_indices.insert(j);
			}
		}
	}
	return hull_indices;
}

위 코드가 해당 내용을 바탕으로 구현한 코드입니다.

for (int i = 0; i < num_points - 1; i++)
	{
		for (int j = i; j < num_points; j++)
		{
			trigSide.first = true;
			trigSide.second = true;
			for (int k = 0; k < num_points && (trigSide.first || trigSide.second); k++)
			{
				getSide = get_location(points[i].x, points[i].y, points[j].x, points[j].y, points[k].x, points[k].y);
				if (i != k && j != k)
				{
					trigSide.first = getSide.first && trigSide.first;
					trigSide.second = getSide.second && trigSide.second;
				}
			}
			if (trigSide.first || trigSide.second)
			{
				hull_indices.insert(i);
				hull_indices.insert(j);
			}
		}
	}

우선 첫 점을 기준으로 잡고 다른 점 하나를 찾습니다. 

trigSide.first = true;
trigSide.second = true;

이 두 플래그는 참으로 시작해서 get_location 함수를 통해 좌우 위치를 확인합니다. 위치에 따라 각각 참,거짓을 리턴하기때문에 한쪽에만 있을경우 하나의 값만 참이 되고 다른경우엔 두 값 모두 거짓이 되게 됩니다.

trigSide.first = getSide.first && trigSide.first;
trigSide.second = getSide.second && trigSide.second;

위의 부분이 해당내용입니다.

trigSide.first || trigSide.second

아래 내용이 두 값이 모두 거짓일때 루프를 탈출하게하는 부분입니다.

if (trigSide.first || trigSide.second)
{
		hull_indices.insert(i);
		hull_indices.insert(j);
}

성공적으로 루프를 나왔을때 나온 두 값을 리턴할 값에 넣어줍니다.

set 컨테이너에 들어있기에 중복되는 값을 처리해줍니다.