算法与程序设计(实验2)----分治法求最近点对问题

一.实验目的

  1. 掌握分治法思想。
  2. 学会最近点对问题求解方法。

二、实验内容

1. 对于平面上给定的N个点,给出具有最短距离的两点。

2. 要求随机生成N个点的平面坐标,应用蛮力法编程计算出所有点对的最短距离。

3. 要求随机生成N个点的平面坐标,应用分治法编程计算出所有点对的最短距离。

4. 分别对N=100000—1000000,统计算法运行时间,比较理论效率与实测效率的差异,同时对蛮力法和分治法的算法效率进行分析和比较。

5. 算法执行过程可视化。

三、实验步骤与结果

1.实验

(1)问题描述

        对于平面上给定的N个点,给出所有点对的最短距离。即输入是平面上的N个点,输出是N点中具有最短距离的两点。

  1. 生成随机点

Creat_new_point   //点的坐标(结构体)

struct Point  

    double x, y;  

        要保证两点不重合且最终有序,经过比较分析,本实验采用Set容器(集合)。

        时间复杂度上,若采用数组,查重的时间复杂度最优情况为O(nlogn),排序的最优情况时间复杂度为O(nlogn)。而set 中的insert(x)函数可将 x 插入 set 容器中,并自动递增排序和去重,时间复杂度为 O(logn),其中 n 为集合内的元素个数。set集合存储点的信息,创建点集的时间复杂度为O(log1+log2+log3+...+nlogn)。则当n趋近于无穷时,nlogn与log(n!)为同阶无穷大。则该过程时间复杂度为O(logn!)

Creat_Random_Point(n)  //n个点

while(set.size()!=n)

    new_point.x=rand();

    new_point.y=rand();

    set.insert(new_point);  

小规模测试生成随机点坐标,并验证:有序且不重复

图1 生成随机点坐标测试

重载比较函数:

bool operator == (const Point &p2)

bool operator < (const Point &p2)

return x == p2.x && y == p2.y

        if x == p2.x

             return y < p2.y;

        return x < p2.x;

2.算法

(1)暴力算法

        设置变量dis存放最短距离,初始化为INF。变量P1,P2存储相应的两点,初始化为-1。

        循环遍历每一个点,每当遍历到一个点p时,循环遍历其他的点r到p的距离,如果距离小于dis,则更新dis和P1、P2的值。

图2 暴力穷举法流程图

伪代码:

Violent_exhaustive(*P)

q1 = 0 and q2 = 0 and min = INF

//初始化:当前最小距离为INF无穷大,其两端点下标为0

for i = 0 to n-1 //遍历所有的点

        for j = i+1 to n

              if dis(S[i],S[j])<min

min = dis(S[i],S[j]) //更新最短距离点信息

q1 = i

q2 = j

  时间复杂度:遍历两遍循环,则时间复杂度为O(n^2)

表一 暴力算法不同n运行时间

运行时间(s)

实际值

理论值

误差%

100000

28.042

28.042

0

200000

116.524

112.168

-3.883460524

300000

259.923

252.378

-2.989563274

400000

457.987

448.672

-2.076126881

500000

731.232

701.05

-4.305256401

600000

1028.925

1009.512

-1.923008345

700000

1456.279

1374.058

-5.983808544

800000

1889.198

1794.688

-5.266096391

900000

2415.253

2271.402

-6.333136979

1000000

2958.634

2804.2

-5.507239141

             图3 暴力运行时间理论值与实际值比较

对于暴力穷举,由于算法未进行任何简化且算法执行次数与时间复杂度不随数据分布与数量级有任何变化,故整体拟合效果非常好,证明暴力法的时间复杂度为O(n^2)的结论正确。

(2)分治法

①思想

        分治法可以将问题缩小化,将庞大的问题逐渐缩小成单个小的问题进行解决。

②步骤

a.预处理:根据输入点集S中的x轴和y轴坐标进行排序。

b. 分子集:当点数|S|>3时,将平面点集S分割成为大小大致相等的两个子集SL和SR,选取一个垂直线L作为分割直线,如图下图所示。

图4 分治法图示

c.求区间最值:两个递归调用,分别求出SL和SR中的最短距离为dl和dr。如图2所示,取d=min(dl, dr),在直线L两边分别扩展d,得到边界区域Y,Y’是区域Y中的点按照y坐标值排序后得到的点集,Y'又可分为左右两个集合Y’L和Y’R。

递归调用的临界判断条件为:①该区域仅剩一个点时,返回INF;②该区域剩两个点时,返回两点距离③该区域有三个点时,返回三对点对之间距离最小值。④否则,将区间分成两份,分别执行c操作(递归求区间最值)。

图5 分治法划分约束区域

d.对于Y’L中的每一点,检查Y’R中的点与它的距离,更新所获得的最近距离。而更新最近距离时,不需要使用暴力穷举法,对[mid-d,mid+d]中的排序后,不妨设最终情况为pl,pr两点分别位于点集PL,PR,且该点对间距离小于d,则pl,pr两点一定位于下图中2d*d的矩形内。这是因为根据分析,当且仅当点对位于该蓝色区域内时,两点间纵坐标之差小于d,任何其他不在该范围内的点横坐标或纵坐标之差必定大于d,距离必定大于d,此时,该点对间距离一定不为最小,故无需进行比较。所以每个点最多比较6次,当与往后第i个点的距离大于当前最短距离变量d时(i<6),无需比较剩下6-i个点。

图6 约束区间

图7 分治法流程图

伪代码

#结构体点的构造:

struct Point

    int x,y;

    bool operator <(const Point &p2)

        if(x==p2.x)

            return y<p2.y;

        return x<p2.x;

    bool operator ==(const Point &p2)

        return x==p2.x&&y==p2.y;

#重载比较函数:

bool cmpy(const Point &p1,const Point &p2)  重载比较函数

      if(p1.y==p2.y)

           return p1.x<p2.x;

      return p1.y<p2.y;

#返回距离值函数:

double dis(Point p1,Point p2)   返回距离的值

d=sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));

return d;

#递归求区域内两点的最小距离函数:

double get_min(int begin,int end)      

if (begin >= end)   区域内只有一个点时:返回无穷大

        return INF;

    if (end - begin == 1)   区域内有两个点:返回两点距离

        return dis(P[begin], P[end]);

if (end - begin == 2)   区域内有三个点:返回区域三条连线中最短的线的距离

        d = min(dis(P[begin], P[begin + 1]), dis(P[begin], P[begin + 1]));

        d = min(dis(P[end], P[begin + 1]), d);

        return d;   

    mid = (begin + end) / 2;    中点坐标作为划分区域的直线

    d = min(get_min(begin, mid), get_min(mid, end));  更新最短距离

#求中间区域[mid-d,mid+d]的最小值  

cnt = 0;

    Point *py=new Point[end-begin+1];  设立新数组存放中间区域的点

    for (int i = begin; i <= end; i++)  

        if (P[i].x >= P[mid].x-d)

            if(P[i].x>P[mid].x+d)

                break;

            py[cnt++] = P[i];

    sort(py, py + cnt, cmpy);    按y坐标排序

    for (int i = 0; i < cnt; i++)

        for (int j = i + 1; j < cnt; j++)

            if (py[j].y - py[i].y >=d)

                break;

            d= min(d, dis(py[i], py[j]));  更新最短距离

    return d;   返回最短距离

表二 分治算法不同n运行时间

运行时间(ms)

实际值

理论值

误差%

100000

53.7

53.7

0

200000

109.9

113.8661243

3.483146837

300000

164

176.4728468

7.067856076

400000

235

240.6644972

2.353690425

500000

271.1

306.0346892

11.41527103

600000

349.6

372.3440666

6.108346719

700000

420

439.4344706

4.422609501

800000

493.8

507.1934917

2.640706535

900000

545.3

575.537081

5.253715523

1000000

588.5

644.4

8.674736189

图8 分治算法不同n的运行时间

         对于分治法,由于分治法需要开辟空间并实现递归操作,递归栈的迭代与开辟内存空间均需要消耗一部分时间。对于数量级较小的数据时,整个算法实际运行时间比较短,开辟内存空间与递归栈迭代消耗的时间的影响将被一定程度上放大。但当数量级较大时,这种影响被冲淡。因此会体现出当数量级较小时拟合效果较差,但当数量级较大时,拟合效果比较好。

(3)算法性能

图9 两种时间复杂度比较

通过对算法时间的分析知道,当对于小数量级的数据时(10^3以下),暴力穷举法要优于分治法,但当数量级较大时,分治法明显优于暴力穷举法。这是由于分治法需要开辟内存空间并通过递归栈实现递归。因此将消耗更多时间,并对小数量级下的最终时间消耗产生较大影响。

因此对于“最近点对”问题最优的方法是当数量级小于10^3时选择暴力穷举法进行运算,当数量级大于10^3时选择分治法进行运算。

表3 对小规模数据下测试两种算法的性能

穷举法

分治

数量级

平均时间

理论时间

数量级

平均时间

理论时间

10

0.00025

0.00025

10

0.0043

0.001078

100

0.02805

0.02805

100

0.03371

0.0215687

1000

2.79846

2.79846

1000

0.38379

0.3235305

10000

278.374

278.374

10000

4.31374

4.31374

100000

28042

28042

100000

52.4283

53.92175

三.实验心得

        实验成功!在本次实验中我了解并掌握了分治法的思想、学会了最近点对问题求解的方法。在实验过程中,也遇到了许多问题,经过努力都得以解决。值得一提的是,在测试的时候发现,当测试规模相同时,每次测试生成的随机数相同,查阅资料得知rand()方法生成的是伪随机数,是根据一个数按照某个公式推算出来的,这个数我们称之为“种子”,但是这个种子在系统启动之后就是一个定值,所以造成了这种问题。后来想到了解决办法:rand() 产生随机数时,用srand(seed) 用时间作种子 srand(time(NULL)) 播下后,因为每次运行程序的时间肯定是不相同的,所以产生的随机数必然不同。time()返回值是以秒为单位,故在每一次测试后休眠一秒钟

算法过程的可视化

        为了对实验的算法过程有更清晰深入的了解,我使用Python中的matplotlib函数库,实现了小数据规模下算法过程的可视化,清晰的展示了排序的具体过程。请查看附件中的两个gif文件。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Gateway的简单介绍和使用

1、Gateway简介&#xff1a; Gateway 是一种 API 网关&#xff08;API Gateway&#xff09;技术&#xff0c;它作为微服务架构中的关键组件&#xff0c;负责为系统的外部请求与内部服务之间提供统一的接入点。Spring Cloud Gateway 是基于 Spring 生态系统实现的一个高性能、易…

基于SpringBoot+Vue的古风生活体验交流网站(源码+文档+部署+讲解)

一.系统概述 二十一世纪我们的社会进入了信息时代&#xff0c;信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式对时间、地点的限制太多&#xff0c;而在线管理系统刚好能满足这些需求&#xff0c;在线管理系统突破了传统管理方式的局限性。于是本文针…

天工 AI 爆赞的数据分析能力

分享一个 AI 应用。 天工 AI 天工AI - 首页 (tiangong.cn) 可以上传数据&#xff0c;给出数据分析命令&#xff0c;并能出图。 数据分析师岌岌可危。 又知道其他好用的数据分析应用么&#xff0c;可以告诉我下。

MobX 中 runInAction 的威力:构建原子性状态更新

"原子性状态更新"这个词可以很好地概括 runInAction 的核心功能,即将一组相关的状态更新作为一个整体,要么全部成功,要么全部失败。这种特性对于复杂的异步操作和状态管理非常重要。可以帮助我们构建更加可靠和可预测的 React 应用程序。 怎么理解原子性操作 "…

为什么忘记密码要重置密码而不是直接告诉你密码?

上一篇博客&#xff1a;LeetCode 2529. 正整数和负整数的最大计数 写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blog.…

Tensorflow(GPU版本配置)一步到位!!!

Tensorflow&#xff08;GPU版本配置&#xff09;一步到位&#xff01;&#xff01;&#xff01; CUDA安装CUDA配置Tensorflow配置常见的包 CUDA安装 配置了N次的Tensorflow–Gpu版本&#xff0c;完成了踩坑&#xff0c;这里以配置Tensorflow_gpu 2.6.0为例子进行安装 以下为ten…

React + three.js 3D模型面部表情控制

系列文章目录 React 使用 three.js 加载 gltf 3D模型 | three.js 入门React three.js 3D模型骨骼绑定React three.js 3D模型面部表情控制 示例项目(github)&#xff1a;https://github.com/couchette/simple-react-three-facial-expression-demo 示例项目(gitcode)&#xff…

Linux网络名称空间和cgroup的关系

在Linux系统中&#xff0c;网络名称空间&#xff08;Network Namespaces&#xff09;和控制组&#xff08;cgroups&#xff09;是两种重要的资源管理和隔离技术。虽然它们在功能和应用场景上有所不同&#xff0c;但二者共同为Linux容器技术&#xff0c;如Docker和Kubernetes&am…

编程规范(保姆级教程)

文章目录 为什么需要编程规范&#xff1f;&#x1f4a1;代码检测工具 ESLint&#x1f4a1;代码格式化 Prettier&#x1f4a1;ESLint 与 Prettier 配合解决代码格式问题eslint支持ts约定式提交规范Commitizen助你规范化提交代码什么是 Git Hooks使用 husky commitlint 检查提交…

wsl初步使用记录

wsl介绍 WSL是windows平台下Linux环境的子系统&#xff08;Windows Subsyetem for Linux&#xff09;&#xff0c;可以让Windows下方便的安装Linux系统&#xff0c;而无需安装其他虚拟机软件。 wsl使用 Windows操作系统支持 Windows 10 版本 2004 及更高版本&#xff08;内…

OpenHarmony南向开发案例:【智能保险柜】

样例简介 智能保险柜实时监测保险柜中振动传感器&#xff0c;当有振动产生时及时向用户发出警报。在连接网络后&#xff0c;配合数字管家应用&#xff0c;用户可以远程接收智能保险柜的报警信息。后续可扩展摄像头等设备&#xff0c;实现对危险及时报警&#xff0c;及时处理&a…

缝合的作品(并查集/逆序)

、思路&#xff1a;首先是并查集来做&#xff0c;首先给给每个单词一个id&#xff0c;然后把它放到ans[i]处。 对于操作1&#xff1a;把a单词换为单词b&#xff0c;就相当于a、b两个集合结合。然后再给a单词赋一个新的id&#xff0c;用来进行操作2&#xff0c;因为之后的操作2…

HarmonyOS实战开发-证书管理、如何实现对签名数据进行校验功能。

介绍 本示例使用了ohos.security.certManager相关接口实现了对签名数据进行校验的功能。 实现场景如下&#xff1a; 1&#xff09;使用正确的原始数据和签名数据进行签名校验场景&#xff1a;模拟服务端对签名数据进行校验&#xff0c;验证客户端身份和原始数据完整性。 2&…

车载摄像头图像及画质增强解决方案

车载摄像头作为汽车智能化、安全化的关键组件&#xff0c;其图像质量直接影响着驾驶者的视觉感知和行车安全。美摄科技凭借其在图像处理和AI算法领域的深厚积累&#xff0c;推出了一款专为车载摄像头打造的图像及画质增强解决方案&#xff0c;助力企业实现摄像头画面的实时优化…

基于“遥感+”蓝碳储量估算、红树林信息提取实践技术应用与科研论文写作

大气温室气体浓度不断增加&#xff0c;导致气候变暖加剧&#xff0c;随之会引发一系列气象、生态和环境灾害。如何降低温室气体浓度和应对气候变化已成为全球关注的焦点。海洋是地球上最大的“碳库”,“蓝碳”即海洋活动以及海洋生物&#xff08;特别是红树林、盐沼和海草&…

电商技术揭秘十九:电商平台的智能化与自动化技术

相关系列文章 电商技术揭秘一&#xff1a;电商架构设计与核心技术 电商技术揭秘二&#xff1a;电商平台推荐系统的实现与优化 电商技术揭秘三&#xff1a;电商平台的支付与结算系统 电商技术揭秘四&#xff1a;电商平台的物流管理系统 电商技术揭秘五&#xff1a;电商平台…

✔ ★Java项目——设计一个消息队列(二)

Java项目——设计一个消息队列 四. 项⽬创建五. 创建核⼼类创建 Exchange&#xff08;名字、类型、持久化&#xff09;创建 MSGQueue&#xff08;名字、持久化、独占标识&#xff09;创建 Binding&#xff08;交换机名字、队列名字、bindingKey用于与routingKey匹配&#xff09…

WPS的JS宏如何批量实现文字的超链接

表格中需要对文字进行超链接&#xff0c;每个链接指引到不同的地址。例如&#xff1a; 实现如下表格中&#xff0c;文件名称超级链接到对应的文件路径上&#xff0c;点击对应的文件名称&#xff0c;即可打开对应的文件。 序号文件名称文件路径1变更申请与处理表.xls文档\系统…

HTTPS证书是什么?申请方法是什么?

HTTPS证书是互联网上由权威证书颁发机构&#xff08;CA&#xff09;签发的数字文件&#xff0c;用于证明网站的身份&#xff0c;并通过其中包含的公钥为网站启用HTTPS加密连接&#xff0c;确保用户与网站间的通信数据安全且不可被第三方窃取或篡改。 怎么申请&#xff1f; 一&…

实验案例一:交换机的初始配置

1、实验环境 实验用具包括一台 Cisco 交换机&#xff0c;一台 PC&#xff0c;一根 Console 线缆。 2、需求描述 如图 5.17 所示&#xff0c;实验案例一的配置需求如下。 通过 PC 连接并配置一台 Cisco 交换机在交换机的各个配置模式之间切换将交换机主机的名称改为 BDON 3、…