最近点对问题(python实现)

最近用python比较多,想要用python写出一个最近点对查询算法,结果网上找不到,于是自己写了一个,发在这里是供自己以后参考,也是与大家共享,具体思路已经写在代码里,自测是通过的,如果有什么问题,希望大家能够指出来。

'''
最小点对算法:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小。严格地说,最接近点对可能多于1对。为了简单起见,这里只限于找其中的一对。
主要算法思想:先把所有点按照x值排序,从最中间(x值中位数b)将所有点分为三个部分,左子域(左子域包括中间子域)、右子域、x值恰好等于中位数的中间子域,分别找出左右子域中的最近点,取最小值为d,
再将左右子域的x值范围缩减到[b-d,b+d]之间,循环遍历左子域中的每个点,在右子域中寻找y值离当前点最近的四个点,分别求距离,取最小距离,然后寻找中间子域中的最近点,取最小距离
通过递归(分治)的方法,最终正确查找到最近点对
'''
import math
# 输入
p = [[0,0], [1,1],[3,4],[0,3],[3.2, 4.2], [0,-1],[-2,-2],[-1,-2],[0, 0.4], [-1,2],[0,2],[0.5,2]]
# p = [[0,0], [1,1], [3,4]]
n = len(p)
# 预期输出:最近点对是 ( 3.2 , 4.2 ) 和 ( 3 , 4 ), 最短距离为 0.2828427124746193

# 查找最接近的目标的y下标
def find_closed_index(collections, target):
    n = len(collections)
    low = 0
    high = n - 1
    mid = 0
    while low <= high:
        mid = math.floor((low + high) / 2)
        if target > collections[mid][1]:
            low = mid + 1
        elif target < collections[mid][1]:
            high = mid - 1
        else:
            return mid

    return mid

def distance(p1, p2):
    return math.sqrt(pow(p1[0] - p2[0], 2) + pow(p1[1] - p2[1], 2))

def divide(l, r, res):
    if l == r:
        return float("inf"), []
    if (l + 1) == r:
        return distance(p[l], p[r]), [p[l], p[r]]
    mid = math.floor((l + r) / 2)
    d1, res1 = divide(l, mid, res)
    d2, res2 = divide(mid + 1, r, res)
    if d1 >= d2:
        d = d2
        res = res2
    else:
        d = d1
        res = res1

    # 先将所有左子域、右子域、同一x值的中间子域,删选出来
    left = []
    right = []
    midd = []
    b = p[mid][0]
    bl = b - d
    br = b + d
    for i in range(n):
        if ((p[i][0] >= bl) & (p[i][0] <= b)) == True:
            left.append(p[i])
            if p[i][0] == b:
                midd.append(p[i])
        elif ((p[i][0] <= br) & (p[i][0] >= b)) == True:
            right.append(p[i])


    if len(right) == 0:
        return d, res

    # 将右子域先按照y值大小排好序
    right.sort(key=lambda x:x[1])

    # 遍历左子域中的每一个点,在右子域中寻找y值最邻近的四个点,求出最小距离以及最近点对
    for i in range(len(left)):
        closed_point = []
        right_num = len(right)
        if right_num <= 4:
            closed_point = right
        else:
            index = find_closed_index(right, left[i][1])
            if index >= 4:
                start = index - 4
            else:
                start = 0
            if index + 5 > len(right) - 1:
                end = len(right) - 1
            else:
                end = index + 5

            for j in range(start, end):
                    closed_point.append(right[j])

            # 前四个就是右子域中离左子域最近的四个点
            closed_point.sort(key=lambda x: abs(x[1] - left[i][1]))

        if len(closed_point) >= 4:
            end2 = 4
        else:
            end2 = len(closed_point)

        for k in range(0, end2):
            dist = distance(closed_point[k], left[i])
            if dist < d:
                res = [closed_point[k], left[i]]
                d = dist

    # 再在中间子域的内部进行比较,看看有没有x值相同,且距离最近的两个点
    if len(midd) > 1:
        midd.sort()
        for j in range(len(midd) - 1):
            dist = distance(midd[j], midd[j + 1])
            if dist < d:
                res = [midd[j], midd[j + 1]]
                d = dist

    return d, res

# 前期需要按照x值排序
p.sort()
d, res = divide(0, n-1, [])
print("最近点对是 (", res[0][0], ",", res[0][1], ") 和 (", res[1][0], ",", res[1][1], "), 最短距离为", d)

  • 10
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我有明珠一颗

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

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

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

打赏作者

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

抵扣说明:

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

余额充值