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

news2024/11/29 11:56:31

一.实验目的

  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文件。

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

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

相关文章

unity动画的关键帧添加event-同步语音

在iclone中做的语音嘴型动画&#xff0c;因是用下图自带的方式语音生成的动画&#xff0c;而不是用plugin(面捕live会连同语音一起导出)&#xff0c;所以导出来到Unity中&#xff0c;之后口型、动作、表情等没有声音。 我需要把原有的语音也重新在unity中加载上&#xff0c;原来…

[lesson19]对象的构造(下)

对象的构造(下) 特殊的构造函数 两个特殊的构造函数 无参构造函数 没有参数的构造函数当类中没有定义构造函数时&#xff0c;编译器默认提供一个无参构造函数&#xff0c;并且其函数体为空 拷贝构造函数 参数为const class_name&的构造函数当类中没有定义拷贝构造函数时&…

React复习全攻略:浅尝Redux的全局滋味

是什么? Redux 是一个使用叫做 “action” 的事件来管理和更新应用状态的模式和工具库 提供全局状态数据的单一仓库&#xff08;对象树&#xff09;&#xff0c;管理应用的全局状态单一数据源&#xff0c;状态只读&#xff0c;状态修改只由纯函数完成 为什么用&#xff08;特点…

C语言操作符详解(三)

一、表达式求值 1.1整型提升 C语言中整型算术运算总是至少以缺省整型类型的精度来进行的。 为了获得这个精度&#xff0c;表达式中的字符和短整型操作数在使用之前被转换为普通整型&#xff0c;这种转换称为整型提升。 如何进行整型提升呢&#xff1f; 1. 有符号整数提升是按…

如何理解Fourier Neural Operator (FNO)

写这篇blog是因为多方参考才读懂这两篇文章&#xff0c;希望能用自己的语言表达出来加深一下理解。因为是刚刚开始学习这部分内容&#xff0c;错误之处敬请指出。 文章目录 前言算子Operator概念理解问题建立Graph Neural Operator的思想证明采样多少个数据点可以用来表示一组a…

五金件外观检测机如何确保产品质量?

五金件是指由金属材料制成的各种零部件或制品&#xff0c;主要用于各种机械设备、工具、建筑结构以及其他领域。五金件通常由金属加工、铸造、锻造等方式制造&#xff0c;具有耐久性、强度高、稳定性强等特点。应用于工业制造、建筑、家具、汽车、电子产品等领域。 本案五金件尺…

Linux使用C语言实现Socket编程

Socket编程 这一个课程的笔记 相关文章 协议 Socket编程 高并发服务器实现 线程池 网络套接字 socket: &#xff08;电源&#xff09;插座&#xff08;电器上的&#xff09;插口&#xff0c;插孔&#xff0c;管座 在通信过程中, 套接字是成对存在的, 一个客户端的套接字, 一个…

佛山市人工智能产业生态交流会:实在智能Agent引领“智造浪潮”

制造业&#xff0c;实体经济的核心&#xff0c;技术创新的先锋&#xff0c;供给侧结构性改革的关键。在新一轮产业竞争中&#xff0c;数字化转型成为制造业升级的必由之路&#xff0c;是引领未来的重要抓手。 为促进数字经济赋能实体经济&#xff0c;加快人工智能技术的推广应用…

Object类里面的clone方法与接口cloneable的理解

文章目录 前言一、Object类二、Cloneable接口 前言 今天在进行使用clone方法时产生了一个疑问&#xff0c;为什么Object类明明有clone()方法&#xff0c;但为什么还要加上cloneable这个接口呢&#xff0c;下面我们就来解答一下为什么要重写clone()方法非要加上cloneable接口&a…

Ant Design 表单基础用法综合示例

Ant Design 的表单组件设计得非常出色,极大地简化了表单开发的复杂度,让开发者能够快速构建出功能丰富、交互友好的表单界面。 接下来总结一下 Ant Design 中表单的基本用法。 Form 组件 用于定义整个表单,可以设置表单的布局方式、提交行为等。通常会将表单字段组件嵌套在 F…

Meta宣布全新训推一体加速器:完全集成PyTorch 2,性能3倍提升

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了免费的人工智能中文站https://ai.weoknow.com 新建了收费的人工智能中文站https://ai.hzytsoft.cn/ 更多资源欢迎关注 Meta 疯狂砸入数十亿美元&#xff0c;一部分招揽人才&#xff0c;一部分造芯片。 Meta 正在不…

用户状态保持机制-Session

0、业务需求 会话&#xff1a;web应用中的会话是指一个客户端浏览器和服务器之间连续发生的一系列请求和响应的过程。 会话状态&#xff1a;web应用中的会话状态是指web服务器与浏览器在会话过程中产生的状态信息&#xff0c;借助会话状态&#xff0c;服务器能够把属于同一会话…

1572. 【基础赛】涂色(paint)

1572. 【基础赛】涂色&#xff08;paint&#xff09; (Input: paint.in, Output: paint.out) 时间限制: 2 s 空间限制: 256 MB 具体限制 题目描述 Introl获得了一个N行的杨辉三角&#xff0c;他将每行中值为奇数的位置涂为了黑色。 Chihiro将提出M次询问&#xff0c;在第L…

51单片机上面的IIC协议

1、什么是IIC协议 2、模拟IIC协议 51单片机上面是没有与IIC协议相关的寄存器的&#xff08;没有相关的硬件&#xff09;&#xff0c;不像串口可以配置对应的寄存器达到目的&#xff08;比如修改波特率9600 or 115200&#xff09;&#xff0c;要配置IIC只能够根据用户手册里面的…

【LeetCode】单调栈类题目详解

所有题目均来自于LeetCode&#xff0c;刷题代码使用的Python3版本 单调栈 通常针对一维数组的问题&#xff0c;如果需要寻找一个元素右边或者左边第一个比自己大或者小的元素的位置&#xff0c;就可以使用单调栈&#xff0c;时间复杂度为O(n) 单调栈的本质是空间换时间&#…

[AIGC] Spring中的SPI机制详解

文章目录 1. 什么是SPI2. Spring中的SPI机制3. Spring SPI的工作流程4. Spring SPI的应用 1. 什么是SPI SPI &#xff08;Service Provider Interface&#xff09;是Java中服务提供者接口的缩写&#xff0c;它是Java提供的一种用于被第三方实现或扩展的接口&#xff0c;SPI的作…

2024 年最新前端工程师使用 Webpack 模块打包工具详细教程(更新中)

概述 Webpack 模块打包工具 Webpack 是一个现代的静态模块打包工具&#xff0c;用于将前端应用程序的各种资源&#xff08;例如如&#xff1a;JavaScript、CSS、图片等&#xff09;视为模块&#xff0c;并将它们打包成可以在浏览器中运行的静态文件。它的主要功能包括模块打包…

对 FileReader 的理解

1、文档 FileReader - Web API 接口参考 | MDN 2、概念 FileReader 对象是一个内置的 JavaScript 对象&#xff0c;用于在客户端&#xff08;浏览器&#xff09;中异步读取文件内容。 它提供了一种在 Web 应用程序中读取文件数据的方式&#xff0c;可以读取文件内容并将其转…

跟TED演讲学英文:The next grand challenge for AI by Jim Fan

The next grand challenge for AI Link: https://www.ted.com/talks/jim_fan_the_next_grand_challenge_for_ai? Speaker: Jim Fan Date: October 2023 文章目录 The next grand challenge for AIIntroductionVocabularyTranscriptSummary后记 Introduction Researcher Jim…

MySQL事务、主从、分库分表常见面试题

文章目录 1.事务的特性2.并发事务问题&#xff0c;如何解决&#xff0c;默认隔离级别3.undo log和redo log的区别4.事务中的隔离性是如何保证的&#xff08;解释一下MVCC&#xff09;5.主从同步原理6.分库分表 1.事务的特性 2.并发事务问题&#xff0c;如何解决&#xff0c;默认…