分治法:二维最近点对问题

大家好,我是连人。本期讲解二维最近点对的问题。

一维点对问题十分简单,在此不再赘述,大致就如同二分法般不断将区域分为两段。

假设区域被分为A以左和B以右,A以左的最近距离d1B以右的最近距离d2,以及A与B的距离d3取最小值,即为这片区域的最近距离。

二维最近点对问题

首先,将点集按x从小到大排好,这一步的目的是使分区合理。

在这里插入图片描述
这个图中一共有十个点,根据分治策略,分为两个区,每个区五个点。
在这里插入图片描述
这是我看的无论是课本还是别的都画的是这条线,实际上在代码实现的时候,是这样分的。

在这里插入图片描述

在二维最近点对问题中,“A与B之间的距离d3”发生了些许变化。在一维点对问题中,A区域与B区域的最近距离一定是A与B之间的距离,而在二维问题中,情况会稍微不同,例如下图。
在这里插入图片描述
显然,这张图中,A区域与B区域最近的距离不是a与b而是a与c。

为了避免这种情况,我们将满足条件的点再次进行取最近点对算法,作为“A与B的距离d3”。

而这个条件则是:取d1与d2的最小距离d。A,B区域中所有水平距离与中点相距d以内的点。

原理就是如此,下面介绍代码部分。

首先使用归并排序将点集按顺序排序,之后放入分治算法中。

我的函数中,返回左侧点位,右侧点位和距离三个值。

# -*- coding:utf-8 -*-
import math
import random


def generate_point():
    points = []
    points_num = random.randint(1, 20)
    for i in range(points_num):
        x = random.randint(-50, 50)
        y = random.randint(-50, 50)
        points.append([x, y])
    return points


def merge(points, start, mid, end):
    temp = []
    i = start
    j = mid + 1
    while i < mid + 1 and j < end + 1:
        if points[i][0] > points[j][0]:
            temp.append(points[j])
            j += 1
        else:
            temp.append(points[i])
            i += 1
    while i < mid + 1:
        temp.append(points[i])
        i += 1
    while j < end + 1:
        temp.append(points[j])
        j += 1
    points[start:end + 1] = temp                    # 切片是左闭右开,实际不包含第end+1点


def merge_sort(points, start, end):
    if start < end:
        mid = start + (end - start) // 2
        merge_sort(points, start, mid)
        merge_sort(points, mid + 1, end)
        merge(points, start, mid, end)


def distance(p1, p2):
    x = math.fabs(p1[0] - p2[0])
    y = math.fabs(p1[1] - p2[1])
    return math.sqrt(x*x + y*y)


def get_closest_distance(points, l, r):
    if r <= l:                          # 报错或只有一个点,返回无限大
        return [0, 0, 65535]
    if r - l == 1:                          # 只有两个点直接求解
        return [l, r, distance(points[l], points[r])]
    if r - l == 2:                          # 三个点时使用比较的方法
        d1 = distance(points[l], points[l+1])
        d2 = distance(points[l+1], points[r])
        d3 = distance(points[l], points[r])
        if d1 <= d2 and d1 <= d3:
            return [l, l+1, d1]
        elif d2 <= d1 and d2 <= d3:
            return [l+1, r, d2]
        else:
            return [l, r, d3]
# 四个点以上使用分治法
    m = l + (r - l) // 2
    result1 = get_closest_distance(points, l, m)
    result2 = get_closest_distance(points, m+1, r)
    d = min(result1[2], result2[2])
    # 寻找m左侧和右侧范围为d内的所有点
    i = m
    j = m
    while i >= l:                               # 寻找左侧的值
        if points[m][0] - points[i][0] < d:
            i -= 1
        else:
            break
    while j <= r:                               # 寻找右侧的值
        if points[j][0] - points[m][0] < d:
            j += 1
        else:
            break
    if i < m:              # 如果m左侧没有符合条件的点了,此时i会停留在m,如果有,此时i会在最左侧的点之前一位,所以+1
        i += 1
    result3 = [0, 0, 65535]
    for a in range(i, m+1):                     # range()左闭右开,我们需要m点作为左侧的点
        for b in range(m+1, j):                 # j此时是右侧中最右边符合条件的点的位置+1,因为range,所以不包含j
            d0 = distance(points[a], points[b])
            if d0 < result3[2]:
                result3 = [a, b, d0]
    if result1[2] <= result2[2] and result1[2] <= result3[2]:
        return result1
    elif result2[2] <= result1[2] and result2[2] <= result3[2]:
        return result2
    else:
        return result3


p = generate_point()
print("随机生成点:")
print(p)
merge_sort(p, 0, len(p)-1)
print("归并排序:")
print(p)
result = get_closest_distance(p, 0, len(p)-1)
print("最近的两个点:" + str(p[result[0]]) + ", " + str(p[result[1]]))
print("距离为" + str(result[2]))

转载注明出处。

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 分治法是把一个复杂的问题分解成若干个相对简单的子问题,递归地求解子问题,而动态规划则是通过把原问题分解成若干个子问题,并存储子问题的答案,从而获得原问题的答案。两者的不同之处在于,分治法适用于求解最优解,而动态规划则是最优子结构和子问题重叠的情况。 ### 回答2: 分治法和动态规划法都是常用的算法设计方法,它们之间有一些明显的异同。 首先,分治法和动态规划法的相似之处在于它们都使用了递归的思想。两种方法都将原问题分解成若干个子问题,并通过对子问题的求解来得到原问题的解。因此,它们都具有相同的时间复杂度,通常为O(n^2)或O(2^n)。 然而,两种方法的不同之处在于它们对子问题的处理方式。分治法通过将问题划分成彼此相互独立的子问题,并将子问题的解合并起来得到原问题的解。每个子问题的解只需计算一次,然后进行合并,避免了重复计算,从而提高了算法的效率。而动态规划法则将问题划分成依赖关系的子问题,并使用一个表格来记录每个子问题的解,以避免重复计算。动态规划法利用了子问题之间的重叠性质,通过填表的方式逐步求解子问题,并最终得到原问题的解。 此外,分治法和动态规划法在设计思路上也有所不同。分治法通常通过递归的方式将问题划分,然后使用多个递归函数进行求解。每个递归函数的输入和输出都是问题的一部分。而动态规划法则侧重于自底向上的求解方法,它将问题划分为子问题,并使用迭代的方式逐步求解。动态规划法通常使用一维或二维数组来记录中间结果,以实现时间和空间的优化。 总的来说,分治法和动态规划法都是重要的算法设计思想,它们在解决问题时有各自的优势和适用范围。分治法适用于问题可以划分为独立子问题,并且问题的子问题间没有重叠的情况。而动态规划法适用于问题的子问题具有重叠性质,并且需要使用表格记录中间结果。 ### 回答3: 分治法和动态规划法是两种常用的算法设计方法,它们有一些相似之处,也有一些不同之处。 首先,分治法和动态规划法的相似在于都将原问题分解为几个子问题,并通过求解子问题来最终求解原问题。它们都是将复杂的问题简化为更小规模的子问题进行求解,然后再将子问题的解合并起来得到原问题的解。 其次,分治法和动态规划法的不同在于它们对子问题的求解方式不同。分治法将原问题划分为互不相交的子问题,每个子问题独立求解,并将每个子问题的解合并起来得到原问题的解。而动态规划法则将原问题划分为重叠的子问题,通过存储子问题的解并重复利用,避免重复计算,从而提高算法的效率。 另外,动态规划法还具有最优子结构的特,即原问题的最优解可以由子问题的最优解通过递推关系得到。这使得动态规划法在求解最优化问题时比分治法更加高效。 在应用上,分治法常用于解决可拆分为多个相似子问题问题,如求解大规模矩阵的乘法、排序等。而动态规划法常用于求解具有最优子结构的问题,如求解背包问题、最长公共子序列等。 总而言之,分治法和动态规划法都是解决复杂问题的有效方法,它们在问题分解和求解方式上略有不同,因此在具体应用中根据问题的性质和特选择合适的方法能够达到更好的效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值