最接近点对问题

文章提供了两个Python函数,分别使用递归和分治策略解决一维和二维空间中最接近点对的问题。一维问题通过排序和比较中位数来找到最近点对。二维问题在X轴和Y轴上进行排序,然后通过分治法计算最接近的点对,涉及到了对Y坐标重新排序和筛选的过程。
摘要由CSDN通过智能技术生成

一维最接近点问题

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/1/5 20:36
# @Author  : Lili
# @File    : 一维最接近点对问题.py
# @Description : 递归与分治,来探索二维中最接近点对问题的求解

from utils import lp_wrapper

# 统计运行时间的注解
@lp_wrapper()
def cpair1(S: list) -> (bool, float):
    """
    递归分治求解一维最接近点对问题

    :param S: 待求解点集合
    :param d: 最接近两点的距离
    :return: 成功与否
    """
    if len(S) < 3:
        d = float("inf")
        return False, d
    # mid: S中各点坐标的中位数位置
    mid = int(len(S)/2)
    S.sort()
    # print("中位数mid: ", mid)
    S1 = S[:mid]
    S2 = S[mid:]
    # print(S1, S2)
    d1 = cpair1(S1)[1]
    d2 = cpair1(S2)[1]
    # print("S1, d1", S1, d1)
    # print("S2, d2", S2, d2)
    return True, min(d1, d2, S[mid + 1] - S[mid])

二维最接近点问题

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/1/5 20:36
# @Author  : Lili
# @File    : 二维最接近点对问题.py, closest中对y的重构出错
# @Description :
from math import hypot
import sys
from random import randint

sys.setrecursionlimit(2400)

ID, X, Y = 0, 1, 2


def getx(point):
    return point[X]


def gety(point):
    return point[Y]


def distance(pointa, pointb):
    return hypot(pointa[X]-pointb[X], pointa[Y]-pointb[Y])


def closest(point_x, point_y, point_z, l, r):
    """
    计算最接近点对

    :param point_x: 原列表,按x坐标升序
    :param point_y: 按y坐标升序
    :param point_z: 同point_y用于存放分治后点集以及存放筛选后d矩形内的点集
    :param l: 起始位置
    :param r: 终止位置
    :return: (point_a, point_b, min_distance)
    """
    # 包含2点的情况
    if r-l == 1:
        return point_x[l], point_x[r], distance(point_x[l], point_x[r])
    elif r-l == 2:
        d1 = distance(point_x[l], point_x[l+1])
        d2 = distance(point_x[r], point_x[l+1])
        d3 = distance(point_x[l], point_x[r])
        if d1 <= d2 and d1 <= d3:
            return point_x[l], point_x[l+1], d1
        elif d2 <= d3:
            return point_x[r], point_x[l + 1], d2
        else:
            return point_x[l], point_x[r], d3
    # 分治法, 按照x坐标等分为左右两部分
    py_temp = point_y[l:r+1]
    pz_temp = point_z[l:r+1]
    mid = int((l+r)/2)
    f, g = l, mid + 1

    for py in point_y[l:r+1]:
        if py[ID] > mid:
            point_z[g] = py
            g += 1
        else:
            point_z[f] = py
            f += 1
    point_a, point_b, min_distance = closest(point_x, point_z, point_y, l, mid)
    ar, br, dr = closest(point_x, point_z, point_y, mid+1, r)
    if min_distance > dr:
        point_a, point_b = ar, br
        min_distance = dr
    # merge(z,y,l,m,r)
    point_y[l:r+1] = point_z[l:r+1]
    # point_y[l:r+1] = py_temp
    # point_z[l:r+1] = pz_temp
    k = l
    for i in range(l, r+1):
        if abs(point_y[mid][X]-point_y[i][X]) < min_distance:
            point_z[k] = point_y[i]
            k += 1
    for i in range(l, k):
        for j in range(i+1, k):
            if abs(point_z[j][Y]-point_z[i][Y]) >= min_distance:
                break
            dp = distance(point_z[i], point_z[j])
            if dp < min_distance:
                min_distance = dp
                point_a = point_x[point_z[i][ID]]
                point_b = point_x[point_z[j][ID]]
    return point_a, point_b, min_distance


def cpair2(point: [[]]):
    """
    递归与分治,求解二维最接近点问题算法

    :param point: [[id, x, y], ]点集列表,id: int
    :return: (bool, min_distance), 返回执行结果和最小距离值
    """
    min_dis = float("inf")
    if len(point) < 2:
        return False, None, None, min_dis
    point.sort(key=getx)
    point_x = point[:]
    i = 0
    for p in point_x:
        p[ID] = i
        i += 1
    point_y = point_x[:]
    point_y.sort(key=gety)
    point_z = point_y[:]
    point_a, point_b, min_dis = closest(point_x, point_y, point_z, 0, len(point)-1)
    del point_y
    del point_z
    return True, point_a, point_b, min_dis


# 测试
LEN = 20
BEGIN, END = 0, int(LEN*10)

points = []
for i in range(0, LEN):
    p = [i, randint(BEGIN, END), randint(BEGIN, END)]
    points.append(p)

print(points)
d, p1, p2 = float("inf"), 0, 0
for i in range(0, LEN):
    for j in range(i+1, LEN):
        dis = hypot(points[i][1]-points[j][1], points[i][2]-points[j][2])
        if dis < d:
            d = dis
            p1 = i
            p2 = j

print(d, points[p1], points[p2])
t = cpair2(points)[1:]
print(t[2], t[0], t[1])

# 错误项
# [[0, 69, 109], [1, 75, 128], [2, 87, 185], [3, 121, 107], [4, 152, 158], [5, 103, 42], [6, 145, 110], [7, 21, 122], [8, 176, 33], [9, 144, 121], [10, 194, 74], [11, 193, 22], [12, 110, 89], [13, 32, 61], [14, 135, 85], [15, 36, 5], [16, 138, 170], [17, 121, 141], [18, 55, 137], [19, 73, 15]]
# 11.045361017187261 [6, 145, 110] [9, 144, 121]
# 21.095023109728988 [9, 110, 89] [10, 121, 107]

# [[0, 59, 135], [1, 49, 96], [2, 16, 43], [3, 143, 131], [4, 192, 94], [5, 126, 82], [6, 66, 154], [7, 43, 154], [8, 71, 186], [9, 75, 173], [10, 161, 67], [11, 23, 54], [12, 6, 32], [13, 87, 159], [14, 124, 84], [15, 90, 128], [16, 36, 49], [17, 128, 156], [18, 70, 179], [19, 63, 75]]
# 2.8284271247461903 [5, 126, 82] [14, 124, 84]
# 7.0710678118654755 [9, 70, 179] [10, 71, 186]

# [[0, 171, 55], [1, 11, 133], [2, 6, 62], [3, 164, 193], [4, 127, 180], [5, 33, 152], [6, 188, 65], [7, 5, 78], [8, 161, 148], [9, 47, 16], [10, 68, 102], [11, 20, 62], [12, 78, 8], [13, 155, 182], [14, 1, 159], [15, 50, 119], [16, 187, 21], [17, 134, 80], [18, 174, 72], [19, 171, 105]]
# 14.0 [2, 6, 62] [11, 20, 62]
# 16.0312195418814 [2, 6, 62] [1, 5, 78]

# [[0, 34, 14], [1, 172, 198], [2, 150, 102], [3, 126, 15], [4, 66, 79], [5, 165, 97], [6, 38, 75], [7, 195, 158], [8, 195, 173], [9, 38, 91], [10, 146, 164], [11, 163, 84], [12, 19, 64], [13, 103, 139], [14, 116, 7], [15, 20, 3], [16, 110, 196], [17, 108, 130], [18, 154, 160], [19, 109, 6]]
# 7.0710678118654755 [14, 116, 7] [19, 109, 6]
# 10.295630140987 [7, 108, 130] [6, 103, 139]

二维算出来有问题,line70: # merge(z,y,l,m,r)这块重构point_y没看懂,《计算机算法设计与分析(王晓东)》p35

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值