最近点对问题

最近点对问题

代码清单如下:

文件名

定位

作用

tool.py

工具模块

提供计算欧式距离、平方欧式距离、生成一定范围随机数等函数

point.py

类模块

提供Point点类和生成随机点的函数

algorithm.py

算法模块

利用分治法和穷举法,求解最近点对问题

draw.py

绘图模块

画出点集分布和最近点对

main.py

主模块

程序入口

代码:

tool.py:工具模块

# -*- coding:utf-8 -*-



import random

import math



#  生成_min到_max之间的随机数

def random_double(_min, _max):

    return _min + random.random() * (_max - _min)



# 计算欧式距离

def distance(x1, y1, x2, y2):

    return math.sqrt(square_distance(x1, y1, x2, y2))



# 计算平方欧式距离

def square_distance(x1, y1, x2, y2):

    return (x1 - x2) ** 2 + (y1 - y2) ** 2

point.py:类模块

# -*- coding:utf-8 -*-



from tool import distance, square_distance, random_double



# 点类

class Point:

    def __init__(self, x, y):

        self.x = x

        self.y = y



    def __str__(self):

        return "( %.2f, %.2f)" % (self.x, self.y)



    def __repr__(self):

        return "( %.2f, %.2f)" % (self.x, self.y)



    def distance(self, point):

        return distance(self.x, self.y, point.x, point.y)



    def square_distance(self, point):

        return square_distance(self.x, self.y, point.x, point.y)



    def list(self):

        return [self.x, self.y]



# 生成随机点

def random_point(_min, _max):

    x = random_double(_min, _max)

    y = random_double(_min, _max)

    return Point(x, y)

algorithm.py:算法模块

# -*- coding:utf-8 -*-

# 穷举法,求最近点对

def exhaustion_min_pair(points):

    point_num = len(points)

    min_dis = float('inf')

    _min_i = -1

    _min_j = -1

    for i in range(point_num - 1):

        for j in range(i + 1, point_num):

            dis = points[i].distance(points[j])

            if dis < min_dis:

                min_dis = dis

                _min_i = i

                _min_j = j

    return [_min_i, _min_j]

# 计算两点的距离

def cal_dis(points):

    return points[0].distance(points[1])

# 生成器:生成横跨跨两个点集的候选点

def candidate_dot(u, right, dis, med_x):

    # 遍历right(已按横坐标升序排序)。

    # 若横坐标小于med_x-dis则进入下一次循环;

    # 若横坐标大于med_x+dis则跳出循环;

    # 若点的纵坐标好是否落在在[u.y-dis,u.y+dis],则返回这个点

    for point in right:

        if point.x < med_x - dis:

            continue

        if point.x > med_x + dis:

            break

        if u.y - dis <= point.y <= u.y + dis:

            yield point

# 求出横跨两个部分的点的最小距离

def combine(left, right, res_min, med_x):

    # res_min[1] 左或右部分最小值

    # res_min[0] 存放最近点对[(x1,y1), (x2, y2)]

    dis = res_min[1]

    min_dis = res_min[1]

    pair = res_min[0]

    for point in left:

        if point.x < med_x - dis:

            continue

        for v in candidate_dot(point, right, dis, med_x):

            dis = cal_dis([point, v])

            if dis < min_dis:

                min_dis = dis

                pair = [point, v]

    return [pair, min_dis]

def divide(points):

    # 长度

    length = len(points)

    if length <= 1:

        # 只有一个点

        return [None, float('inf')]

    elif length == 2:

        # 只有两个点

        return [points, cal_dis(points)]

    else:

        # 按x坐标排序

        points = sorted(points, key=lambda p: p.x)

        # 长度一半

        half = length // 2

        # 求中间x坐标

        med_x = (points[half].x + points[-half - 1].x) / 2

        # 左部分

        left = points[:half]

        res_left = divide(left)

        # 右部分

        right = points[half:]

        res_right = divide(right)

        # 获取两集合中距离最短的点对

        if res_left[1] < res_right[1]:

            # 左边比右边短

            # 从左边开始,找到横跨两个部分的点的最小距离

            res_min = combine(left, right, res_left, med_x)

        else:

            # 右边比左边短

            # 从右边开始,找到横跨两个部分的点的最小距离

            res_min = combine(left, right, res_right, med_x)

        return res_min

draw.py:绘图模块

# -*- coding:utf-8 -*-



from algorithm import divide

import matplotlib.pyplot as plt



# 画出最近点对

def draw_min_pair(points):

    point_num = len(points)

    # 求出一个矩形将所有点都包括的矩形

    min_x = min(points, key=lambda p: p.x).x

    max_x = max(points, key=lambda p: p.x).x

    min_y = min(points, key=lambda p: p.y).y

    max_y = max(points, key=lambda p: p.y).y

    # 适当扩大范围

    min_x = int(min_x - 1)

    max_x = int(max_x + 1)

    min_y = int(min_y - 1)

    max_y = int(max_y + 1)

    # 设定X、Y轴范围

    plt.xlim(min_x, max_x)

    plt.ylim(min_y, max_y)

    # 显示网格线

    plt.grid()

    # 添加X、Y轴描述

    plt.xlabel('X')

    plt.ylabel('Y')

    # 画出散点图

    for i in range(point_num):

        point = points[i]

        plt.scatter(point.x, point.y)

    # 利用分治法,获取最近点对

    p1, p2 = divide(points)[0]

    # 设置标题

    plt.title("Recent point pair:%s, %s" % (p1, p2))

    # 将最近点对连线

    plt.plot([p1.x, p2.x], [p1.y, p2.y], color='red', linestyle='--')

    # 显示图像

    plt.show()

main.py:主模块

# -*- coding: utf-8 -*-



from point import random_point

from draw import draw_min_pair

from algorithm import divide



MIN = 1

MAX = 10

POINT_NUM = 5



if __name__ == '__main__':

    ps = [random_point(MIN, MAX) for i in range(POINT_NUM)]

    data = divide(ps)

    # 输出

    print('随机生成%d个点,分别为:' % POINT_NUM)

    for p in ps:

        print(p, end='')

    # 打印最近点对信息

    print('\n最近点对为:')

    print(data[0][0], data[0][1])

    print('最近距离为:')

    print(data[1])

    # 绘图

    draw_min_pair(ps)

运行结果如下:

 

 

 

绘图结果:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值