分治暴力求解最近点对问题 + 时间性能量化分析

news2025/1/13 6:32:31

Catalogue

    • 1 Intro
    • 2 Problem
    • 3 Time performance analysis
    • 4 Solution
    • 5 Reference

1 Intro

本文旨在讨论分治和暴力在求解最近点对问题时的时间性能问题,关于解题部分不做过多讲解,只附上相关代码。

2 Problem

给定平面上N个点,找出其中的一对点的距离,使得在这N个点的所有点的对中,该距离为所有点对中最小的。

3 Time performance analysis

测试围绕模拟次数和集合S的规模大小两个维度进行展开进行测试,并分析时间复杂度。

其中集合S存放所有的点信息,S的规模大小即为点的个数

A. 当S大小为100时,采用100次和1000次模拟情况,以程序稳定性、单次耗时和总耗时为评价指标
a.蛮力法
在经过100次和1000次随机测试之后,可以发现大多数的程序单次耗时稳定在0.004s到0.005s之内。

image.png

图为100次len(S)=100模拟测试

image.png

图为1000次len(S)=100模拟测试

b. 分治法
在经过100次和1000次随机测试之后可以看出,大多数测试用例的单次耗时维持在0.01s~0.014s之间,但从总体的稳定性不如蛮力法,可以看出不同类型的测试用例对分治法的影响大于对蛮力法的影响
从平均耗时可以看出,在S=100的数量级下,分治法的效果不如蛮力法。
image.png

图为100次len(S)=100模拟测试

image.png

图为1000次len(S)=100模拟测试

B. 当模拟次数固定为100时,采用len(S)=100和len(S)=1000进行模拟,以程序稳定性、单次耗时和总耗时为评价指标。
a.分治法
在这里插入图片描述

b.蛮力法
可以发现,在集合S由100变为1000之后,总体耗时增加了,最低耗时由之前的0.004s变为现在的0.44s,时间上的稳定性也下降了,但总体维持在合理的耗时区间内,较为稳定。
在这里插入图片描述

图为100次len(S)=1000模拟测试

C. 时间复杂度分析
蛮力法
蛮力法需要两边for循环,因此时间复杂度为O(n^2),在实际模拟验证的情况下也确实如此,如下图所示。
image.png

图为使用蛮力法的时间复杂度

分治法
在程序的分治过程中,采用了O(nlogn)时间,程序中部分地方为预排序时间,不算在算法中。当n>3时满足递归方程 T(n) = 2T(n/2) + O(n),因此,有T(n)=O(nlogn)

事实上上面是理论分析,下面展现实际运行情况。

下图为1000次模拟时的分治法运行情况,说实话,我用了几种函数,并不能很好的拟合这种情况,原因有二:其一,测试规模过小,只有1000,但是事实上本人的计算机跑了1000次也花了几分钟时间,较为耗时,有兴趣的朋友可以尝试扩大数据的规模进一步的测试;其二,在测试规模没有达到一定程度时,时间复杂度最高阶部分的耗时并不能显著的体现出来,这也就是为什么这个曲线看起来奇奇怪怪的原因了。

在这里插入图片描述

笔者在这里并没有做深入的时间性能分析,如果大家有更好的想法,欢迎提出来,想要分析部分的全部代码可以私聊笔者。

4 Solution

直接附上求解代码

分治法
代码如下所示:

import math
import time

class SolutionByDivide:
    def find_nearest_direction(self, p: list):
        start_time = time.time()
        n = len(p)
        p.sort()
        min_dir, min_p = divide(0, n - 1, [], p, n)

        return min_dir, min_p, time.time() - start_time

# 查找最接近的目标的y下标
def find_closed_index(collections, target):
    n = len(collections)
    low = 0
    high = n - 1
    mid = 0
    while low <= high:
        mid = math.floor((low + high) / 2)
        if target > collections[mid][1]:
            low = mid + 1
        elif target < collections[mid][1]:
            high = mid - 1
        else:
            return mid
    return mid

def distance(p1, p2):
    return math.sqrt(pow(p1[0] - p2[0], 2) + pow(p1[1] - p2[1], 2))


def divide(l, r, res, p, n):
    if l == r:
        return float("inf"), []
    if (l + 1) == r:
        return distance(p[l], p[r]), [p[l], p[r]]
    mid = math.floor((l + r) / 2)
    d1, res1 = divide(l, mid, res, p, n)
    d2, res2 = divide(mid + 1, r, res, p, n)
    if d1 >= d2:
        d = d2
        res = res2
    else:
        d = d1
        res = res1

    # 先将所有左子域、右子域、同一x值的中间子域,删选出来
    left = []
    right = []
    midd = []
    b = p[mid][0]
    bl = b - d
    br = b + d
    for i in range(n):
        if ((p[i][0] >= bl) & (p[i][0] <= b)) == True:
            left.append(p[i])
            if p[i][0] == b:
                midd.append(p[i])
        elif ((p[i][0] <= br) & (p[i][0] >= b)) == True:
            right.append(p[i])

    if len(right) == 0:
        return d, res

    # 将右子域先按照y值大小排好序
    right.sort(key=lambda x: x[1])

    # 遍历左子域中的每一个点,在右子域中寻找y值最邻近的四个点,求出最小距离以及最近点对
    for i in range(len(left)):
        closed_point = []
        right_num = len(right)
        if right_num <= 4:
            closed_point = right
        else:
            index = find_closed_index(right, left[i][1])
            if index >= 4:
                start = index - 4
            else:
                start = 0
            if index + 5 > len(right) - 1:
                end = len(right) - 1
            else:
                end = index + 5

            for j in range(start, end):
                closed_point.append(right[j])

            # 前四个就是右子域中离左子域最近的四个点
            closed_point.sort(key=lambda x: abs(x[1] - left[i][1]))

        if len(closed_point) >= 4:
            end2 = 4
        else:
            end2 = len(closed_point)

        for k in range(0, end2):
            dist = distance(closed_point[k], left[i])
            if dist < d:
                res = [closed_point[k], left[i]]
                d = dist

    # 再在中间子域的内部进行比较,看看有没有x值相同,且距离最近的两个点
    if len(midd) > 1:
        midd.sort()
        for j in range(len(midd) - 1):
            dist = distance(midd[j], midd[j + 1])
            if dist < d:
                res = [midd[j], midd[j + 1]]
                d = dist

    return d, res

def single_test(ins):
    p = [[0, 0], [1, 1], [3, 4], [0, 3], [3.2, 4.2], [0, -1], [-2, -2], [-1, -2], [0, 0.4], [-1, 2], [0, 2], [0.5, 2]]
    min_dir, min_p, cost_time = ins.find_nearest_direction(p)
    print('距离最小两个点为:', min_p[0], min_p[1])
    print('距离为:', min_dir)

if __name__ == '__main__':
    ins = SolutionByDivide()

    # single instance
    single_test(ins)

运行结果如下所示:
image.png

蛮力法
代码如下所示:

# -*- coding: utf-8 -*-
# @Time    : 2022/10/20 15:56
# @Author  : Zeeland
# @File    : 最近点对点问题_暴力.py
# @Software: PyCharm

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

class Solution:
    def find_nearest_direction(self, p: list):
        start_time = time.time()
        n = len(p)
        min_dir = float('inf')
        min_p = []
        for i in range(n):
            for j in range(n):
                if j == i:
                    break
                dir = math.sqrt(math.pow(p[i][0]-p[j][0], 2) + math.pow(p[i][1]-p[j][1], 2))
                if dir < min_dir:
                    min_dir = dir
                    min_p = [[p[i]], [p[j]]]
        return min_dir, min_p, time.time() - start_time

def plot_analysis(historty_time: list):
    plt.plot(list(range(len(history_time))), history_time)
    plt.xlabel('times')
    plt.ylabel('signle cost time/s')
    plt.show()

if __name__ == '__main__':
    # single instance
    ins = Solution()
    p = [[0, 0], [1, 1], [3, 4], [0, 3], [3.2, 4.2], [0, -1], [-2, -2], [-1, -2], [0, 0.4], [-1, 2], [0, 2], [0.5, 2]]
    min_dir, min_p, cost_time = ins.find_nearest_direction(p)
    print('距离最小两个点为:', min_p[0][0], min_p[1][0])
    print('距离为:', min_dir)

    # 100 times simulation
    all_cost_time = 0
    history_time = []
    for i in range(100):
        p = [[random.randint(1, 10000), random.randint(1, 100)] for _ in range(100)]
        history_time.append(ins.find_nearest_direction(p)[2])
        all_cost_time += history_time[i]
    print('100次模拟的总时间花费:', all_cost_time,'s')

    # stability analysis
    plot_analysis(history_time)

运行结果如下所示:
image.png

5 Reference

【算法设计与分析】分治法求最近点对问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】第七章 进程控制(进程创建+进程终止+进程等待+进程替换+min_shell)

&#x1f3c6;个人主页&#xff1a;企鹅不叫的博客 ​ &#x1f308;专栏 C语言初阶和进阶C项目Leetcode刷题初阶数据结构与算法C初阶和进阶《深入理解计算机操作系统》《高质量C/C编程》Linux ⭐️ 博主码云gitee链接&#xff1a;代码仓库地址 ⚡若有帮助可以【关注点赞收藏】…

【路径规划-多式联运】基于遗传算法求解多式联运运输问题(考虑碳交易)附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

字符串和编码那些事

一、字符编码 1. ASCII字符编码 因为计算机只能处理数字&#xff0c;如果要处理文本&#xff0c;就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特&#xff08;bit&#xff09;作为一个字节&#xff08;byte&#xff09; 由于计算机是美国人发明的&#…

【Apache Spark 】第 11 章使用 Apache Spark 管理、部署和扩展机器学习管道

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

编程神器Copilot逐字抄袭他人代码?

自面世后就饱受争议的 GitHub Copilot 编程神器最近又遭遇舆论风暴。 日前,德州农工大学的一位计算机科学教授 Tim Davis 在推特上发文称, GitHub Copilot 在没有标注来源也没有 LGPL 许可的情况下,输出了大量应该受版权保护的代码。 Tim Davis 还发了自己和 GitHub Copil…

90后汕头返种水稻 国稻种芯·中国水稻会:广东新农人田保姆

90后汕头返种水稻 国稻种芯中国水稻会&#xff1a;广东新农人田保姆 南方日报 张伟炜 新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯中国水稻节 中国三农智库网-功能性农业农业大健康大会报道&#xff1a;“5月稻谷病虫害防护非常…

机器学习(周志华)课后习题

第1章 绪论 1.1 表1.1若只包含编号1和4的两个样例&#xff0c;试给出相应的版本空间。 版本空间&#xff1a;与训练及一致的假设集合。 色泽青绿&#xff0c;根蒂*&#xff0c;敲声*&#xff1b; 色泽*&#xff0c;根蒂蜷缩&#xff0c;敲声*&#xff1b; 色泽*&#xff0c;根…

nuxt.js 进行项目重构-首页

nuxt.js 也是基于vue 的 那么就离不开组件化开发 我们按照组件结构来进行分析 navTop 页面的头部 通用组件 分隔了三个位置 适用于大多数头部 且预留插槽 <template><div class"nav-top"><div class"left"><slot name"left…

Spring5入门到实战------10、操作术语解释--Aspectj注解开发实例。AOP切面编程的实际应用

1、操作术语 1.1、连接点 类里面哪些方法可以被增强、这些方法被称为连接点。比如&#xff1a;用户控制层有登录、注册、修改密码、修改信息等方法。假如只有登录类和注册类可以被增强&#xff0c;登录和注册方法就称为连接点 1.2、切入点 实际被真正增强的方法&#xff0c…

C++ 【UVA488】Triangle Wave

&#x1f4cb; 个人简介 &#x1f496;大家好&#xff0c;我是2022年3月份新人榜排名第三的 ༺Blog༒Hacker༻ &#x1f389;支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4ac;格言&#xff1a;༺永做优质༒programmer༻ &#x1f4e3; 系列专栏&am…

Unity技术手册-编辑器基础入门万字大总结

往期文章分享点击跳转>《导航贴》- Unity手册&#xff0c;系统实战学习点击跳转>《导航贴》- Android手册&#xff0c;重温移动开发 本文约8千字&#xff0c;新手阅读需要20分钟&#xff0c;复习需要12分钟 【收藏随时查阅不再迷路】 &#x1f449;关于作者 众所周知&…

【C/C++】程序环境,探索程序的执行过程(习得无上内功《易筋经》的第一步)

目录1.程序的翻译环境和执行环境2.详解编译链接2.1翻译环境2.2编译本身也分为几个阶段预编译&#xff08;预处理&#xff09;编译汇编详解符号表形成符号表2.3.链接合并段表符号表的合并和重定位3.运行环境总结&#xff1a;1.程序的翻译环境和执行环境 在ANSIC&#xff08;标准…

LeetCode每日一题——1235. 规划兼职工作

LeetCode每日一题系列 题目&#xff1a;1235. 规划兼职工作 难度&#xff1a;困难 文章目录LeetCode每日一题系列题目示例思路题解题目 你打算利用空闲时间来做兼职工作赚些零花钱。 这里有 n 份兼职工作&#xff0c;每份工作预计从 startTime[i] 开始到 endTime[i] 结束&a…

1024程序员节|基于Springboot实现爱心捐赠管理系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 文末获取源码 项目编号&#xff1a;BS-XX-…

Mybatis-plus学习(基于版本3.0.5)

文章目录一.概念1.1 简介1.2 特性二.快速入门三.CRUD扩展3.1 Insert插入3.2 主键生成策略3.3 Update更新3.4 自动填充3.5 乐观锁3.6 查询操作3.7 删除操作3.8 性能分析插件&#xff08;新版本的Mybatis-plus已将此插件移除&#xff09;3.9 条件构造器3.10 代码生成器一.概念 1…

Transformer合集3

太多了 我都累了 这都第4了 这次先是关于他的小样本目标检测 , 用很少的训练示例检测新目标 小样本目标检测 论文地址&#xff1a; https://openaccess.thecvf.com/content/CVPR2022/papers/Han_Few-Shot_Object_Detection_With_Fully_Cross-Transformer_CVPR_2022_paper.…

docker安装influxdb及备份恢复

influxdb安装influxdb1&#xff0c;拉取镜像2&#xff0c;创建目录并进入到目录内3&#xff0c;创建influxdb容器服务4&#xff0c;访问&#xff1a;ip8086备份恢复influxdb数据准备1.1 创建用户&#xff0c;填入组织&#xff0c;桶信息1.2&#xff0c;给桶添加点数据1&#xf…

ansible部署lnmp架构

环境准备&#xff1a; 主机名IP服务系统ansible192.168.160.131ansibleCentOS-8.5nginx192.168.160.132nginxCentOS-8.5mysql192.168.160.137mysqlCentOS-8.5php192.168.160.139phpCentOS-8.5 1、生成私钥&#xff0c;对另外三台主机进行免密登入 [rootansible ~]# ssh-keyge…

【单片机毕业设计】【mcuclub-jj-007】基于单片机的门铃的设计

最近设计了一个项目基于单片机的门铃&#xff0c;与大家分享一下&#xff1a; 一、基本介绍 项目名&#xff1a;门铃 项目编号&#xff1a;mcuclub-jj-007 单片机类型&#xff1a;STC89C52、STM32F103C8T6 具体功能&#xff1a; 1、通过人体热释电检测是否有人&#xff0c;当…

Java --- 创建SpringMVC项目

目录 一、什么是MVC 二、什么是SpringMVC 三、SpringMVC的特点 四、创建SpringMVC项目 4.1、开发环境 4.2、创建maven工程 4.3、配置web.xml文件 4.4、创建请求控制器 4.5、配置springMVC.xml文件 4.5、访问首页面 4.6、访问指定页面 一、什么是MVC MVC是一种软件架…