Python动态演示分治法解决凸包问题

第一步:把给定点集中的点在横坐标方向上按照大小排序。找到横坐标相距最大的两个点p1和p2,这两个点必定是凸包上的两个点,如下图所示。
Python动态演示分治法解决凸包问题_第1张图片
第二步:在点集合中找到与p1和p2构成三角形面积大于0的所有点,在这些点中找到构成面积的点p_max,如下图所示。显然直线段p1 p_max与直线段p_max p2把点集分成了三个集合。由凸包的性质可知p1 p_max p2三点围成的三角形中的点不可能作为凸包的顶点,所以只需考虑直线p1 p_max左边的点以及直线p_max p2右边的点。
Python动态演示分治法解决凸包问题_第2张图片Python动态演示分治法解决凸包问题_第3张图片
第三步:递归求解得到凸多边形的边。
第四步:合并这些边即得所求凸包。

from threading import Thread
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import random

# 生成所有的点
all = []
def all_points():
   n = int(input("生成的随机点数量:"))
   while len(all) < n:
       x = random.randint(0, 250)
       y = random.randint(0, 250)
       if [x, y] not in all:
           all.append([x, y])
   return all

# 计算面积
def area(a, b, c):
    x1, y1 = a
    x2, y2 = b
    x3, y3 = c
    return x1 * y2 + x3 * y1 + x2 * y3 - x3 * y2 - x2 * y1 - x1 * y3

# 将所有的点绘制在坐标图上
def draw(list_points):
    list_all_x = []
    list_all_y = []
    for item in list_points:
        a, b = item
        list_all_x.append(a)
        list_all_y.append(b)
    ax.scatter(list_all_x, list_all_y)

# 自定义类,用来封装三个按钮的单击事件处理函数
class ButtonHandler:
    def __init__(self,left_point, right_point, lists, border_points):
        self.area_max = 0
        self.point_max = ()
        self.left_point=left_point
        self.right_point=right_point
        self.lists = lists
        self.border_points=border_points

    # 线程函数,用来更新数据并重新绘制图形
    # 寻找上半部分的边界点,并连成线段
    def Thread_up(self):
        ax.plot([self.left_point[0], self.right_point[0]], [self.left_point[1], self.right_point[1]], color='y')
        plt.pause(1)
        for item in self.lists:
            if item == self.left_point or item == self.right_point:
                continue
            else:
                new_area = area(self.left_point, self.right_point, item)
                if new_area > self.area_max:
                    self.point_max = item
                    self.area_max = new_area

        if self.area_max != 0:
            self.border_points.append(self.point_max)
            a = ButtonHandler(self.left_point, self.point_max, self.lists, self.border_points)
            a.Thread_up()
            b = ButtonHandler(self.point_max, self.right_point, self.lists, self.border_points)
            b.Thread_up()

    def up1(self):
        for item in self.lists:
            if item == self.left_point or item == self.right_point:
                continue
            else:
                new_area = area(self.left_point, self.right_point, item)
                if new_area > self.area_max:
                    self.point_max = item
                    self.area_max = new_area

        if self.area_max != 0:
            self.border_points.append(self.point_max)
            a = ButtonHandler(self.left_point, self.point_max, self.lists, self.border_points)
            a.up1()
            b = ButtonHandler(self.point_max, self.right_point, self.lists, self.border_points)
            b.up1()

    # 寻找下半部分的边界点,并连成线段
    def Thread_down(self):
        ax.plot([self.left_point[0], self.right_point[0]], [self.left_point[1], self.right_point[1]], color='y')
        plt.pause(1)
        for item in self.lists:
            if item == self.left_point or item == self.right_point:
                continue
            else:
                new_area = area(self.left_point, self.right_point, item)
                if new_area < self.area_max:
                    self.point_max = item
                    self.area_max = new_area

        if self.area_max != 0:
            border_points.append(self.point_max)
            c = ButtonHandler(self.left_point, self.point_max, self.lists, border_points)
            c.Thread_down()
            d = ButtonHandler(self.point_max, self.right_point, self.lists, border_points)
            d.Thread_down()

    def down1(self):
        for item in self.lists:
            if item == self.left_point or item == self.right_point:
                continue
            else:
                new_area = area(self.left_point, self.right_point, item)
                if new_area < self.area_max:
                    self.point_max = item
                    self.area_max = new_area

        if self.area_max != 0:
            border_points.append(self.point_max)
            c = ButtonHandler(self.left_point, self.point_max, self.lists, border_points)
            c.down1()
            d = ButtonHandler(self.point_max, self.right_point, self.lists, border_points)
            d.down1()

    def Up(self, event):
        t = Thread(target=self.Thread_up)
        t.start()

    def Down(self, event):
        t = Thread(target=self.Thread_down)
        t.start()

    def draw(self,event):
        self.border_points.sort()
        first_x, first_y = self.border_points[0]  # 最左边的点
        last_x, last_y = self.border_points[-1]  # 最右边的点
        list_border_up = []  # 上半边界
        for item in self.border_points:
            x, y = item
            if y > max(first_y, last_y):
                list_border_up.append(item)
            if min(first_y, last_y) < y < max(first_y, last_y):
                if area(self.border_points[0], self.border_points[-1], item) > 0:
                    list_border_up.append(item)
                else:
                    continue
        list_border_down = [_ for _ in self.border_points if _ not in list_border_up]  # 下半边界
        list_end = list_border_up + list_border_down[::-1]  # 最终顺时针输出的边界点
        list_end.append(list_end[0])
        for i in range(len(list_end) - 1):
            one_, oneI = list_end[i]
            two_, twoI = list_end[i + 1]
            ax.plot([one_, two_], [oneI, twoI],color='r')

if __name__ == "__main__":
    fig, ax = plt.subplots()
    plt.subplots_adjust(bottom=0.2)

    all_points=all_points()   # 生成所有的点
    all_points.sort()
    left_p, right_p = all_points[0], all_points[-1]  # 所有点中横坐标相距最大的两个点
    border_points = []  # 边界点集
    draw(all_points)

    # 创建按钮并设置单击事件处理函数
    callback = ButtonHandler(left_p, right_p, all_points, border_points)
    axprev = plt.axes([0.81, 0.05, 0.1, 0.075])
    bprev = Button(axprev, 'UP')
    bprev.on_clicked(callback.Up)

    down = ButtonHandler(left_p, right_p, all_points, border_points)
    axnext = plt.axes([0.7, 0.05, 0.1, 0.075])
    bnext = Button(axnext, 'DOWN')
    bnext.on_clicked(down.Down)

    e=ButtonHandler(left_p, right_p, all_points, border_points)
    e.up1()
    e.down1()
    border_points.append(left_p)
    border_points.append(right_p)  # 将首尾两个点添加到边界点集中
    print(border_points)  # 输出边界点
    zuihou = plt.axes([0.59, 0.05, 0.1, 0.075])
    tubao = Button(zuihou, 'tubao')
    tubao.on_clicked(e.draw)
    plt.show()


你可能感兴趣的:(Python)