python 最近对问题图像(分治法)

分治法解决最近对问题,按照x坐标分成两堆,mid是最大的x值的下标加上最小的x值的下标除以二,不断递归,直到只有两个或者三个点。两个点或者三个点能够直接处理。然后就是合并,先分别求出左右两堆的最近对,然后让较小的那个距离赋值给d, 以mid为中心,在mid-d和mid+d的范围内找点,因为只有在这个范围内才可能出现比d小的最近距离。找到了就更新d的值。

画图比较简单,画个散点图和一条直线就好了。

 

import math
import matplotlib.pyplot as plt
import numpy as np


class Point:
    def __init__(self):
        self.x = 0
        self.y = 0


class Apoint:
    def __init__(self):
        self.index = 0
        self.x = 0
        self.y = 0

#按照x坐标排序
def cmpx(a):
    return a.x


def cmpy(b):
    return b.y

#计算两点的距离
def dist(a, b):
    return math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y))


def closest(xx, yy, low, high, a, b, d):
    if (high - low) == 1:#只有两个点
        a = xx[low]
        b = xx[high]
        d = dist(a, b)
    elif (high - low) == 2:#有三个点
        dl = dist(xx[low], xx[low+1])
        dr = dist(xx[low], xx[high])
        d = dist(xx[low+1], xx[high])
        if dl < dr and dl < d:
            a = xx[low]
            b = xx[low+1]
            d = dl
        elif dr < d:
            a = xx[low]
            b = xx[high]
            d = dr
        else:
            a = xx[low+1]
            b = xx[high]
    else:#有多个,分成左右两堆
        sl = []
        sr = []
        temp = Apoint()
        mid = (low + high)//2
        #以mid为中心
        for i in range(high - low + 1):
            if yy[i].index <= mid:
                sl.append(yy[i])
            else:
                sr.append(yy[i])
        al, bl, dl = closest(xx, sl, low, mid, a, b, d)
        ar, br, dr = closest(xx, sr, mid+1, high, a, b, d)
        #分别计算左右两堆
        if dl < dr:
            a, b, d = al, bl, dl
        else:
            a, b, d = ar, br, dr
        z = []
        #判断左右两堆点,在mid-d到mid+d的范围内有没有两个以上的点。
        for i in range(high - low + 1):
            if math.fabs(xx[mid].x - yy[i].x) < d:
                temp = Point()
                temp.x = yy[i].x
                temp.y = yy[i].y
                z.append(temp)
        for i in range(len(z)):
            j = i + 1
            while j < len(z) and (z[j].y - z[i].y) < d:
                dz = dist(z[j], z[i])
                #判断是否比d小
                if dz < d:
                    a = z[i]
                    b = z[j]
                    d = dz
                j += 1
    return a, b, d

#画出图
def closest_pair(xx, n, a, b, d):
    if n < 2:
        print('n必须大于1')
        return -1
    else:
        xx.sort(key=cmpx)
        yy = []
        for i in range(n):
            temp = Apoint()
            temp.index = i
            temp.x = xx[i].x
            temp.y = xx[i].y
            yy.append(temp)
        yy.sort(key=cmpy)
        a, b, d = closest(xx, yy, 0, n-1, a, b, d)
        return a, b, d


n = int(input())
arr = []
a = Point()
b = Point()
d = 0.0
for i in range(n):
    t = Point()
    t.x = np.random.randint(1, 1000)
    t.y = np.random.randint(1, 1000)
    arr.append(t)
a, b, d = closest_pair(arr, n, a, b, d)
for p in arr:
    plt.scatter(p.x, p.y, marker='o', color='r')
plt.plot([a.x, b.x], [a.y, b.y], c='g')
plt.show()


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值