Jarvis步进法(Jarvis March)凸包算法

2024-01-10 10:47:00

Jarvis步进法(也称为包裹法): Jarvis步进法是一种逐步选择凸包顶点的算法。从点集中选择一个起始点,然后在每一步中选择下一个顶点,该顶点是当前点集中与当前点形成的线段上,极角最小的点。该算法的时间复杂度为O(nh),其中n是点的数量,h是凸包的顶点数。请帮我使用 python 实现该算法,并且绘制出原始的离散点和凸包的点并连接成凸包多边形

Jarvis步进法(Jarvis March),又称为包裹法(Gift Wrapping),是一种求解平面点集凸包问题的算法。凸包是包含给定点集合的最小凸多边形。以下是Jarvis步进法的基本数学原理:

  • 选择起始点: 算法首先需要选择一个起始点。通常情况下,选择最左下角的点,以确保算法的稳定性。选择起始点的过程可以通过遍历点集,找到y坐标最小的点,并在y坐标相同时,选择x坐标最小的点。

  • 极角排序: 在选择了起始点之后,对剩余的点按照相对于当前点的极角进行排序。极角可以使用反正切函数(atan2)计算,它表示从当前点到其他点的线段与x轴正方向之间的夹角。排序后的点集顺序将决定扫描过程中点的访问顺序。

  • 扫描过程: 从起始点开始,选择当前点(假设为p),然后选择下一个点(假设为q),这个点是当前点p与其他点形成的线段上,极角最小的点。通过比较极角大小,可以确定下一个点的选择。重复这个过程,直到回到起始点形成一个闭合的凸包。

  • 构建凸包: 扫描完成后,选择的点序列就是凸包的顶点,这个顺序是按照逆时针方向排列的。

    关键思想是通过在每一步中选择具有最小极角的点,逐步构建凸包。这样可以确保在构建的凸包上,点的极角是递增的,最终形成一个逆时针方向的凸多边形。算法的时间复杂度主要取决于对点的排序操作,通常为O(n log n),其中n是点的数量。 Jarivs步进法的优势在于其相对简单的实现和较好的性能。然而,在某些情况下,例如存在大量共线点的情况下,算法的性能可能会下降。

import matplotlib.pyplot as plt
import numpy as np

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

def orientation(p, q, r):
    val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)
    if val == 0:
        return 0  # 平行
    return 1 if val > 0 else 2  # 顺时针或逆时针

def jarvis_march(points):
    n = len(points)
    if n < 3:
        print("Jarvis March requires at least 3 points.")
        return []

    hull = []

    # 找到最左下角的点作为起始点
    start_point = min(points, key=lambda p: (p.y, p.x))
    current_point = start_point

    while True:
        hull.append(current_point)
        next_point = points[0]

        for candidate_point in points:
            if candidate_point != current_point:
                if next_point == current_point or orientation(current_point, next_point, candidate_point) == 2:
                    next_point = candidate_point

        current_point = next_point

        if current_point == start_point:
            break

    return hull

def plot_convex_hull(points, convex_hull):
    x_values = [point.x for point in points]
    y_values = [point.y for point in points]

    hull_x = [point.x for point in convex_hull]
    hull_y = [point.y for point in convex_hull]
    hull_x.append(convex_hull[0].x)  # 闭合凸包
    hull_y.append(convex_hull[0].y)

    plt.scatter(x_values, y_values, color='blue', label='Original Points')
    plt.plot(hull_x, hull_y, color='red', linestyle='-', linewidth=2, label='Convex Hull')

    plt.legend()
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.title('Jarvis March Convex Hull')
    plt.grid(True)
    plt.show()

# 示例点集
new_points = [Point(1, 1), Point(2, 3), Point(4, 2), Point(5, 5), Point(7, 1), Point(8, 4), Point(9, 2), Point(10, 6)]

# 计算凸包
new_convex_hull_points = jarvis_march(new_points)

# 绘制原始离散点和凸包
plot_convex_hull(new_points, new_convex_hull_points)

在这里插入图片描述

#include <iostream>
#include <vector>
#include <algorithm>

class Point {
public:
    double x, y;

    Point(double _x, double _y) : x(_x), y(_y) {}

    // 定义 == 运算符
    bool operator==(const Point& other) const {
        return (x == other.x) && (y == other.y);
    }

    // 定义 != 运算符
    bool operator!=(const Point& other) const {
        return !(*this == other);
    }
};

int orientation(const Point& p, const Point& q, const Point& r) {
    double val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
    if (val == 0) return 0;  // 平行
    return (val > 0) ? 1 : 2; // 顺时针或逆时针
}

std::vector<Point> jarvisMarch(const std::vector<Point>& points) {
    size_t n = points.size();
    if (n < 3) {
        std::cerr << "Jarvis March requires at least 3 points." << std::endl;
        return std::vector<Point>();
    }

    std::vector<Point> hull;
    Point start_point = *std::min_element(points.begin(), points.end(),
        [](const Point& p1, const Point& p2) {
            return (p1.y < p2.y) || (p1.y == p2.y && p1.x < p2.x);
        });
    Point current_point = start_point;

    while (true) {
        hull.push_back(current_point);
        Point next_point = points[0];

        for (const auto& candidate_point : points) {
            if (candidate_point != current_point) {
                if (next_point == current_point || orientation(current_point, next_point, candidate_point) == 2) {
                    next_point = candidate_point;
                }
            }
        }

        current_point = next_point;

        if (current_point == start_point) {
            break;
        }
    }

    return hull;
}

int main() {
    // 示例点集
    std::vector<Point> newPoints = { {1, 1}, {2, 3}, {4, 2}, {5, 5}, {7, 1}, {8, 4}, {9, 2}, {10, 6} };

    // 计算凸包
    std::vector<Point> newConvexHullPoints = jarvisMarch(newPoints);

    // 打印凸包点信息
    std::cout << "Convex Hull Points:" << std::endl;
    for (const auto& point : newConvexHullPoints) {
        std::cout << "(" << point.x << ", " << point.y << ")" << std::endl;
    }

    return 0;
}

在这里插入图片描述

文章来源:https://blog.csdn.net/weixin_43862398/article/details/135495546
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。