【分治法】求平面點集距離最近的兩個點及其距離

問題:平面點集求其中距離最近的兩個點及其距離。

思路:採用分治法,將“求n個點之間最小距離”問題劃分爲很多個“求n/t個點之間最小距離”問題。

(1)將lstPoint根據X座標由小到大排序得到點集pointsSortedX,方法很多,冒泡、選擇、插入、歸併,快排等,本文采用快排,其優點就不多說了。

(2)pointsSortedX爲一個點集,可以採用二分法分爲兩個數量均分的點集pointsX1([0,indexMid]),pointsX2([indexMid,Count-1]),對於每個分點集,求其中距離最近的兩個點及其距離,方法如前,採用遞歸求解;

(3)對於2個分點集,可得兩個最小距離,取其小者,爲分點集各自內的最小距離dis,但是仍然存在這種情況,即可能存在兩個點分別位於兩個分點集中,所以還需考慮2個分點集間的情況。而參考已求得的dis,2個分點集間區域可根據X座標取

X∈[pointsSortedX[indexMid].X - dis,pointsSortedX[indexMid].X + dis]範圍的點並構成臨時點集pointsTemp;

(4)將點集pointsTemp根據Y座標進行由小到大排序,對於每個點ptCurrent,求Y座標∈[ptCurrent.Y - dis, ptCurrent.Y + dis]的點ptCurrent的距離,並與dis取最小賦給dis;

圖解如下:

 

圖1 根據X座標排序

圖2 二分法分解

圖3 遞歸求解並考慮區域間點距離最小情況

圖4 代碼運行效果

public class ShortestDisPointFinder
{
   public delegate bool CompareDelegate(HPoint2d point0, HPoint2d point1);
   public class ShortestDisPointResult
   {
      public ShortestDisPointResult(double dis, HPoint2d point0, HPoint2d point1)
      {
         Distance = dis;
         Point0 = point0;
         Point1 = point1;
      }

      public double Distance { get; set; }
      public HPoint2d Point0 { get; set; }
      public HPoint2d Point1 { get; set; }
   }

   private List<HPoint2d> m_lstPoint;
   public ShortestDisPointFinder(List<HPoint2d> lstPoint)
   {
      m_lstPoint = new List<HPoint2d>(lstPoint);
   }

   public ShortestDisPointResult GetShortestDistance()
   {
      //  首先按X座標排序
      QuikSort(m_lstPoint, 0, m_lstPoint.Count - 1, CompareX);

      //  分治法求最短距離
      return Binarysearch(m_lstPoint, 0, m_lstPoint.Count - 1);
   }

   private ShortestDisPointResult Binarysearch(List<HPoint2d> lstPoint, int indexS, int indexE)
   {
      if (indexS + 1 == indexE)
            return new
ShortestDisPointResult(lstPoint[indexE].Distance(lstPoint[indexS]), lstPoint[indexS], lstPoint[indexE]);
      else if (indexS + 2 == indexE)
      {
         double dis1 = lstPoint[indexE].Distance(lstPoint[indexS]);
         double dis2 = lstPoint[indexS + 1].Distance(lstPoint[indexS]);
         double dis3 = lstPoint[indexS + 1].Distance(lstPoint[indexE]);
         var result = new ShortestDisPointResult(dis1, lstPoint[indexS], lstPoint[indexE]);
         if (result.Distance > dis2)
         {
            result.Distance = dis2;
            result.Point0 = lstPoint[indexS];
            result.Point1 = lstPoint[indexS + 1];
         }

         if (result.Distance > dis3)
         {
            result.Distance = dis3;
            result.Point0 = lstPoint[indexS + 1];
            result.Point1 = lstPoint[indexE];
         }
         return result;
      }

      ShortestDisPointResult resultRe = null;
      int indexM = (indexE + indexS) / 2;
      var result1 = Binarysearch(lstPoint, indexS, indexM);
      var result2 = Binarysearch(lstPoint, indexM, indexE);
      if (result1.Distance < result2.Distance)
         resultRe = result1;
      else
         resultRe = result2;

      //  判斷兩區域之間X座標在[indexM].X - dis到[indexM].X + dis內是否存在距離比dis小的兩個點
      //  找出X座標在[indexM].X - dis到[indexM].X + dis內的點
      var lstPtTemp = new List<HPoint2d>();
      for(int index = indexS; index <= indexE; index++)
      {
         if (Math.Abs(lstPoint[index].X - lstPoint[indexM].X) < resultRe.Distance)
            lstPtTemp.Add(lstPoint[index]);
      }

      //  將點集按Y座標排序
      QuikSort(lstPtTemp, 0, lstPtTemp.Count - 1, CompareY);

      //  對於點集中的任一點,其它點若在該點的[Y - dis, Y + dis]內即判斷與該點距離
      for (int i = 0; i < lstPtTemp.Count - 1; i++)
      {
         for (int j = i + 1; j < lstPtTemp.Count; j++)
         {
            if (lstPtTemp[j].Y - lstPtTemp[i].Y < resultRe.Distance)
            {
               var disTemp = lstPtTemp[i].Distance(lstPtTemp[j]);
               if (resultRe.Distance > disTemp)
               {
                  resultRe.Distance = disTemp;
                  resultRe.Point0 = lstPtTemp[i];
                  resultRe.Point1 = lstPtTemp[j];
               }
            }
            else
               break;
         }
      }

      return resultRe;
   }

   private bool CompareX(HPoint2d pointL, HPoint2d pointR)
   {
      if (pointL.X <= pointR.X)
         return true;

      return false;
   }

   private bool CompareY(HPoint2d pointL, HPoint2d pointR)
   {
      if (pointL.Y <= pointR.Y)
         return true;

      return false;
   }

   public void QuikSort(List<HPoint2d> lstPoint, int indexS, int indexE, CompareDelegate compare)
   {
      if (indexE <= indexS)
         return;

      int indexP = Partition(lstPoint, indexS, indexE, compare);
      QuikSort(lstPoint, indexS, indexP - 1, compare);
      QuikSort(lstPoint, indexP + 1, indexE, compare);
   }

   public int Partition(List<HPoint2d> lstPoint, int indexS, int indexE, CompareDelegate compare)
   {
      HPoint2d ptBase = lstPoint[indexE];
      int i = indexS - 1;
      for(int j = indexS; j < indexE; j++)
      {
         if(compare(lstPoint[j], ptBase))
         {
            i++;
            var ptTemp = lstPoint[i];
            lstPoint[i] = lstPoint[j];
            lstPoint[j] = ptTemp;
         }
      }

      var ptTemp2 = lstPoint[i + 1];
      lstPoint[i + 1] = lstPoint[indexE];
      lstPoint[indexE] = ptTemp2;

      return i + 1;
   }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章