计算几何常用算法及numpy仿真

30 篇文章 0 订阅

在很久之前的一篇的文章点乘和叉乘及其物理意义(C++STL实现),我们用C++(STL)实现了对向量内积和叉积的定义与简单计算,最后演示了如何用几何的方法计算点到直线的距离计算任意三角形的面积等问题。把这些放在更大的范围内,其实就是今天的主角,计算几何(Computational geometry)

本文所有的仿真程序均在numpy(python第三方的科学计算库)下进行:

import numpy as np

1. 矢量减法

设二维矢量 P=(x 1 ,y 1 )  Q=(x 2 ,y 2 ) 
则矢量减法定义(对应位相减)为: PQ=(x 1 x 2 ,y 1 y 2 ) 
显然有性质 PQ=(QP) 
如不加说明,下面所有的都看作矢量,两点的减法就是矢量相减;

def sub(p, q):
    return p-q
p, q = np.array([1, 2]), np.array([3, 5])
print(sub(p, q))
print(-sub(q, p))

2. 矢量叉积

设矢量 P=(x 1 ,y 1 ),Q=(x 2 ,y 2 )  (本文以二维为例,可自然推广到三维)
则矢量叉积定义为: P×Q=x 1 y 2 x 2 y 1   得到的是一个标量
显然有性质 P×Q=(Q×P),P×(Q)=(P×Q) 
如不加说明,下面所有的点都看作矢量,点的乘法看作矢量叉积;

叉乘的重要性质:

若 P × Q > 0 , 则P 在Q的顺时针方向
若 P × Q < 0 , 则P 在Q的逆时针方向(与性质1等价)
若 P × Q = 0 , 则P 与Q共线,但可能同向也可能反向

def cross_prod(p, q):
    assert len(p)==2 and len(q)==2
    return p[0]*q[1]-p[1]*q[0]
p, q = np.array([1, 0]), np.array([0, 1])
                        # p在q的顺时针方向
print(cross_prod(p, q))
                        # 1>0
p, q = np.array([1, 2], [2, 4])         
                        # p, q 同向
print(cross_prod(p, q))
                        # 0

3. 判断点在线段上

设点为 Q  ,线段为P 1 P 2   ,判断点 Q  在该线段上的依据是:
(QP 1 )×(P 2 P 1 )=0  Q  在以 P 1 ,P 2   为对角顶点的矩形内;
这是叉积性质的直接应用,也即 (QP 1 )×(P 2 P 1 )=0  ,则 QP 1   P 2 P 1   共线;

def is_point_on_line(q, p1, p2):
    return True if cross_prod(q-p1, p2-p1)==0 else False
p1, p2 = np.array([1, 0]), np.array([0, 1])
q = np.array([1/2, 1/2])
print(is_point_on_line(q, p1, p2))
                    # True
q = np.array([1/2, 1/3])
print(is_point_on_line(q, p1, p2))
                    # False

4. 判断两线段是否相交

我们分两步确定两条线段是否相交:

  1. 快速排斥试验

    设以线段 P 1 P 2   为对角线的矩形为 R  , 设以线段 Q 1 Q 2   为对角线的矩形为 T  ,如果R  T  不相交,显然两线段不会相交;

def rect_overlap(r1, r2):
    return not (((r1[1][0] < r2[0][0])|(r1[0][1] > r2[1][1]))
               |((r2[1][0] < r1[0][0])|(r2[0][1] > r1[1][1]))
  1. 跨立试验

    如果两线段相交,则两线段必然相互跨立对方,如图1所示。在图1中,P 1 P 2  跨立 Q 1 Q 2   ,则 矢量 (P1Q1)  (P 2 Q 1 )  位于矢量 (Q 2 Q 1 )  的两侧,即 (P1Q1)×(Q2Q1)(P2Q1)×(Q2Q1)<0  上式可改写成 ( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) > 0,
    当 ( P1 - Q1 ) × ( Q2 - Q1 ) = 0 时,说明 ( P1 - Q1 ) 和 ( Q2 - Q1 )共线,但是因为已经通过快速排斥试验,所以 P1 一定在线段 Q1Q2上;同理,( Q2 - Q1 ) ×(P2 - Q1 ) = 0 说明 P2 一定在线段 Q1Q2上。 所以判断P1P2跨立Q1Q2的依据是: ( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) ≥ 0 同理判断Q1Q2跨立P1P2的依据是: ( Q1 - P1 ) × ( P2 - P1 ) * ( P2 - P1 ) × ( Q2 - P1 ) ≥ 0至此已经完全解决判断线段是否相交的问题。

def line_intersection(p1, p2, q1, q2):
    return False if not rect_overlap([p1, p2], [q1, q2]) else \             # 不相交直接返回
            (True if cross_prod(p1-q1, q2-q1)*cross_prod(q2-q1, p2-q1) >= 0 else False)

5. 判断线段和直线是否相交

从标题即可看出,这里的直线和线段是不相同的;
如果线段 P 1 P 2   和直线 Q 1 Q 2   相交,则 P 1 P 2   跨立 Q 1 Q 2   ,根据4的结论:

(P 1 Q 1 )×(Q 2 Q 1 )(Q 2 Q 1 )×(P 2 ,Q 1 )\beq0 

6. 判断点是否在矩形内

只需判断该点的横纵坐标是否夹在矩形的左右边,上下边之间。
判断线段、折线、多边形是否在矩形中,因为矩形是个凸集,所以只要判断所有端点是否都在矩形中即可;

7. 判断矩形是否在矩形中

只要把比较左右边界和上下边界即可;

8. 判断圆是否在矩形中:

圆在矩形内的充要条件
圆心在矩形中且圆的半径小于等于圆心到矩形四条边的距离的最小值。

def circle_in_rect(c, r, lb, rt):
                # c: center
                # r: radius
                # lb: left bottom
                # rt: right top
    if ( ((center[0]<lb[0]) | (center[0]>rt[0])) | ((center[1]<lb[1])|(center[1]>rt[1])) ):
                # 圆心是否在矩形内
        return False
    return True r <= min(center[0]-lb[0], rt[0]-center[0], center[1]-lb[1], rt[1]-center[1]) if else False

9. 判断点是否在多边形中

References

[1] 计算几何常用算法

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: K-means聚类算法是一种常见的无监督学习算法,用于对数据进行聚类。在Python中,可以使用numpy库来实现K-means聚类算法。 以下是一个简单的示例代码: ```python import numpy as np def kmeans(X, K, max_iters): m, n = X.shape centroids = X[np.random.choice(m, K, replace=False), :] for i in range(max_iters): idx = np.argmin(np.sum((X - centroids[:, np.newaxis])**2, axis=2), axis=0) centroids = np.array([X[idx == k].mean(axis=0) for k in range(K)]) return centroids, idx # 测试代码 X = np.random.randn(100, 2) centroids, idx = kmeans(X, 3, 10) print(centroids) print(idx) ``` 在上面的代码中,`X`是数据集,`K`是聚类的数量,`max_iters`是最大迭代次数。函数`kmeans`返回聚类中心和每个数据点所属的聚类编号。 首先,随机选择K个数据点作为初始聚类中心。然后,重复以下步骤直到收敛: 1. 将每个数据点分配到最近的聚类中心。 2. 计算每个聚类的新中心位置。 最后,返回最终的聚类中心和每个数据点所属的聚类编号。 注意,这里的实现方式是比较简单的,并没有考虑到优化算法性能的问题。在实际应用中,可能需要使用更复杂的算法来提高算法效率。 ### 回答2: kmeans聚类算法是一种经典的无监督学习算法,主要用于将一组数据点划分到不同的类别中。它的原理是将数据点分为K个簇,每个簇通过计算数据点与其所属簇的中心之间的距离,将数据点分配给最近的簇。 在使用kmeans算法进行聚类时,可以使用numpy库来进行向量化计算,提高算法的效率。首先,我们可以使用numpy的数组来存储数据点,每个数据点可以表示为一个包含多个特征的一维数组。 在使用kmeans算法时,首先需要确定聚类的个数K,然后随机选择K个数据点作为初始的聚类中心。接下来,将每个数据点与聚类中心计算距离,并将其分配到与其最近的聚类中心所属的簇中。 在numpy中,可以使用欧氏距离公式来计算两个点之间的距离。通过numpy的广播功能,我们可以简洁地计算每个数据点与每个聚类中心的距离。 计算完距离后,可以根据距离来更新每个数据点所属的簇,将其分配到离其最近的聚类中心所属的簇中。 然后,更新每个簇的中心,方法是计算每个簇中所有数据点的平均值。通过numpy的sum和mean函数,可以方便地进行这一计算。 然后,重复以上两个步骤,直到达到终止条件,如迭代次数达到预设值或簇中心不再发生显著变化。 最后,通过numpy可以方便地将聚类结果进行可视化,将每个簇的数据点以不同的颜色进行展示。 总结来说,numpy库是一种强大的工具,可以在kmeans聚类算法中实现向量化计算,提高算法的效率,并能方便地进行聚类结果的可视化。 ### 回答3: k-means聚类算法是一种常用的无监督学习算法,通过将数据集划分为k个不同的簇,使得簇内的数据点相似度较高,簇间的数据点相似度较低。而numpy是一个Python中常用的科学计算库,在k-means聚类算法中可以很好地配合使用。 首先,在使用k-means算法前,我们需要准备一个包含特征数据的numpy数组。对于每个样本点,都有一个包含其特征的向量,这个特征向量可以是多维的。 然后,我们需要选择一个合适的k值,即簇的个数。根据选择的k值,我们可以使用numpy的rand()函数或者其他方法来初始化k个初始聚类中心。 接下来,我们通过计算每个样本点与各个聚类中心的距离,将样本划分给最近的聚类中心。这里,我们可以使用numpy的linalg.norm()函数来计算欧氏距离。 然后,我们根据每个簇内的样本点计算新的聚类中心,这个新的聚类中心将取簇内样本点的平均值。在numpy中,我们可以使用mean()函数来计算平均值。 之后,我们可以迭代地重复上述过程,直到聚类中心不再发生变化或者达到最大迭代次数。在numpy中,我们可以使用while循环或者其他方式来实现迭代过程。 最后,我们可以输出每个样本点所属的簇,并可视化结果。在numpy中,我们可以使用unique()函数来获得每个簇的唯一值,使用scatter()函数等方法来进行数据可视化。 综上所述,在使用k-means聚类算法时,可以借助numpy库中提供的函数和方法来实现算法的具体步骤,从而对数据集进行聚类分析。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五道口纳什

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值