分治法解决最近点对问题,python实现

自己对照《算法分析与设计》书上的伪代码用python实现的,个人原创,欢迎指导借鉴。

书本伪代码:

 

不多说,直接上干货:

import math

# 求两点之间距离的函数
def distance(a, b):
    return math.sqrt((b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2)

# 蛮力法求一组数据中最近的两点(处理点的个数小于等于3的情形)
def manli_nearest_points(points):
    n = len(points)
    dmin = distance(points[0], points[1])
    p1 = points[0]
    p2 = points[1]
    for i in range (0,n-1):
        for j in range (i+1,n):
            if dmin > distance(points[i], points[j]):
                dmin = distance(points[i], points[j])
                p1 = points[i]
                p2 = points[j]
    return p1, p2, dmin

# 对一组点按照x坐标的升序排列
def paixu_x(points):
    n = len(points)
    for i in range (0, n-1):
        for j in range (0, n-1-i):
            if points[i][0] > points[i + 1][0]:
                tp = points[i]
                points[i] = points[i + 1]
                points[i + 1] = tp
    return points

# 对一组点按照y坐标的升序排列
def paixu_y(points):
    n = len(points)
    for i in range (0, n-1):
        for j in range (0, n-1-i):
            if points[i][1] > points[i + 1][1]:
                tp = points[i]
                points[i] = points[i + 1]
                points[i + 1] = tp
    return points


# 分治法求解最近点对
def efficient_closest_pair(p,q):
    n = len(p)
    if n <= 3:
        x1, x2, dmin = manli_nearest_points(p)  # 点集中的点的个数小于3的时候直接用蛮力法求
    else:
        pl = {}  # 创建空的点集用来保存点
        ql = {}
        pr = {}
        qr = {}
        s = {}
        for i in range(0, int(n/2)):  # 将p和q的前n/2个点复制到pl和ql
            pl[i] = p[i]
            ql[i] = q[i]
        for i in range(int(n/2),n):   # 将p和q中余下的点复制到pr和qr
            pr[i-int(n/2)] = p[i]
            qr[i-int(n/2)] = q[i]
        xl1, xl2, dl = efficient_closest_pair(pl, ql)  # 递归调用分治法,保存左边内部的最近点对和最近距离
        xr1, xr2, dr = efficient_closest_pair(pr, qr)  # 递归调用分治法,保存右边内部的最近点对和最近距离
        if dl <= dr:     # 比较左右两边的最近距离,保存较小者和对应的点对
            x1, x2, dmin = xl1, xl2, dl
        else:
            x1, x2, dmin = xr1, xr2, dr
        m = p[int(n/2)][0]  # 取中线
        num = 0   # 用来记录中线两端距离为dmin内的点的个数
        for i in range (0, n):   # 保存中线两端距离为dmin内的点到s集合中
            if abs(q[i][0] - m) < dmin:
                s[num] = q[i]
                num = num + 1
        dminsq = dmin**2
        for i in range (0, num-1):   # 遍历s,寻找其中的最近点对
            k = i + 1
            while k <= num - 1 and (s[k][1]-s[i][1])**2 < dminsq: # 对s内的点,只需要比较s点上下dmin距离内的点
                t = (s[k][0]-s[i][0])**2+(s[k][1]-s[i][1])**2
                if t < dminsq:
                    x1, x2, dmin = s[i], s[k], math.sqrt(t)
                k = k + 1
    return x1, x2, dmin

def main():
    points = [(1, 1), (2, 3), (3, 4), (4, 5), (5, 6), (6, 4), (7, 3), (8, 1), (5, 1), (10, 8),
              (20, 8), (8, 7), (8, 8), (18, 4), (2, 5), (6, 10)]
    p = paixu_x(points)  # 按照分治法的要求对点集进行排序
    q = paixu_y(points)
    p1, p2, dmin = efficient_closest_pair(p,q)  # 将排序好的点集输入分治法函数
    print("最近的两个点的坐标为:", p1, p2, "最近距离为:", dmin)

if __name__ == "__main__":
    main()

  • 5
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值