最小距离点对的暴力枚举算法与分治算法

​最小距离点对问题,顾名思义,即在一堆点中,寻找最小距离的点对。
暴力枚举的时间复杂度为O(n2),分治算法的时间复杂度为O(nlogn)。

以下解决的问题是:

生成任意多个随机点,找到最近的点对后,将这对点排除在外,继续寻找剩下的点的最近的点对,重复上述操作,直到把所有点排除完毕(个数剩下为1,也停止操作)。其中,输出两种算法的执行时间,暴力枚举法用白线连接,分治算法用红线连接,用白线来验证红线的结果是否正确。

视频:https://zhuanlan.zhihu.com/p/711430070

/// <summary>
/// Find Closest Point Pair Using Divide and Conqure Method, time complex is O(nlogn).
/// </summary>
/// <param name="pts"></param>
/// <returns></returns>
public static DbPt[] ClosestPtPair(List<DbPt> pts)
{
    //移除重复点
    GMath.DelSamePt(pts);
    //基本条件
    if (pts.Count <= 3)
    {
        return ClosestPtPairBruteForce(pts);
    }
    List<DbPt> ptsX = (from n in pts
                       select n.EleCopy()).OrderBy(p => p.X).ThenBy(p=>p.Y).ToList();
    //List<DbPt> ptsY = (from n in pts
    //                   select n.EleCopy()).OrderBy(p => p.Y).ToList();
    //return ClosestPtPair(ptsX, ptsY);
    return ClosestPtPairTemp(ptsX);

}
static DbPt[] ClosestPtPairTemp(List<DbPt> ptsX)
{
    //DbPt[] nearestL=null, //最近左点对
    //    nearestR = null,//最近右点对
    //    nearestSplit=null;//最近分离点对
    if (ptsX.Count <= 3)
    {
        return ClosestPtPairBruteForce(ptsX);
    }
    else
    {
        int mid = ptsX.Count / 2;
        List<DbPt> Lx = ptsX.Take(mid).ToList();
        //List<DbPt> Ly = (from n in Lx select n).OrderBy(p=>p.Y).ToList();
        List<DbPt> Rx = ptsX.Skip(mid).Take(ptsX.Count - mid).ToList();
        //List<DbPt> Ry = (from n in Rx select n).OrderBy(p=>p.Y).ToList();

        DbPt[] nearestL = ClosestPtPairTemp(Lx);
        DbPt[] nearestR = ClosestPtPairTemp(Rx);
        double delta = Math.Min(
            GMath.Distance(nearestL[0], nearestL[1]),
            GMath.Distance(nearestR[0], nearestR[1])
            ) ;
        DbPt[] nearestSplit = ClosestSplitPair(ptsX,delta);
        List<DbPt[]> candidates = new List<DbPt[]>
        {
            nearestL,nearestR
        };
        if (nearestSplit != null)
        {
            candidates.Add(nearestSplit);
        }
        candidates.Sort((a, b) => GMath.Distance(a[0], a[1]).CompareTo(GMath.Distance(b[0], b[1])));
        return candidates[0];

    }
   

}
static DbPt[] ClosestSplitPair(List<DbPt> ptsX,double delta,double allow=0.01)
{
    int mid = ptsX.Count / 2;
    double xMid = ptsX[mid].X;
    List<DbPt> Sy = (from n in ptsX
                     where n.X <= xMid + delta && n.X >= xMid - delta
                     select n).OrderBy(p=>p.Y).ToList();
    double best = delta;
    DbPt[] result = null;
    for(int i = 0; i < Sy.Count; i++)
    {
        for(int j = 1; j < Math.Min(7, Sy.Count - i); j++)
        {
            if (GMath.Distance(Sy[i], Sy[i + j]) < best)
            {
                best = GMath.Distance(Sy[i], Sy[i+j]);
                result = new DbPt[]
                {
                    Sy[i],Sy[i+j]
                };
            }
        }
    }


    return result;
}
/// <summary>
/// Find Closest Point Pair Using Brue Force Method
/// </summary>
/// <param name="pts"></param>
/// <returns></returns>
public static DbPt[] ClosestPtPairBruteForce(List<DbPt> pts)
{
    List<Tuple<DbPt, DbPt>> ptsPair = new List<Tuple<DbPt, DbPt>>();
    for (int i = 0; i < pts.Count; i++)
    {
        for (int j = i + 1; j < pts.Count; j++)
        {
            ptsPair.Add(Tuple.Create(pts[i], pts[j]));
        }
    }
    var tup = ptsPair.OrderBy(t => GMath.Distance(t.Item1, t.Item2)).FirstOrDefault();
    if (tup != null)
    {
        return new DbPt[] { tup.Item1, tup.Item2 };

    }
    return null;
}
posted @ 2024-07-28 23:58  JohnYang819  阅读(11)  评论(0编辑  收藏  举报