最近点对问题
代码清单如下:
文件名 | 定位 | 作用 |
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)
运行结果如下:
绘图结果: