อัลกอริทึม Jarvis March ใช้สำหรับหาจุดมุมของ Convex Hull จากชุดข้อมูลจุด เริ่มจากจุดที่อยู่ซ้ายสุดของชุดข้อมูล เราจะเก็บจุดใน Convex Hull โดยหมุนทวนเข็มนาฬิกา จากจุดปัจจุบัน สามารถเลือกจุดถัดไปได้โดยตรวจสอบการวางแนวของจุดเหล่านั้นจากจุดปัจจุบัน เมื่อมุมมากที่สุด จุดนั้นจะถูกเลือก หลังจากครบทุกจุด เมื่อจุดถัดไปคือจุดเริ่มต้น ให้หยุดอัลกอริทึม
Input: ชุดจุด {(-7,8), (-4,6), (2,6), (6,4), (8,6), (7,-2), (4,-6), (8,-7),(0,0), (3,-2),(6,-10),(0,-6),(-9,-5),(-8,-2),(-8,0),(-10,3),(-2,2),(-10,4)}
Output: จุดขอบเขต Convex Hull: (-9, -5) (6, -10) (8, -7) (8, 6) (-7, 8) (-10, 4) (-10, 3)
อัลกอริทึม
findConvexHull(points, n)
Input: จุดต่างๆ และจำนวนจุด
Output: จุดมุมของ Convex Hull
Begin
start := points[0]
สำหรับแต่ละจุด i ทำ
ถ้า points[i].x < start.x แล้ว
// หาจุดซ้ายสุด
start := points[i]
จบ if
จบ for
current := start
เพิ่มจุดเริ่มต้นในชุดผลลัพธ์
กำหนดชุด colPts สำหรับเก็บจุด collinear
while true ทำ
// เริ่มลูปไม่รู้จบ
next := points[i]
สำหรับทุกจุด i ยกเว้นจุดที่ 0 ทำ
ถ้า points[i] = current แล้ว
ข้ามส่วนต่อไป ไปรอบถัดไป
จบ if
val := cross product ของ current, next, points[i]
ถ้า val > 0 แล้ว
next := points[i]
ล้างอาร์เรย์ colPts
ไม่เช่นนั้น ถ้า val = 0 แล้ว
ถ้า next ใกล้ current กว่า points[i] แล้ว
เพิ่ม next ใน colPts
next := points[i]
ไม่เช่นนั้น
เพิ่ม points[i] ใน colPts
จบ if
จบ if
จบ for
เพิ่มทุกอย่างใน colPts ลงในผลลัพธ์
ถ้า next = start แล้ว
ออกจากลูป
จบ if
เพิ่ม next ลงในผลลัพธ์
current := next
จบ while
return result
End
ตัวอย่างโค้ด
#include <iostream>
#include <set>
#include <vector>
using namespace std;
struct point {
int x, y;
bool operator==(point p2) {
if(x == p2.x && y == p2.y)
return 1;
return 0;
}
bool operator<(const point &p2)const {
// ฟังก์ชันเปรียบเทียบ dummy ใช้สำหรับจัดเรียงใน set
return true;
}
};
int crossProduct(point a, point b, point c) {
// หาตำแหน่งของ c จากเวกเตอร์ ab
int y1 = a.y - b.y;
int y2 = a.y - c.y;
int x1 = a.x - b.x;
int x2 = a.x - c.x;
return y2*x1 - y1*x2; // ถ้าผลลัพธ์ < 0, c อยู่ซ้าย, > 0 อยู่ขวา, = 0 จุด a,b,c อยู่บนเส้นตรงเดียวกัน
}
int distance(point a, point b, point c) {
int y1 = a.y - b.y;
int y2 = a.y - c.y;
int x1 = a.x - b.x;
int x2 = a.x - c.x;
int item1 = (y1*y1 + x1*x1);
int item2 = (y2*y2 + x2*x2);
if(item1 == item2)
return 0; // เมื่อ b และ c อยู่ห่างจาก a เท่ากัน
else if(item1 < item2)
return -1; // เมื่อ b ใกล้ a กว่า
return 1; // เมื่อ c ใกล้ a กว่า
}
set<point> findConvexHull(point points[], int n) {
point start = points[0];
for(int i = 1; i<n; i++) {
// หาจุดซ้ายสุดสำหรับเริ่มต้น
if(points[i].x < start.x)
start = points[i];
}
point current = start;
set<point> result;
// ใช้ set เพื่อป้องกันจุดซ้ำ
result.insert(start);
vector<point> *collinearPoints = new vector<point>;
while(true) {
point nextTarget = points[0];
for(int i = 1; i<n; i++) {
if(points[i] == current) // เมื่อจุดที่เลือกคือจุดปัจจุบัน ข้ามส่วนที่เหลือ
continue;
int val = crossProduct(current, nextTarget, points[i]);
if(val > 0) {
// เมื่อจุดที่ i อยู่ด้านซ้าย
nextTarget = points[i];
collinearPoints = new vector<point>; // รีเซตจุด collinear
}else if(val == 0) {
// ถ้าจุด 3 จุดอยู่บนเส้นตรงเดียวกัน
if(distance(current, nextTarget, points[i]) < 0) {
// เพิ่มจุดที่ใกล้กว่าในรายการ collinear
collinearPoints->push_back(nextTarget);
nextTarget = points[i];
}else{
collinearPoints->push_back(points[i]); // เมื่อจุด i ใกล้กว่าหรือเท่ากับ nextTarget
}
}
}
vector<point>::iterator it;
for(it = collinearPoints->begin(); it != collinearPoints->end(); it++) {
result.insert(*it); // เพิ่มจุดทั้งหมดใน collinear points ลงใน result set
}
if(nextTarget == start) // เมื่อจุดถัดไปคือจุดเริ่มต้น หมายถึงครอบคลุมพื้นที่แล้ว
break;
result.insert(nextTarget);
current = nextTarget;
}
return result;
}
int main() {
point points[] = {{-7,8},{-4,6},{2,6},{6,4},{8,6},{7,-2},{4,-6},{8,-7},{0,0},
{3,-2},{6,-10},{0,-6},{-9,-5},{-8,-2},{-8,0},{-10,3},{-2,2},{-10,4}};
int n = 18;
set<point> result;
result = findConvexHull(points, n);
cout << "Boundary points of convex hull are: "<<endl;
set<point>::iterator it;
for(it = result.begin(); it!=result.end(); it++)
cout << "(" << it->x << ", " <<it->y <<") ";
}
ผลลัพธ์
Boundary points of convex hull are: (-9, -5) (6, -10) (8, -7) (8, 6) (-7, 8) (-10, 4) (-10, 3)