点集的最小覆盖圆求解

2023-10-13 03:30
文章标签 覆盖 最小 求解 点集

本文主要是介绍点集的最小覆盖圆求解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最小覆盖圆问题就是给定一个由n个点组成的点集,寻找一个半径最小的圆,将整个点集包围。求解的方法由很多,例如点增量法和三角形增量法。

参考解析几何--最小圆覆盖_牛客博客

点增量法的步骤如下:

下面针对点增量法,用python进行一步一步实现。

首先,先定义点(point)和距离(distance)

class point:def __init__(self, x, y):self.x = xself.y = ydef distance(A, B):return math.sqrt((A.x - B.x)**2 + (A.y - B.y)**2)

一、三点的最小覆盖圆

三点的最小覆盖圆既可能是三点的外接圆,也可能是以其中两个点连线为直径的圆。

三角形外接圆

针对三点组成的三角形,求取其半径及圆心坐标。

def circumcircle(A, B, C):# 防止出现三个点位于一条直线上的情况eps = 0.000000000000000000001a = distance(A, B)b = distance(B, C)c = distance(C, A)r = (a*b*c) / math.sqrt((a+b+c) * (-a+b+c) * (a-b+c) * (a+b-c) + eps)# 计算外心坐标a1 = 2*(A.x - B.x)b1 = 2*(A.y - B.y)a2 = 2*(B.x - C.x)b2 = 2*(B.y - C.y)c1 = A.x**2 - B.x**2 + A.y**2 - B.y**2c2 = B.x**2 - C.x**2 + B.y**2 - C.y**2x = (b2*c1 - b1*c2) / (a1*b2 - a2*b1 + eps)y = (-a2*c1 + a1*c2) / (a1*b2 - a2*b1 + eps)circum_point = point(x, y)results = [r, circum_point]return results

以其中两个点连线为直径的覆盖圆

以两点连线为直径的圆作为覆盖圆的充要条件是,第三个点在该圆内。否则,这个圆就不能作为覆盖圆,就无需与三点的外接圆进行比较是否是最小覆盖圆。

def twoPointCircle(A, B, C):""":param A: 点1:param B: 点2:param C: 直线AB外一点C:return: 如果点C在以AB为直径的圆内,则返回半径和圆心,否则返回-1"""centerPoint = point((A.x+B.x)/2, (A.y+B.y)/2)r = distance(A, B) / 2d = distance(C, centerPoint)if d <= r:results = [r, centerPoint]else:results = [-1, centerPoint]return results

求取三点的最小覆盖圆

比较外接圆和以其中两个点连线为直径的覆盖圆的半径,取最小半径的作为最小覆盖圆。

# 找三点中最小覆盖圆
def minCircle(A, B, C):# 外接圆circum_results = circumcircle(A, B, C)# 以其中两点为直径的圆AB_results = twoPointCircle(A, B, C)BC_results = twoPointCircle(B, C, A)CA_results = twoPointCircle(C, A, B)circleOfTwoPoint = [AB_results, BC_results, CA_results]min_r = circum_results[0]min_circle_point = circum_results[1]for result in circleOfTwoPoint:if result[0] == -1:continueelse:if result[0] < min_r:min_r = result[0]min_circle_point = result[1]return min_r, min_circle_point

二、四个点的最小覆盖圆

在求取了ABC三点的最小覆盖圆以后,若 点集 中距离该最小覆盖圆圆心最远的点D不在圆内,那么根据步骤(4),需要在这四个点中找到其中三个点的最小覆盖圆,使其包含这四个点,且半径最小。

寻找点集中距离ABC三点最小覆盖圆的点D

暴力搜索

def findLongestPoint(pointSet, centralPoint):d = 0longestPoint = Nonefor p in pointSet:d_p = distance(p, centralPoint)if d_p >= d:longestPoint = pd = d_preturn longestPoint

判断点D是否在ABC三点的最小覆盖圆中

def isInMinCircle(A, B, C, D):min_r, min_circle_point = minCircle(A, B, C)d = distance(D, min_circle_point)if d > min_r:results = [0, min_r, A, B, C]else:results = [1, min_r, A, B, C]return results

在ABCD四点中寻找新的ABC三点,生成四点最小覆盖圆

def findNewThreePoint(A, B, C, D):ABD_results = isInMinCircle(A, B, D, C)ACD_results = isInMinCircle(A, C, D, B)BCD_results = isInMinCircle(B, C, D, A)results = [ABD_results, ACD_results, BCD_results]# 较大的数值min_r = 100000000for result in results:if result[0] == 0:continueelse:if result[1] <= min_r:A = result[2]B = result[3]C = result[4]return A, B, C

三、实验验证

点集

使用包含二十个点的点集对算法进行验证,寻找其最小覆盖圆。

画图

def plt_circle(pointSet, min_circle_point, min_r, isCircum=False):# 所有点positionPointSet = np.array([[-7.590, 0.796],[-7.601, 0.796],[-7.603, 0.797],[-7.592, 0.796],[-7.591, 0.788],[-7.592, 0.794],[-7.587, 0.794],[-7.587, 0.789],[-7.590, 0.792],[-7.588, 0.798],[-7.596, 0.794],[-7.606, 0.797],[-7.607, 0.795],[-7.609, 0.797],[-7.610, 0.798],[-7.610, 0.790],[-7.607, 0.791],[-7.610, 0.797],[-7.605, 0.796],[-7.604, 0.791]])pointSets = []for p in positionPoint:pointSets.append(point(p[0], p[1]))# 画出所有的点plt.plot(positionPointSet[:, 0], positionPointSet[:, 1], "go", label="point set")# 最远点D = findLongestPoint(pointSets, min_circle_point)plt.plot(D.x, D.y, "ys", label="most far point")# 画三点positionPoints = []for p in pointSet:positionPoints.append([p.x, p.y])positionPoints = np.array(positionPoints)plt.plot(positionPoints[:, 0], positionPoints[:, 1], "r*", label="calcu points")# 画圆心plt.plot(min_circle_point.x, min_circle_point.y, "kx", label="min circle central point")# 画圆theta = np.arange(-math.pi, math.pi, 2*math.pi/1000)x_circle = [min_circle_point.x + min_r * math.cos(t) for t in theta]y_circle = [min_circle_point.y + min_r * math.sin(t) for t in theta]plt.plot(x_circle, y_circle, label="min circle")if isCircum:result = circumcircle(pointSet[0], pointSet[1], pointSet[2])x_circumcircle = [result[1].x + result[0] * math.cos(t) for t in theta]y_circumcircle = [result[1].y + result[0] * math.sin(t) for t in theta]plt.plot(x_circumcircle, y_circumcircle, label="circumcircle")plt.legend()plt.axis("equal")plt.grid()plt.show()

测试代码

if __name__ == "__main__":eps = 0.0000001pointSet = []positionPoint = np.array([[-7.590, 0.796],[-7.601, 0.796],[-7.603, 0.797],[-7.592, 0.796],[-7.591, 0.788],[-7.592, 0.794],[-7.587, 0.794],[-7.587, 0.789],[-7.590, 0.792],[-7.588, 0.798],[-7.596, 0.794],[-7.606, 0.797],[-7.607, 0.795],[-7.609, 0.797],[-7.610, 0.798],[-7.610, 0.790],[-7.607, 0.791],[-7.610, 0.797],[-7.605, 0.796],[-7.604, 0.791]])for p in positionPoint:pointSet.append(point(p[0], p[1]))A = pointSet[0]B = pointSet[1]C = pointSet[2]min_r, min_circle_point = minCircle(A, B, C)plt_circle([A, B, C], min_circle_point, min_r, isCircum=True)D = findLongestPoint(pointSet, min_circle_point)while distance(D, min_circle_point) - min_r > eps:A, B, C = findNewThreePoint(A, B, C, D)min_r, min_circle_point = minCircle(A, B, C)plt_circle([A, B, C], min_circle_point, min_r, isCircum=True)D = findLongestPoint(pointSet, min_circle_point)print(min_r)print("X: {0} | Y: {1}".format(min_circle_point.x, min_circle_point.y))

结果

第一次迭代结果

 第二次迭代结果

 第三次迭代结果

第四次迭代结果

 经过四次迭代之后,找到了点集的最小覆盖圆,输出半径和圆心结果。

0.012349089035228739
X: -7.5985 | Y: 0.7935000000000001

这篇关于点集的最小覆盖圆求解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/200461

相关文章

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri

poj 2175 最小费用最大流TLE

题意: 一条街上有n个大楼,坐标为xi,yi,bi个人在里面工作。 然后防空洞的坐标为pj,qj,可以容纳cj个人。 从大楼i中的人到防空洞j去避难所需的时间为 abs(xi - pi) + (yi - qi) + 1。 现在设计了一个避难计划,指定从大楼i到防空洞j避难的人数 eij。 判断如果按照原计划进行,所有人避难所用的时间总和是不是最小的。 若是,输出“OPETIMAL",若

poj 2135 有流量限制的最小费用最大流

题意: 农场里有n块地,其中约翰的家在1号地,二n号地有个很大的仓库。 农场有M条道路(双向),道路i连接着ai号地和bi号地,长度为ci。 约翰希望按照从家里出发,经过若干块地后到达仓库,然后再返回家中的顺序带朋友参观。 如果要求往返不能经过同一条路两次,求参观路线总长度的最小值。 解析: 如果只考虑去或者回的情况,问题只不过是无向图中两点之间的最短路问题。 但是现在要去要回

poj 3422 有流量限制的最小费用流 反用求最大 + 拆点

题意: 给一个n*n(50 * 50) 的数字迷宫,从左上点开始走,走到右下点。 每次只能往右移一格,或者往下移一格。 每个格子,第一次到达时可以获得格子对应的数字作为奖励,再次到达则没有奖励。 问走k次这个迷宫,最大能获得多少奖励。 解析: 拆点,拿样例来说明: 3 2 1 2 3 0 2 1 1 4 2 3*3的数字迷宫,走两次最大能获得多少奖励。 将每个点拆成两个

poj 2195 bfs+有流量限制的最小费用流

题意: 给一张n * m(100 * 100)的图,图中” . " 代表空地, “ M ” 代表人, “ H ” 代表家。 现在,要你安排每个人从他所在的地方移动到家里,每移动一格的消耗是1,求最小的消耗。 人可以移动到家的那一格但是不进去。 解析: 先用bfs搞出每个M与每个H的距离。 然后就是网络流的建图过程了,先抽象出源点s和汇点t。 令源点与每个人相连,容量为1,费用为

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了