《算法导论》实验五:最近点对算法(C++)

一、题目描述

在n>=2个点的集合Q中寻找最近点对。
“最近”是指通常意义下的欧几里得距离:即点p1(x1,y1)和p2(x2,y2)之间的距离为:sqrt((x1-x2)2 +(y1-y2)2)。

二、算法设计与分析

算法主要思想就是分治
情况1:点数小于等于二时:直接计算,求该两点之间的距离。
情况2:集合中有三个点:两两比较,求三个点中的最近的两个点距离。
情况3:点数大于三时:首先划分集合S为SL和SR,使得SL中的每一个点位于SR中每一个点的左边,并且SL和SR中点数相同。分别在SL和SR中解决最近点对问题,得到DL和DR,分别表示SL和SR中的最近点对的距离。令d=min(DL,DR)。如果S中的最近点对(P1,P2)。P1、P2两点一个在SL和一个在SR中,那么P1和P2一定在以L为中心的间隙内,以L-d和L+d为界。
如下图-1所示:
这里写图片描述
图-1 点数大于三时时分治

如果在SL中的点P和在SR中的点Q成为最近点对,那么P和Q的距离必定小于d。因此对间隙中的每一个点,在合并步骤中,只需要检验yp+d和yp-d内的点即可。
步骤1:根据点的y值和x值对S中的点排序。
步骤2:找出中线L将S划分为SL和SR
步骤3:将步骤2递归的应用解决SL和SR的最近点对问题,并令d=min(dL,dR)。
步骤4:将L-d~L+d内的点以y值排序,对于每一个点(x1,y1)找出y值在y1-d~y1+d内的接下来的7个点,计算距离为d’。如果d’小于d,令d=d’,最后的d值就是答案。

三、实验结果与分析

本实验的所用到的测试用例是用随机函数生成的,所以每次的测试用例都有所不同。但可通过控制台设定需要测试点的个数N。
(一)、当点个数N=1时(输入不合理)。见下图-2:
这里写图片描述
图-2 N=1时(输入不合理)

(二)、当点个数N=2时,见下图-3:
这里写图片描述
图-3 N=2的输出结果

(三)、当点个数N=3时,见下图-4:
这里写图片描述
图-4 N=3的输出结果

(四)、当点个数N>3时,见下图-5:
这里写图片描述
图-5 N=15的输出结果

四、实验总结

1、 采用分治法寻找最近点对时,相较于寻找一维的最近点来说,二维的最近点对寻找要困难许多,难点在于分界线周围的点的处理,即跨分治区域的点的比较
2、 可证明,处理δ*2δ区间内的点时,只需处理与当前点递增相连的7个点即可。因此可以大大减少开销,提高算法效率,改进算法时间复杂度。
3、 可对点对进行预排序,即在第一次递归调用前,对所有的点进行排序。预排序使运行时间增加了O(nlogn),但这样一来,除递归调用外,递归过程的每一步仅需线性时间。因此算法的整个时间复杂度为O(nlogn)


五、源代码(C++)

#include <iostream>  
#include <ctime>
#include <cmath>
#include <algorithm>

using namespace std;  

#define NO_DISTANCE 1000000

//定义二维点Point
typedef struct Point 
{    
    float x,y;     //二维点的横纵坐标,范围均为[-100,100]
}Point;

//用随机函数对点数组points中的二维点进行初始化
void SetPoints(Point *points,int length)
{
    srand(unsigned(time(NULL)));  //设置随机种子
    for(int i=0;i<length;i++)
    {
        points[i].x=(rand()%20000)/100.0-100;    //调整rand(),使得横纵坐标范围为[-100,100]
        points[i].y=(rand()%20000)/100.0-100;
    }

}

//平面上任意两点对之间的距离公式计算
float Distance(Point a,Point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

//自定义排序规则:依照结构体中的x成员变量升序排序
bool CmpX(Point a,Point b)
{
    return a.x<b.x;
}

//求出最近点对记录,并将两点记录再a、b中
float ClosestPair(Point points[],int length,Point &a,Point &b)
{
    float distance;        //记录集合points中最近两点距离 
    float d1,d2;           //记录分割后两个子集中各自最小点对距离
    int i=0,j=0,k=0;       //用于控制for循环的循环变量
    Point a1,b1,a2,b2;     //保存分割后两个子集中最小点对

    if(length<2)return NO_DISTANCE;    //若子集长度小于2,定义为最大距离,表示不可达
    if(length==2)
    {
        a=points[0];
        b=points[1];
        distance=Distance(points[0],points[1]);
    }
    else
    {
        Point *pts1=new Point[length];     //开辟两个子集
        Point *pts2=new Point[length];  

        sort(points,points+length,CmpX);   //调用algorithm库中的sort函数对points进行排序,CmpX为自定义的排序规则
        float mid=points[(length-1)/2].x;  //排完序后的中间下标值,即中位数

        for(i=0;i<length/2;i++)
            pts1[i]=points[i];
        for(int j=0,i=length/2;i<length;i++)
            pts2[j++]=points[i];
        d1=ClosestPair(pts1,length/2,a1,b1);           //分治求解左半部分子集的最近点  
        d2=ClosestPair(pts2,length-length/2,a2,b2);    //分治求解右半部分子集的最近点  
        if(d1<d2) { distance=d1; a=a1; b=b1;}
        else { distance=d2; a=a2; b=b2;}

        //求解跨分割线并在δ×2δ区间内的最近点对
        Point *pts3=new Point[length];   
        for(i=0,k=0;i<length;i++)
            if(abs(points[i].x-mid)<=distance)pts3[k++]=points[i];

        for(i=0;i<k;i++)
            for(j=i+1;j<=i+7&&j<k;j++)    //只需与有序的领接的的7个点进行比较
            {
                if(Distance(pts3[i],pts3[j])<distance)
                {//如果跨分割线的两点距离小于已知最小距离,则记录该距离
                    distance=Distance(pts3[i],pts3[j]);
                    a=pts3[i];
                    b=pts3[j];
                }
            }
    }
    return distance;
}

int main()
{
    int N;      //随机生成的点对个数
    Point a,b;
    float diatance;

    cout<<"请您输入二维点对个数:";
    cin>>N;
    if(N<2)
        cout<<"请输入大于等于2的点个数!!"<<endl;
    else
    {
        cout<<endl<<"随机生成的"<<N<<"个二维点对如下:"<<endl;
        Point *points=new Point[N];

        SetPoints(points,N);

        for(int i=0;i<N;i++)
            cout<<"("<<points[i].x<<","<<points[i].y<<")"<<endl;

        diatance=ClosestPair(points,N,a,b);

        cout<<endl<<endl<<"按横坐标排序后的点对:"<<endl;
        for(int i=0;i<N;i++)
        {
            cout<<"("<<points[i].x<<","<<points[i].y<<")"<<endl;
        }
        cout<<endl<<"最近点对为:"<<"("<<a.x<<","<<a.y<<")和"<<"("<<b.x<<","<<b.y<<")"<<endl<<"最近点对距离为:"<<diatance<<endl;
    }
        system("pause");
}
  • 19
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
### 回答1: 开源c算法csdn是一个编程学习和交流的平台,主要关注开源的C语言算法和数据结构。在这个平台上,学习者可以找到大量的开源C语言算法代码,可以通过阅读这些源码来学习C语言的算法实现和优化方法。同时,学习者也可以在csdn上分享自己的C语言算法实现,通过与其他开发者的交流和讨论来不断提高自己的编程能力。 在开源c算法csdn上,你可以找到各种各样的算法实现,包括排序算法、查找算法、图算法、字符串算法等等。通过研究和使用这些算法,你可以更好地理解算法的原理和应用场景,提高编程的效率和质量。 除了算法代码,开源c算法csdn还提供了丰富的教程和学习资源,例如算法导论算法设计与分析等方面的书籍推荐,以及算法相关的博文和技术文章。借助这些资源,你可以系统地学习和掌握C语言算法的知识。 此外,开源c算法csdn还建立了一个活跃的社区,你可以在社区中与其他学习者和专业人士交流和讨论。在这个社区中,你可以提问疑惑、解答他人的问题、分享经验和思考等。 总之,开源c算法csdn为学习者提供了一个便捷的平台,让他们能够学习和交流C语言算法知识,不断提升自己的编程水平。无论是初学者还是专业人士,都可以从中获益,并找到解决问题和提高技术的途径。 ### 回答2: 开源C算法是指基于C语言开发的算法代码具有开源性质,可以在网上自由获取和使用。CSDN是一个面向IT技术人员的专业技术社区和论坛,提供了海量技术博文、论坛问答、源代码下载等服务。 在CSDN上,开源C算法的相关资源非常丰富。首先,CSDN上有很多开发者和技术专家分享了自己开发的开源C算法代码,这些代码可以直接下载和使用,节省了开发者的时间和精力。 其次,CSDN上有很多关于C算法的教程和学习资料,可以帮助开发者更好地了解和学习C算法的原理和实现方法。这些教程通常由经验丰富的技术专家编写,内容详尽,易于理解。 此外,CSDN上还有很多开源C算法相关的讨论和问答,可以帮助开发者解决遇到的问题和困惑。在这些讨论中,开发者们可以相互交流经验,共同探讨C算法的优化和改进方法。 总的来说,CSDN为广大开发者提供了一个交流与学习的平台,通过CSDN,开发者可以获取开源的C算法代码、学习C算法的知识和技巧、解决C算法中的问题。这对于提高开发者的技术水平和开发效率都有着重要的意义。 ### 回答3: 开源C算法是指将C语言编写的算法公开并允许其他人自由使用、修改和发布的行为。CSDN是一个为程序员提供学习、交流和分享的开发者社区。 在CSDN上,许多开源的C算法可以找到。这些算法通常由广大开发者们共享而来,涵盖了各种领域和应用。开源C算法在CSDN上的发布,有助于推动技术的进步和分享,让更多人受益。 通过在CSDN上共享开源C算法,有以下几个好处: 1. 学习交流:开发者可以通过学习他人的开源C算法来扩展自己的知识和技能。CSDN提供了评论、留言和私信等交流方式,可以使开发者之间更好地分享和讨论,提高学习效果。 2. 节省时间:利用别人已经实现并公开的开源C算法,可以避免从零开始编写相同的功能,节省了开发时间和精力。 3. 质量保证:开源的C算法通常经过了众多开发者的反复测试和优化,更加稳定和可靠,可以提高程序的质量和性能。 4. 助于解决问题:在CSDN上,开发者可以提出自己遇到的问题并求助于其他开发者。其他开发者可以通过提供已有的开源C算法或给出实现思路来帮助解决问题。 5. 社区贡献:通过分享自己编写的开源C算法,开发者不仅可以获得他人的反馈和支持,还可以为整个开发者社区做出贡献,促进技术的共同进步。 总的来说,开源C算法通过CSDN的平台,促进了开发者之间的学习交流和分享,提高了开发效率和质量,同时也为整个开发者社区带来了相互支持和共同进步的机会。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值