最近點對問題[CPP]C# N*LogN複雜度解法

using System;
using System.Collections.Generic;

public class UTest
{
   
static void Main()
    {
        Vec2[] points
= new Vec2[ 20000 ];
        Random random
= new Random(123456);
       
for (int i = 0; i < points.Length; i++)
        {
            points[i]
= new Vec2(random.Next(200000) / 100.0f, random.Next(200000) / 100.0f);

        }
       
int startTick, endTick;

        startTick
= System.Environment.TickCount;
       
float divideconquer = (float)Math.Sqrt((double)MinDistanceSquared(points));         // 140ms
        endTick = System.Environment.TickCount;
        Console.WriteLine(
"Divide and conquer finishes in {0,6}ms, result={1}", endTick - startTick, divideconquer);

        startTick
= System.Environment.TickCount;
       
float bruteforce = (float)Math.Sqrt((double)BruteForceMinDistanceSquared(points));  // 13200ms
        endTick = System.Environment.TickCount;
        Console.WriteLine(
"Brute force method finishes in {0,6}ms, result={1}", endTick - startTick, bruteforce);
    }

   
static float MinDistanceSquared(Vec2[] points)
    {
       
// 如果點的數目少於一定數量,直接窮舉最短距離
        if (points.Length < 6) return BruteForceMinDistanceSquared(points);

       
// 將點按照x軸排序。並分成左邊部分,和右邊部分。
        Array.Sort<Vec2>(points, Vec2.CompareByX);
       
int middleIdx = points.Length / 2;
        Vec2[] leftPoints
= new Vec2[middleIdx];
        Vec2[] rightPoints
= new Vec2[points.Length - middleIdx];
       
for (int i = 0; i < leftPoints.Length; i++)
        {
            leftPoints[i]
= new Vec2(points[i].y, points[i].x);
        }
       
for (int i = 0; i < rightPoints.Length; i++)
        {
            rightPoints[i]
= new Vec2(points[i + middleIdx].y, points[i + middleIdx].x);
        }

       
// 分而治之: 算出左邊部分的最短距離和右邊部分的最短距離。
        float l = MinDistanceSquared(leftPoints);
       
float r = MinDistanceSquared(rightPoints);
       
float minDistance = Math.Min(l, r);

       
// 靠近中間線的點,有可能跨越中間線出現更短的距離。
       
// 但該中間區域有很大侷限。假定最短距離是s,中線的x爲X,則該區域限定在X-S ~ X+S之間
        float middle = points[middleIdx].x;
        List
<Vec2> middleStrip = new List<Vec2>();
       
foreach (Vec2 p in points)
        {
           
if ((p.x - middle) * (p.x - middle) < minDistance) middleStrip.Add(new Vec2(p.y, p.x));
        }

       
// 不同於wiki的算法描述,這裏再次採用分而治之的原則(代碼簡潔很多),算出中間地帶的最短距離
        float m = MinDistanceSquared(middleStrip.ToArray());

       
return Math.Min(minDistance, m);
    }

   
static float BruteForceMinDistanceSquared(Vec2[] points)
    {
       
float minDistance = float.MaxValue;
       
// 窮舉法,O(n*n)
        for (int i = 0; i < points.Length; i++)
        {
           
for (int j = i + 1; j < points.Length; j++)
            {
               
float distance = (points[i] - points[j]).LengthSquare();
               
if (distance < minDistance) minDistance = distance;
            }
        }
       
return minDistance;
    }
}

struct Vec2
{
   
public float x, y;
   
public Vec2(float x, float y)
    {
       
this.x = x; this.y = y;
    }
   
public float LengthSquare()
    {
       
return x * x + y * y;
    }
   
public static int CompareByX(Vec2 v1, Vec2 v2)
    {
       
return v1.x - v2.x > 0 ? 1 : (v1.x - v2.x == 0 ? 0 : -1);
    }
   
public static Vec2 operator -(Vec2 v1, Vec2 v2)
    {
       
return new Vec2(v1.x - v2.x, v1.y - v2.y);
    }
}

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