分治法解二维的最近对问题,算法分析与代码实现,蛮力法与分治法解决二维的最近对问题的区别

news2024/9/29 21:27:38

🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨,经典算法的解析✨都在这儿,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏

🪔本系列专栏 -  数据结构与算法_勾栏听曲_0

🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️

📌个人主页 - 勾栏听曲_0的博客📝

🔑希望本文能对你有所帮助,如有不足请指正,共同进步吧🏆

🎇为人性僻耽佳句,语不惊人死不休。📈

目录

分治法

算法思想

时间效率分析 

二维的最近对问题

算法思路

举例分析

代码实现


分治法

算法思想

        分治法可能是最著名的通用算法设计技术了。虽然它的名气可能和它那好记的名字有关,但它的确是当之无愧的:很多非常有效的算法实际上就是这个通用算法的特殊实现。其实,分治法是按照以下方案工作的。

        (1)将一个问题划分为同一类型的若干子问题,子问题最好规模相同。
        (2)对这些子问题求解(一般使用递归方法,但在问题规模足够小时,有时也会利用另一个算法)。
        (3)有必要的话,合并这些子问题的解,以得到原始问题的答案。

        分治法的流程可以参见下图,该图描述的是将一个问题划分为两个较小子问题的例子,也是最常见的情况(至少那些设计运行在单CPU机器上的分治算法是这样的)。

时间效率分析 

        在分治法最典型的运用中,问题规模为n的实例被划分为两个规模为n/2的实例。更一般的情况下,一个规模为n的实例可以划分为b个规模为n/b的实例,其中α个实例需要求解(这里,a和b是常量,a≥1,b>1)。为了简化分析,我们假设n是b的幂,对于算法的运行时间T(n),我们有下列递推式:

T(n) =aT(n / b)+ f(n)

        其中,f(n)是一个函数,表示将问题分解为小问题和将结果合并起来所消耗的时间(对于求和的例子来说,a = b = 2,f(n)= 1)。上述递推式被称为通用分治递推式(generaldivide-and-conquer recurrence)。显然,T(n)的增长次数取决于常量a和b的值以及函数f(n)的增长次数。在分析许多分治算法的效率时,可以应用下列定理来大大简化我们的工作。

        主定理        如果在递推式(5.1)中 f(n)e e(n*),其中d≥0,那么

 T(n)\in \left\{\begin{matrix}\Theta (n^{d}) & a<b^{d}\\ \Theta (n^{d}\log n) & a=b^{d}\\ \Theta (n^{\log b^{a}}) & a>b^{d} \end{matrix}\right.

        其中,当a < b^{d}​时,该问题的时间复杂度为n的d次方

        当a = b^{d}​时,该问题的时间复杂度为n的d次方乘一个对数级\log n

        当a > b^{d}​时,该问题的时间复杂度为n的log b为底a次方

二维的最近对问题

        二维的最近对问题是指在二维平面上有n个点,如何找到距离最近的两个点的问题。一种常用的解决方法是分治法,即将一个规模较大的问题分解为规模较小的子问题,先求解这些子问题,然后将各子问题的解合并得到原问题的解。

        在之前的章节中,我们有学到蛮力法来解决一些问题,二维的最近对问题如果实用蛮力法来解决问题,那么时间效率为O(n^{2})。但是使用分治技术可以用更高的时间效率来解决这个问题。

算法思路

        左图是最近对问题的分治算法的思想,右图是和点p距离小于d的点可能分布的矩形区域

        具体步骤为:

  • 将n个点按照x坐标排序,然后从中间划分为两个子集,分别求解左右两边的最近点对。
  • 比较左右两边的最近点对的距离,取较小者作为当前的最近点对。
  • 在中间区域内寻找可能存在的更近的点对,即在距离中线不超过当前最近点对距离的范围内,找出所有满足条件的点,并按照y坐标排序。
  • 对于每个点,只需与它后面的7个点进行比较,如果发现更近的点对,则更新当前最近点对。
  • 返回当前最近点对。

举例分析

        我们举个例子,假设我们有以下6个点:

x坐标y坐标
A12
B34
C56
D78
E910
F1112
  • 首先,我们按照x坐标排序,得到以下顺序:

A B C D E F

  • 然后,我们从中间划分为两个子集,分别求解左右两边的最近点对。左边的子集是:

A B C

右边的子集是:

D E F

  • 对于左边的子集,我们可以用暴力法求出最近点对是A和B,距离为根号8。对于右边的子集,我们也可以用暴力法求出最近点对是D和E,距离也是根号8。所以当前的最近点对距离是根号8。
  • 接下来,我们在中间区域内寻找可能存在的更近的点对,即在距离中线不超过根号8的范围内,找出所有满足条件的点,并按照y坐标排序。中线的x坐标是6,所以我们只需要考虑C和D两个点。按照y坐标排序后,得到以下顺序:

C D

  • 对于每个点,只需与它后面的7个点进行比较,如果发现更近的点对,则更新当前最近点对。在这个例子中,只有C和D两个点需要比较,它们的距离是根号8,与当前最近点对距离相等,所以不需要更新。
  • 最后,我们返回当前最近点对,即A和B或者D和E。

代码实现

        其中包括使用蛮力法(暴力破解)与分治法两种方法解决二维的最近对问题,大家可以通过运行代码来更直观的感受这两种方法的异同。

        代码整体逻辑与分析:        

  • 定义了两个结构体,分别表示点和点对,以及一些辅助函数,如计算两点之间的距离,比较两个点对的距离,按照x坐标或y坐标排序的比较函数等。
  • 实现了暴力法求解最近点对的函数,即遍历每个点,与后面的点进行比较,找出最近的点对。这个函数适用于点数较少的情况,时间复杂度是O(n^2)。
  • 实现了分治法求解最近点对的函数,即将一个规模较大的问题分解为规模较小的子问题,先求解这些子问题,然后将各子问题的解合并得到原问题的解。这个函数适用于点数较多的情况,时间复杂度是O(nlogn)。具体的步骤如下:
    • 如果点数小于等于3,直接用暴力法求解。
    • 将n个点按照x坐标排序,然后从中间划分为两个子集,分别求解左右两边的最近点对。
    • 比较左右两边的最近点对的距离,取较小者作为当前的最近点对。
    • 在中间区域内寻找可能存在的更近的点对,即在距离中线不超过当前最近点对距离的范围内,找出所有满足条件的点,并按照y坐标排序。
    • 对于每个点,只需与它后面的7个点进行比较,如果发现更近的点对,则更新当前最近点对。
    • 返回当前最近点对。
  • 主函数,用于测试代码。创建了一个测试用例,包含6个点,并调用分治法求解最近点对,并打印结果。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

//定义一个点的结构体
typedef struct point {
    double x; //x坐标
    double y; //y坐标
} point;

//定义一个点对的结构体
typedef struct pair {
    point p1; //第一个点
    point p2; //第二个点
    double dist; //两点之间的距离
} pair;

//计算两点之间的距离
double distance(point p1, point p2) {
    return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}

//比较两个点对的距离,返回较小者
pair min(pair a, pair b) {
    if (a.dist < b.dist) {
        return a;
    } else {
        return b;
    }
}

//按照x坐标排序的比较函数
int compare_x(const void* a, const void* b) {
    point* p1 = (point*)a;
    point* p2 = (point*)b;
    if (p1->x < p2->x) {
        return -1;
    } else if (p1->x > p2->x) {
        return 1;
    } else {
        return 0;
    }
}

//按照y坐标排序的比较函数
int compare_y(const void* a, const void* b) {
    point* p1 = (point*)a;
    point* p2 = (point*)b;
    if (p1->y < p2->y) {
        return -1;
    } else if (p1->y > p2->y) {
        return 1;
    } else {
        return 0;
    }
}

//暴力法求解最近点对,适用于点数较少的情况
pair brute_force(point* points, int n) {
    pair min_pair; //最近点对
    min_pair.dist = INFINITY; //最近点对距离初始化为无穷大
    for (int i = 0; i < n; i++) { //遍历每个点
        for (int j = i + 1; j < n; j++) { //与后面的点进行比较
            double dist = distance(points[i], points[j]); //计算两点之间的距离
            if (dist < min_pair.dist) { //如果发现更近的点对,更新最近点对和最近点对距离
                min_pair.p1 = points[i];
                min_pair.p2 = points[j];
                min_pair.dist = dist;
            }
        }
    }
    return min_pair; //返回最近点对
}

//分治法求解最近点对,适用于点数较多的情况
pair divide_and_conquer(point* points, int n) {
    pair min_pair; //最近点对

    //如果点数小于等于3,直接用暴力法求解
    if (n <= 3) {
        return brute_force(points, n);
    }

    //将n个点按照x坐标排序,然后从中间划分为两个子集,分别求解左右两边的最近点对
    qsort(points, n, sizeof(point), compare_x); //按照x坐标排序
    int mid = n / 2; //中间位置的索引
    point mid_point = points[mid]; //中间位置的点

    pair left_pair = divide_and_conquer(points, mid); //求解左边子集的最近点对
    pair right_pair = divide_and_conquer(points + mid, n - mid); //求解
    //右边子集的最近点对
    min_pair = min(left_pair, right_pair); //比较左右两边的最近点对,取较小者作为当前的最近点对

    //在中间区域内寻找可能存在的更近的点对,即在距离中线不超过当前最近点对距离的范围内,找出所有满足条件的点,并按照y坐标排序
    point* strip = (point*)malloc(n * sizeof(point)); //创建一个动态数组,用于存放满足条件的点
    int size = 0; //记录满足条件的点的个数
    for (int i = 0; i < n; i++) { //遍历每个点
        if (fabs(points[i].x - mid_point.x) < min_pair.dist) { //如果该点距离中线不超过当前最近点对距离
            strip[size++] = points[i]; //将该点加入到动态数组中
        }
    }
    qsort(strip, size, sizeof(point), compare_y); //按照y坐标排序

    //对于每个点,只需与它后面的7个点进行比较,如果发现更近的点对,则更新当前最近点对
    for (int i = 0; i < size; i++) { //遍历每个点
        for (int j = i + 1; j < size && (strip[j].y - strip[i].y) < min_pair.dist; j++) { //与后面的7个点进行比较
            double dist = distance(strip[i], strip[j]); //计算两点之间的距离
            if (dist < min_pair.dist) { //如果发现更近的点对,更新最近点对和最近点对距离
                min_pair.p1 = strip[i];
                min_pair.p2 = strip[j];
                min_pair.dist = dist;
            }
        }
    }

    free(strip); //释放动态数组的内存空间
    return min_pair; //返回最近点对
}

//主函数,用于测试代码
int main() {
    //创建一个测试用例,包含6个点
    point points[] = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}};
    int n = sizeof(points) / sizeof(points[0]); //计算点的个数

    pair result = divide_and_conquer(points, n); //调用分治法求解最近点对

    printf("The closest pair is (%.2f, %.2f) and (%.2f, %.2f), and the distance is %.2f.\n", result.p1.x, result.p1.y, result.p2.x, result.p2.y, result.dist); //打印结果

    return 0;
}

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

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

相关文章

人工智能发展到GPT4经历了什么,从专家系统到机器学习再到深度学习,从大模型到现在的GPT4

大家好&#xff0c;我是微学AI&#xff0c;今天给大家讲一下人工智能的发展&#xff0c;从专家系统到机器学习再到深度学习&#xff0c;从大模型到现在的GPT4&#xff0c;讲这个的目的是让每个人都懂得人工智能&#xff0c;每个人都懂得人工智能的发展&#xff0c;未来人工智能…

“智慧赋能 强链塑链”—— 煤炭行业数字化转型探讨

煤炭作为传统能源行业之一&#xff0c;是国民经济中不可或缺的一部分&#xff0c;随着国家能源结构的战略转型&#xff0c;煤炭企业的长期盈利能力将面临巨大的挑战。供应链作为煤炭行业生产运营的基础保障&#xff0c;在企业开源节流的要求下&#xff0c;其传统粗放的供应链管…

Xcode 14.3 cocoapod 1.12.0 打包报错解决

前言 前几天升级Xcode到14.3版本&#xff0c;运行项目报错&#xff0c;于是记录下来。 开发环境 macOS: 13.3.1 Xcode: 14.3 CocoaPods: 1.12.0 问题描述 [Xcode菜单栏] -> [Product] -> [Archive]&#xff0c;进行打包操作。执行到 Run custom shell script [CP]…

day16 信号灯

信号灯概念和有名信号灯 目录 信号灯概念和有名信号灯 有名信号灯 无名信号灯 信号灯P操作 信号灯V操作 system V信号灯的 信号灯/信号量&#xff08;semaphore&#xff09; 信号量代表某一类资源&#xff0c;其值表示系统中该资源的数量&#xff1b; 信号量是一个受保…

【C语言】程序运行环境及预处理指令

文章目录 程序的翻译环境&#xff1a;程序的运行环境&#xff1a;C语言预定义符号#define定义标识符#define定义宏具有副作用的宏参数 #与###的使用##的使用 宏和函数对比#undef命令行定义条件编译常见的条件编译指令&#x1f31e; 文件包含指令嵌套文件包含 其他预处理指令 撒…

【C++】对数组指针的理解,例如 int (*p)[3]

目录 简介思考理解结语 简介 Hello&#xff01; 非常感谢您阅读海轰的文章&#xff0c;倘若文中有错误的地方&#xff0c;欢迎您指出&#xff5e; ଘ(੭ˊᵕˋ)੭ 昵称&#xff1a;海轰 标签&#xff1a;程序猿&#xff5c;C选手&#xff5c;学生 简介&#xff1a;因C语言结识…

Win7 无法安装 VMware Tools 解决方法

文章目录 1.下载kb4474419补丁2.虚拟机 > 设置 > 软盘&#xff0c;选中“使用物理驱动器”3.解决IE浏览器只能访问百度4.下载windows iso的正确方式 win7版本&#xff1a;cn_windows_7_ultimate_with_sp1_x64_dvd_u_677408_2&#xff08;iso大小3.18 GB&#xff09; vmwa…

C++内联函数(编译器角度,汇编角度),auto关键字,范围for语法糖,nullprt与NULL区别等

TIPS 在C当中有一个东西可以打印类型&#xff1f;typeid(变量名).name()数组一旦从参数进入到函数里面&#xff0c;它就已经是个指针了&#xff0c;再也不是一整个数组了 内联函数&#xff08;正常函数定义前加个inline修饰&#xff09; 在实际当中&#xff0c;有时候去调用…

13、拦截器

文章目录 1、HandlerInterceptor 接口2、配置拦截器3、拦截器原理 【尚硅谷】SpringBoot2零基础入门教程-讲师&#xff1a;雷丰阳 笔记 路还在继续&#xff0c;梦还在期许 1、HandlerInterceptor 接口 /*** 登录检查* 1、配置好拦截器要拦截哪些请求* 2、把这些配置放在容器中…

爬虫——肯德基

import requests #UA伪装&#xff1a;将对应的User-Agent封装到一个字典中 headers{User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.48 } #指定url url http://www.kfc.com.c…

【jvm系列-09】垃圾回收底层原理和算法以及JProfiler的基本使用

JVM系列整体栏目 内容链接地址【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963【三】运行时私有区域之虚拟机栈…

一图看懂 xlrd 模块:读写 Excel 文件的数据和格式信息, 资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 xlrd 模块&#xff1a;读写 Excel 文件的数据和格式信息, 资料整理笔记&#xff08;大全&#xff09; 摘要模块图类关系图模块全展开【xlrd】统计常量intdict 模块26 os27 …

【蓝桥杯省赛真题19】python完数及个数 青少年组蓝桥杯python编程省赛真题解析

目录 python完数及个数 一、题目要求 1、编程实现 2、输入输出 二、解题思路

itop-3568开发板驱动学习笔记(22)设备树(一)设备树基础

《【北京迅为】itop-3568开发板驱动开发指南.pdf》 学习笔记 文章目录 设备树简介设备树编译设备树语法设备根节点设备子节点节点名称reg 属性#address-cell 和 #size-cells 属性model 属性status 属性compatible 属性aliases 节点chosen 节点device_type 属性自定义属性 设备树…

使用Docker创建并运行Jenkins详细步骤

文章目录 一、使用Docker搭建Jenkins二、为Jenkins配置执行节点1、进入管理页面2、新建节点3、配置节点信息4、子节点连接master节点5、在子节点机器上运行上面复制下来的命令6、查看子节点是否在线 三、创建一个简单的job1、进入创建页面2、设置job名称3、配置job相关信息4、构…

eBay变现方式有哪些?如何利用好测评自养号?

近年来&#xff0c;越来越多的人选择在eBay开店&#xff0c;甚至很多其他平台的卖家也转型到了eBay。但很多卖家发现&#xff0c;在运营了一段时间后&#xff0c;过了对新账号的流量扶持期&#xff0c;店铺突然出现流量开始下滑的情况&#xff0c;针对这种情况卖家可以采取哪些…

计算机网络(数据链路层)部分习题

1. 通过传统以太网发送中文“华南师范大学计算机学院”&#xff0c;封装成以太网帧&#xff0c;请问该帧的数据字段有效字节是多少&#xff1f;需要填充多少个字节&#xff1f; 答&#xff1a;一个字两个字节&#xff0c;“华南师范大学计算机学院”共11个字&#xff0c;有效字…

Visual Studio容器工具要求在构建,调试或运行容器化项目之前运行Docker

出现此提示&#xff0c;是因为电脑未安装Docker所致&#xff0c;接下来就教大家如何安装Docker。 第一步&#xff1a;下载 地址&#xff1a;Install Docker Desktop on Windows | Docker Documentation 第二步&#xff1a;安装 1、双击Docker Desktop Installer.exe运行安装程…

电子束与材料相互作用Matlab代码

标题 1 题目2 实验原理2.1 蒙特卡洛模拟的基本思想2.2 电子散射的基本概念 3 代码 1 题目 扫描透射电镜(STEM)的基本原理是用极细的扫描电子束透射样品,透射电子直接被具有一定张角的接收器所接收&#xff0c;透射电流的强度直接反应了样品的质量厚度。 对于一定厚度的样品&am…

面试官:一千万的数据,你是怎么查询的?

面试官&#xff1a;一千万的数据&#xff0c;你是怎么查询的&#xff1f; 1 先给结论 对于1千万的数据查询&#xff0c;主要关注分页查询过程中的性能 针对偏移量大导致查询速度慢&#xff1a; 先对查询的字段创建唯一索引 根据业务需求&#xff0c;先定位查询范围&#xff08…