分治法求最近点对问题

目录

 

蛮力法

分治法

探究分治规模小于一定程度时采用暴力解法


蛮力法

  • 算法思想

蛮力法,顾名思义,即穷举所有点与点之间的距离,两层循环暴力找出最近点对。算法执行可视化如图1所示,word文档GIF静态显示,附件已含动图。

图1

  • 伪代码

matlab代码 

result=[];
for power=1:10
    scale=power*100000;
    count=0;
    for times=1:20
        x=randi(scale,1,scale);
        y=randi(scale,1,scale);
        tic;
        [i,j]=brute(x,y,scale);
        count=count+toc;           
    end
    count=count/20;
    result=[result,count];
end
function [mini,minj]=brute(x,y,scale)
    mini=1;
    minj=1;
    minDistance=Inf;
    for i=1:scale-1
        for j=1:scale
            if i==j
                break
            end
            distance=(x(i)-x(j))^2+(y(i)-y(j))^2;
            if distance<minDistance
                mini=i;
                minj=j;
                minDistance=distance;
            end
        end
    end
end
  • 实验结果

环境为MATLAB,数据规模为1w到5w,运行结果如图2所示。

图2

具体数据如表1所示。

表1

分析:

由实验结果可知,蛮力法的实验值与理论值基本一致,算法的时间复杂度确实为O(n2),确实很慢。

分治法

  • 算法思想

先对点进行预处理按横坐标排序,然后每次将点均分成左右两个子集,最短距离的两个点要么都在左子集,要么都在右子集,要么一个点在左子集中,一个点在右子集中,对于前面两种情况,问题变成递归寻找子集的最短距离,算法执行可视化如图3所示,word文档GIF静态显示,附件已含动图。

图3

而对于跨越中间线的情况,由左右两个子集可以算出一个目前最短距离minDistance,然后将距离中间点的距离小于minDistance的点找出来,如图4所示。

图4

如果存在最短距离,那么一定是一边一个点,所以我们需要将两边点的距离算一下,实际上,我们需要对于一边的点,我们需要计算距离的点最多不超过4个,因为同一边的点与点之间的距离肯定大于等于minDistance,所以对于另一边的点来说,范围小于minDistance内的点不会超过4个,如图5所示。

图5

  • 伪代码

matlab代 

result=[];
for power=1:10
    scale=power*100000;
    count=0;
    for times=1:20
        x=randi(scale,1,scale);
        y=randi(scale,1,scale);
        tic;
        [x,y]=Quick(1,scale,x,y);
        [mini,minj,minDistance]=divide(x,y,scale);
        count=count+toc;           
    end
    count=count/20;
    result=[result,count];
end
function [mini,minj,minDistance]=brute(x,y,scale)
    mini=1;
    minj=2;
    minDistance=Inf;
    for i=1:scale-1
        for j=i+1:scale
            distance=(x(i)-x(j))^2+(y(i)-y(j))^2;
            if distance<minDistance
                mini=i;
                minj=j;
                minDistance=distance;
            end
        end
    end
end
function [mini,minj,minDistance]=divide(x,y,scale)
    if length(x)<3
        [mini,minj,minDistance]=brute(x,y,scale);
        return;
    end
    half=floor(scale/2);
    [i,j,minLeft]=divide(x(1:half),y(1:half),half);
    [ii,jj,minRight]=divide(x(half+1:scale),y(half+1:scale),scale-half);
    if minLeft<minRight
        minDistance=minLeft;
        mini=i;
        minj=j;
    else
        minDistance=minRight;
        mini=ii;
        minj=jj;
    end
    left=[];
    right=[];
    for i=half-1:-1:1
        if abs(x(i)-x(half))>=sqrt(minDistance)
            break
        end
        left=[left,i];
    end
    for i=half+1:scale
        if abs(x(i)-x(half))>=sqrt(minDistance)
            break
        end
        right=[right,i];
    end
    for i=1:length(left)
        for j=1:length(right)
            if j>3
                break
            end
            distance=(x(left(i))-x(right(j)))^2+(y(left(i))-y(right(j)))^2;
            if distance<minDistance
                mini=left(i);
                minj=right(j);
                minDistance=distance;
            end
        end
    end
    for i=1:length(right)
        for j=1:length(left)
            if j>3
                break
            end
            distance=(x(left(j))-x(right(i)))^2+(y(left(j))-y(right(i)))^2;
            if distance<minDistance
                mini=left(j);
                minj=right(i);
                minDistance=distance;
            end
        end
    end
end
function[x,y]=Quick(low,high,x,y)
    i=low;
    j=high;
    pivot=x(low);
    temp=y(low);
    while low<high
        while low<high&&pivot<=x(high)
            high=high-1;
        end
        if low<high
            x(low)=x(high);
            y(low)=y(high);
            low=low+1;
        end
        while low<high&&pivot>x(low)
            low=low+1;
        end
        if low<high
            x(high)=x(low);
            y(high)=y(low);
            high=high-1;
        end
    end
    x(low)=pivot;
    y(low)=temp;
    if i<low-1
        [x,y]=Quick(i,low-1,x,y);
    end
    if high+1<j
        [x,y]=Quick(high+1,j,x,y);
    end
end
  • 算法复杂度

对于数据规模为n的情况,二分的次数为log2n次,而计算跨中间线距离的时候计算次数小于3n,即此处的时间复杂度是线性的,即T(n)=T(n/2)+O(n),可算得T(n)=nlogn。

  • 实验结果

先在小规模数据上跑,环境为MATLAB,数据规模为1w到5w,运行结果如图6所示。

图6

具体数据如表2所示。

表2

数据规模为10w到100w,运行结果如图7所示。

图7

具体数据如表3所示。

表3

分析:

由实验结果可知,分治法明显远远快于蛮力法,小规模数据时实验值略小于理论值,大规模时实验值与理论值基本一致。

探究分治规模小于一定程度时采用暴力解法

由于分治时不断递归调用函数,程序开销较大,考虑当分治到数据规模小于一定程度时采用暴力解法的运行效果,数据规模为1w,参数设置100到1000测试,结果如图8所示。

图8

由实验结果可知,分治规模达到200时使用暴力效果最佳,将参数设置为200,在数据规模为1w到5w上与原始分治法对比,如图9所示。

图9

在数据规模为10w到100w上与原始分治法对比,如图10所示。

图10

分析:

由实验结果可知,在分治规模小于一定数量时采用暴力求解效率更快,特别是在数据规模大的时候,这种暴力分治相结合的方法相比原始分治法具有很大的优势。

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
递归和分治算法是计算机科学中常用的算法设计方法,它们通常用于解决复杂的问题。在算法设计实验中,我们可以通过分析递归和分治算法的性能以及实现过程来更深入地理解它们的原理和应用。 下面是针对递归和分治算法设计实验的分析: 1. 算法实现:在实验中,我们需要实现递归和分治算法。递归算法通常包含一个基本情况和一个递归情况。基本情况是结束递归的条件,而递归情况是通过调用自己来解决问题。分治算法通常包含三个步骤:分解问题、解决问题和合并结果。在分解问题的过程中,将原问题划分为若干个子问题,然后递归地解决子问题。在解决问题的过程中,对每个子问题进行解。在合并结果的过程中,将子问题的结果合并成原问题的解。 2. 算法性能:在分析算法性能时,我们需要考虑算法的时间复杂度和空间复杂度。递归算法的时间复杂度通常与递归深度有关,而分治算法的时间复杂度通常与问题规模有关。空间复杂度通常与算法的递归深度和使用的数据结构有关。在实验中,我们可以通过比较递归和分治算法的时间复杂度和空间复杂度来评估它们的性能。 3. 算法应用:递归和分治算法在实际应用中都有广泛的应用。递归算法适用于具有递归结构的问题,例如树和图。分治算法适用于可以分解为若干个子问题问题,例如排序、查找和计算几何等问题。在实验中,我们可以通过应用递归和分治算法来解决不同类型的问题,例如二叉树的遍历、归并排序和最近问题等。 总之,递归和分治算法是计算机科学中非常重要的算法设计方法。通过实验,我们可以更好地理解它们的原理和应用,并且能够更加深入地研究算法的性能和实现过程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaolinYe(叶茂林)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值