zoukankan      html  css  js  c++  java
  • 平面最小点对距:分治法

    平面最小点对距:分治法

    时间:2020.03.21

    地点:深圳

    语言:C#

    IDE: VS2019

    概述:用分治法求平面内离散的点求最小点对距

    1. 分治法

    最小点对距:分治法.png

    语言实现详细步骤:

    1、设计点对距数据结构{点距,起点,终点}
    2、从txt读取点集合
    3、点集合排序
    4、获取中点索引,划分左右区域
    5、左右区域求最小点对距
    6、比较左右区域最小点对距,选取最小值作为中线缓冲距离
    7、获取缓冲区点集合
    8、缓冲区点集合求最小点对距
    9、比较左、中、右点对距,选择最小点对距
    

    PointPairDis.cs

    using System;
    
    namespace 点线面
    {
        public partial class PointPairDis
        {
            #region 字段属性
            private double distance;
            private Point pointFrom;
            private Point pointTo;
    
            public double Distance { get => distance; set => distance = value; }
            public Point PointFrom { get => pointFrom; set => pointFrom = value; }
            public Point PointTo { get => pointTo; set => pointTo = value; }
            #endregion
    
            #region 方法
            public void WriteLine()
            {
                Console.WriteLine("{0}	{1}	{2}	",
                    Distance, PointFrom.PointName, PointTo.PointName);
                Console.WriteLine("-----------------------");
            }
    
            public void MinPointPairDis(Points points)
            {
                #region 变量
                int startIndex;
                int midIndex;
                int endIndex;
                double buffer;
                PointPairDis pointPairDis_left = null;
                PointPairDis pointPairDis_mid = null;
                PointPairDis pointPairDis_right = null;
                #endregion
    
                //排序
                points.SortPoints();
                //获取中线缓冲距离
                points.GetIntervalIndex(out startIndex, out midIndex, out endIndex);
                pointPairDis_left =points.GetMinPointsDis(startIndex, midIndex);
                pointPairDis_right =points.GetMinPointsDis(midIndex, endIndex);
                if (pointPairDis_left.Distance < pointPairDis_right.Distance)
                    buffer = pointPairDis_left.Distance;
                else
                    buffer = pointPairDis_right.Distance;
                //获取中线区域最小点对距
                points.GetIntervalIndex(out startIndex, out midIndex, out endIndex, buffer);
                pointPairDis_mid =points.GetMinPointsDis(startIndex, endIndex);
                //最小点对距
                if (pointPairDis_left.Distance < pointPairDis_right.Distance)
                {
                    if (pointPairDis_mid.Distance < pointPairDis_left.Distance)
                        pointPairDis_mid.WriteLine();
                    else
                        pointPairDis_left.WriteLine();
                }
                else
                {
                    if (pointPairDis_mid.Distance < pointPairDis_right.Distance)
                        pointPairDis_mid.WriteLine();
                    else
                        pointPairDis_right.WriteLine();
                }
            }
            #endregion
        }
    }
    

    Points类内的方法:

    #region 最小点对距
        public void SortPoints()
    {
        pointList.Sort(new ComparerPoints());
    }
    
    public void GetIntervalIndex(out int startIndex, out int midIndex,
                                 out int endIndex, double buffer = 0)
    {
        startIndex = 0;
        endIndex = pointList.Count - 1;
        if (pointList.Count % 2 == 0)
            midIndex = pointList.Count / 2;
        else
            midIndex = (pointList.Count + 1) / 2; ;
        if (buffer != 0)
        {
            //左缓冲
            for (int i = 1; i < midIndex; i++)
            {
                double disLeft2Xmiddle = pointList[midIndex].PointX
                    - pointList[midIndex - i].PointX;
                if (disLeft2Xmiddle >= buffer)
                {
                    startIndex = midIndex - i;
                    break;
                }
            }
            //右缓冲
            for (int i = 1; i < midIndex; i++)
            {
                double disRigth2Xmiddle = pointList[midIndex + i].PointX
                    - pointList[midIndex].PointX;
                if (disRigth2Xmiddle >= buffer)
                {
                    endIndex = midIndex + i;
                    break;
                }
            }
        }
    }
    
    /// <summary>
    /// 获取区间范围最小点对距
    /// </summary>
    /// <param name="start">起点索引</param>
    /// <param name="end">终点索引</param>
    /// <returns>点对距</returns>
    /// <remarks>20200318 刘小贝</remarks>
    public PointPairDis GetMinPointsDis(int start, int end)
    {
        PointPairDis pointPairDis = new PointPairDis
        {
            Distance = pointList[start].Distance2Point(pointList[start + 1]),
            PointFrom = pointList[start],
            PointTo = pointList[start + 1]
        };
    
        for (int i = start; i < end; i++)
        {
            for (int j = i + 1; j <= end; j++)
            {
                double dis = pointList[i].Distance2Point(pointList[j]);
                if (dis < pointPairDis.Distance)
                {
                    pointPairDis.Distance = dis;
                    pointPairDis.PointFrom = pointList[i];
                    pointPairDis.PointTo = pointList[j];
                }
            }
        }
        return pointPairDis;
    } 
    #endregion
    

    IComparer接口实现:

    public class ComparerPoints : IComparer<Point>
    {
        int IComparer<Point>.Compare(Point x, Point y)
        {
            if (x.GetPointX() < y.GetPointX())
            {
                return -1;
            }
            else
                return 1;
        }
    }
    

    运行结果为:

    平面最小点对距

    2. 旋转法

    (1)以横坐标为关键字,将点的坐标排序

    (2)把坐标轴旋转任意角度

    (3)以横坐标为关键字,将n点的坐标排序

    这是数学上的旋转公式

    x1=x*cos(ds)-y*sin(ds);
    y1=x*sin(ds)+y*cos(ds);
    

    代码为:

    作者:硝基苯张
    链接:https://zhuanlan.zhihu.com/p/77472546
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<ctime>
    #include<cstdlib>
    #define db double
    #define ll unsigned long long
    using namespace std;
    int n;db ans=4e20;
    struct E{
        db x,y;
    }a[200001];
    bool cmp(E t1,E t2)
    {
        return t1.x<t2.x;
    }
    db minn(db a,db b)
    {
        if(a<b)return a;
        else return b;
    }
    void dfs(int x)
    {
        for(int i=x+1;i<=min(x+5,n);i++)
        {
            if(a[i].x-a[x].x>=ans)break;
            ans=minn(ans,(a[x].x-a[i].x)*(a[x].x-a[i].x)+(a[x].y-a[i].y)*(a[x].y-a[i].y));
        }
        //cout<<ans<<endl;
    }
    void around(int ds)
    {
    	for(int i=1;i<=n;i++)
    	{
    		db x=a[i].x,y=a[i].y;
    		a[i].x=x*cos(ds)-y*sin(ds);
    		a[i].y=x*sin(ds)+y*cos(ds);
    	}
    	sort(a+1,a+1+n,cmp);
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        scanf("%lf%lf",&a[i].x,&a[i].y);
        sort(a+1,a+1+n,cmp);
        for(int i=1;i<n;i++)dfs(i);
        around((rand()+1)%360-1);
        for(int i=1;i<n;i++)dfs(i);
        around(rand()%360);
        for(int i=1;i<n;i++)dfs(i);
        printf("%.4lf",sqrt(ans));
    }
    

    来源:知乎:硝基苯张

    其他参考:洛谷算法

  • 相关阅读:
    每日一题 为了工作 2020 0412 第四十一题
    每日一题 为了工作 2020 04011 第四十题
    每日一题 为了工作 2020 0410 第三十九题
    每日一题 为了工作 2020 0409 第三十八题
    每日一题 为了工作 2020 0408 第三十七题
    每日一题 为了工作 2020 0407 第三十六题
    每日一题 为了工作 2020 0406 第三十五题
    每日一题 为了工作 2020 0405 第三十四题
    学习总结(二十四)
    学习总结(二十三)
  • 原文地址:https://www.cnblogs.com/liuwenzhen/p/12537323.html
Copyright © 2011-2022 走看看