Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Lập Trình

Thuật toán Jarvis March (Convex Hull)


Giới thiệu

Thuật toán Jarvis March được dùng để phát hiện các điểm góc của bao lồi (convex hull) từ một tập hợp điểm dữ liệu cho trước.

Bắt đầu từ điểm bên trái nhất của tập dữ liệu, ta giữ các điểm trong bao lồi bằng cách quay ngược chiều kim đồng hồ. Từ điểm hiện tại, ta có thể chọn điểm tiếp theo bằng cách kiểm tra hướng của các điểm đó từ điểm hiện tại. Khi góc lớn nhất, điểm đó được chọn. Sau khi hoàn tất tất cả các điểm, khi điểm tiếp theo là điểm bắt đầu, dừng thuật toán.

Đầu vào và Đầu ra

Đầu vào: Tập điểm: {(-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)}
Đầu ra: Các điểm biên của bao lồi là: (-9, -5) (6, -10) (8, -7) (8, 6) (-7, 8) (-10, 4) (-10, 3)

Thuật toán

findConvexHull(points, n)
Đầu vào: Các điểm, số lượng điểm.
Đầu ra: Các điểm góc của bao lồi.

Begin
    start := points[0]
    for each point i, do
        if points[i].x < start.x, then
            // lấy điểm bên trái nhất
            start := points[i]
    done
    current := start
    thêm điểm start vào tập kết quả.
    định nghĩa tập colPts để lưu các điểm thẳng hàng
    while true, do
        // bắt đầu vòng lặp vô hạn
        next := points[i]
        for all points i except 0th point, do
            if points[i] = current, then
                bỏ qua phần tiếp theo, sang lần lặp tiếp theo
            val := tích chéo của current, next, points[i]
            if val > 0, then
                next := points[i]
                xóa mảng colPts
            else if cal = 0, then
                if next gần current hơn points[i], then
                    thêm next vào colPts
                    next := points[i]
                else
                    thêm points[i] vào colPts
        done
        thêm tất cả mục trong colPts vào kết quả
        if next = start, then
            break vòng lặp
        chèn next vào kết quả
        current := next
    done
    return result
End

Ví dụ (C++)

#include <iostream>
#include <set>
#include <vector>
using namespace std;

struct point {
    // định nghĩa điểm cho mặt phẳng 2d
    int x, y;
    bool operator==(point p2) {
        if(x == p2.x && y == p2.y)
            return 1;
        return 0;
    }
    bool operator<(const point &p2)const {
        // hàm so sánh giả dùng để sắp xếp trong set
        return true;
    }
};

int crossProduct(point a, point b, point c) {
    // tìm vị trí của c từ vectơ 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;  // nếu kết quả < 0, c ở bên trái, > 0 c ở bên phải, = 0 a,b,c thẳng hàng
}

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;  // khi b và c cùng khoảng cách từ a
    else if(item1 < item2)
        return -1;  // khi b gần a hơn
    return 1;  // khi c gần a hơn
}

set<point> findConvexHull(point points[], int n) {
    point start = points[0];
    for(int i = 1; i < n; i++) {
        // tìm điểm bên trái nhất để bắt đầu
        if(points[i].x < start.x)
            start = points[i];
    }
    point current = start;
    set<point> result;  // set dùng để tránh nhập điểm trùng lặp
    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)  // khi điểm chọn là điểm hiện tại, bỏ qua phần còn lại
                continue;
            int val = crossProduct(current, nextTarget, points[i]);
            if(val > 0) {  // khi điểm i ở bên trái
                nextTarget = points[i];
                collinearPoints = new vector<point>();  // reset điểm thẳng hàng
            } else if(val == 0) {  // nếu ba điểm thẳng hàng
                if(distance(current, nextTarget, points[i]) < 0) { // thêm điểm gần hơn vào danh sách thẳng hàng
                    collinearPoints->push_back(nextTarget);
                    nextTarget = points[i];
                } else {
                    collinearPoints->push_back(points[i]); // khi điểm i gần hơn hoặc bằng nextTarget
                }
            }
        }
        vector<point>::iterator it;
        for(it = collinearPoints->begin(); it != collinearPoints->end(); it++) {
            result.insert(*it);  // thêm tất cả điểm trong collinear points vào result set
        }
        if(nextTarget == start)  // khi điểm tiếp theo là start nghĩa là khu vực đã được bao phủ
            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 << ") ";
}

Kết quả

Boundary points of convex hull are: (-9, -5) (6, -10) (8, -7) (8, 6) (-7, 8) (-10, 4) (-10, 3)