可演示的凸包算法

本文介绍了一种使用分治法求解凸包问题的算法,并详细阐述了算法的设计思路,包括初始化随机点集、上包和下包的分治策略,以及结合tkinter的可视化展示。在可视化部分,文章讲解了如何逐步显示计算过程,并通过按钮控制上凸包和下凸包的显示,同时提供了退出功能。此外,还讨论了在实现过程中如何处理各种交互和错误提示,以确保程序的鲁棒性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分治法应用-凸包问题

问题描述

给定一个平面上n个点的集合,它的凸包就是包含所有这些点的最小凸多边形,求取满足此条件的所有点。

主要分为以下三个方面设计(思考):

1)初始化:随机生成某范围内一定规模的随机点集
2)问题求解:使用分治法思想求解初始化点集的凸包
3)可视化:分步显示出求解过程

功能模块详细设计

1.初始化

为方便演示设置了X和Y坐标的上限upper和下限lower,通过列表推导式和random.randint()函数生成设置范围内指定数量pointSize个坐标,再通过循环遍历把分散的X,Y坐标合并成点坐标加入点集points中。在利用plt.figure创建好的区域上,利用已生成的坐标再画出散点图。

   pointsSize = 100  # 100个点
   upper = 100  # 点的坐标上限为100
   lower = -100  # 点的坐标下限为-100
   x_ = [random.randint(lower, upper) for i in range(pointsSize)]
   y_ = [random.randint(lower, upper) for i in range(pointsSize)]
   points = []
   f = plt.figure(figsize=(7,5), dpi=100)
   for i in range(pointsSize):
      points.append([x_[i], y_[i]])
   for i in range(0, pointsSize):
      plt.scatter(x_[i], y_[i], facecolor='purple', s=20,alpha=0.5)

2.分治凸包算法

###2.1 判断三点间的位置关系–caculate函数
由 P1(x1, y1), P2 (x2, y2), P3 (x3, y3)三点的坐标组成3*3的行列式,表示三点围成的三角形面积,可以得出 在 和 确定的直线的上侧还是下侧。若值为正则在上侧,为负则在下侧,等于0则三点共线。定义caculate函数,参数为三点坐标,返回值为面积计算结果。
对应行列式

2.2 上包函数—findUpperMax

采用快包法,检测处于上凸包集合pointsup里的点,遍历集合全部的点,找到此时与底边距离最远的点,并连线。如果算出是在底边下侧的点,则设置标志让其坐标设为值-1,在下次递归时筛选出来,无需计算此点,即实现了将不和条件的点从上包点集中舍去的目的。如果遍历结果没有使上凸包的面积取到最大的点,则证明底边已经是凸包边界,需要结束,并输出。如果找到了使上凸包面积取到面积最大的点,则用该点与底边端点连线分成左凸包和右凸包,即分成两个规模更小的子问题,再重复递归问题。
细节:为了使左右凸包结果不相互影响,使用copy.deepcopy深复制来复制点集,分成pointsleft和pointsright。为了可以存储凸包点,用全局变量onside_point保存。

def findUpperMax(x1, y1, x2, y2, points):
    # 选取上包部分的一个点,该点为上包部分最远点
    # x1, y1: 边界上的左端点
    # x2, y2: 边界上的右端点
    # points: 可选点集
    # f: figure
	flag = False
   upperMax = 0
   upperx = uppery = 0
   for i in range(0,len(points)):#筛选点的过程
       if points[i]!=-1:
           if points[i][0] == x1 and points[i][1] == y1 or points[i][0] == x2 and points[i][1] == y2:
               continue
           S = caculate(x1,y1,x2,y2,points[i][0],points[i][1])
           if S > 0:
               if S > upperMax:
                   flag = True
                   upperMax = S
                   upperx = points[i][0]
                   uppery = points[i][1]
           else:
               points[i]=-1
   if flag == False:
       #flag作为标志,如果为F则,在图上连线
       plt.plot([x1, x2], [y1, y2], color='r')
		#把凸包边界上的点无重复的加入凸包点集onside_point
		if [x1,y1] not in onside_point:
    	onside_point.append([x1,y1])
		if [x2,y2] not in onside_point:
    	onside_point.append([x2,y2])
       return
   plt.plot([x1, upperx], [y1, uppery], color='b')
   plt.plot([x2, upperx], [y2, uppery], color='b')
   #可分为两段(左端点,顶点)递归,(顶点,右端点)递归
   pointsleft=copy.deepcopy(points)
   pointsright=copy.deepcopy(points)
   findUpperMax(x1, y1, upperx, uppery, pointsleft)
   findUpperMax(upperx, uppery, x2, y2, pointsright)

2.3 下包函数—findBottomMin

与上包的求解类似,只需修改找到使与底边围成面积最小的点,即为下包上的点。类似的做出下包递归的左右问题子集,递归求解。如果没找见,则连线变成下凸包的边。

3.可视化

3.1 在tkinter窗体显示

matplotlib与tkinter集成主要是通过两个类完成的,总体来说是画图三步走。
把绘制的图形显示到tkinter窗口上

canvas=FigureCanvasTkAgg(figure,self.root) 
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

所以只需要把在matplotlib作出的图传入即可。
把matplotlib绘制图形的导航工具栏显示到tkinter窗口上,总体也是分三步实现。

toolbar=NavigationToolbar2Tk(self.canvas,self.root)            
toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)

设置窗体的大小和位置

root = TK.Tk()
root.title("分治凸包")
root.geometry(f'{650}x{555}+{400}+{120}')
root.mainloop()

3.2 按钮

(1)下一步的实现

通过在每次需要画图显示时,分为两种情况。
情况1.在计算凸包的过程中,如果计算找到凸包上的点时,需要连线该点和底边的两点做出辅助线,便于理解分治法的分解子问题,在三角形内的部分就不在需要访问。继续分为左边的顶点集和右边的顶点集,递归求解。而且为了便于区分上下凸包的求解过程,使用不同颜色的辅助线,上包用蓝色表示,下包用绿色表示。
情况2.在递归过程中,如果未找见凸包顶点,则证明底边为凸包边界,此时应该输出。
只需在程序对应的位置输出图像即可。所谓下一步就是可以关闭当前的图像,并在处理好之后在重新输出的过程。因为这样,所以算是以程序本身驱动的。

def _quit():#下一步按钮调用的函数
    root.destroy()
#设置按钮‘下一步’
button1 = TK.Button(master=root, text=’下一步',width=5,height=1,command=_quit)
button1.place(x=250,y
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值