1.2.1

1.2.1 #

解答 #

这里自己实现了一个 Point2D 类(包含在了 Geometry 库中)。

JAVA 版本参考:http://algs4.cs.princeton.edu/12oop/Point2D.java.html

求最近两点只需要反复调用 Point2D 类中的 DistTo() 方法就可以了。

代码 #

Point2D 类 #

/// <summary>
/// Point2D 二维点类。
/// </summary>
/// <summary>
public sealed class Point2D : IComparable<Point2D>
{
    /// 以 X 坐标升序排序。
    /// </summary>
    /// <value>以 X 坐标升序排序的静态比较器。</value>
    public static readonly Comparer<Point2D> XOrderComparer = new XOrder();
    /// <summary>
    /// 以 Y 坐标升序排序。
    /// </summary>
    /// <value>以 Y 坐标升序排序的静态比较器。</value>
    public static readonly Comparer<Point2D> YOrderComparer = new YOrder();
    /// <summary>
    /// 以极半径升序排序。
    /// </summary>
    /// <value>以极半径升序排序的静态比较器。</value>
    public static readonly Comparer<Point2D> ROrderComparer = new ROrder();

    /// <summary>
    /// 二维点的 X 坐标。
    /// </summary>
    /// <value>X 坐标。</value>
    public double X { get; }
    /// <summary>
    /// 二维坐标的 Y 坐标。
    /// </summary>
    /// <value>Y 坐标。</value>
    public double Y { get; }
    /// <summary>
    /// 绘制时点的半径,以像素为单位,默认值为 2。
    /// </summary>
    /// <value>点的半径,以像素为单位。</value>
    public int Radius { get; set; }

    /// <summary>
    /// 构造一个二维点。
    /// </summary>
    /// <param name="x">点的 X 轴坐标。</param>
    /// <param name="y">点的 Y 轴坐标。</param>
    /// <exception cref="ArgumentException">当 <paramref name="x"/> 或 <paramref name="y"/> 为±无穷时抛出此异常。</exception>
    /// <exception cref="ArgumentNullException">当 <paramref name="x"/> 或 <paramref name="y"/> 为 <see cref="double.NaN"/> 时抛出。</exception>
    public Point2D(double x, double y)
    {
        if (double.IsInfinity(x) || double.IsInfinity(y))
        {
            throw new ArgumentException("x, y must be finite");
        }

        if (double.IsNaN(x) || double.IsNaN(y))
        {
            throw new ArgumentNullException(nameof(x), "Coordinate cannot be NaN");
        }

        X = x;
        Y = y;
        Radius = 2;
    }

    /// <summary>
    /// 返回极半径。
    /// </summary>
    /// <returns>极半径。</returns>
    public double R()
    {
        return Math.Sqrt(X * X + Y * Y);
    }

    /// <summary>
    /// 返回极角。
    /// </summary>
    /// <returns>极角。</returns>
    public double Theta()
    {
        return Math.Atan2(Y, X);
    }

    /// <summary>
    /// 返回两个点之间的角度。
    /// </summary>
    /// <param name="that">要计算角度的另一个点。</param>
    /// <returns>返回与点 <paramref name="that"/> 构成的角度。</returns>
    private double AngleTo(Point2D that)
    {
        var dx = that.X - X;
        var dy = that.Y - Y;
        return Math.Atan2(dy, dx);
    }

    /// <summary>
    /// 判断 a,b,c 三个点的关系,如果 {顺时针, 共线, 逆时针} 则返回 {-1, 0, 1}。
    /// </summary>
    /// <param name="a">第一个点。</param>
    /// <param name="b">第二个点。</param>
    /// <param name="c">第三个点。</param>
    /// <returns>如果 {顺时针, 共线, 逆时针} 则返回 {-1, 0, 1}</returns>
    public static int Ccw(Point2D a, Point2D b, Point2D c)
    {
        var area2 = (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X);
        if (area2 < 0)
            return -1;
        if (area2 > 0)
            return 1;
        return 0;
    }

    /// <summary>
    /// 返回 abc 三个点构成的三角形的面积的平方。
    /// </summary>
    /// <param name="a">第一个点。</param>
    /// <param name="b">第二个点。</param>
    /// <param name="c">第三个点。</param>
    /// <returns><paramref name="a"/> 和 <paramref name="b"/> 和 <paramref name="c"/> 构成的三角形的面积的平方。</returns>
    public static double Area2(Point2D a, Point2D b, Point2D c)
    {
        return (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X);
    }


    /// <summary>
    /// 返回当前点到另一个点之间的距离。
    /// </summary>
    /// <param name="that">另一个点。</param>
    /// <returns>返回到 <paramref name="that"/> 的距离。</returns>
    public double DistanceTo(Point2D that)
    {
        var dx = X - that.X;
        var dy = Y - that.Y;

        return Math.Sqrt(dx * dx + dy * dy);
    }

    /// <summary>
    /// 返回当前点到另一个点之间的距离的平方。
    /// </summary>
    /// <param name="that">另一个点。</param>
    /// <returns>到 <paramref name="that"/> 距离的平方。</returns>
    public double DistanceSquareTo(Point2D that)
    {
        var dx = X - that.X;
        var dy = Y - that.Y;

        return dx * dx + dy * dy;
    }

    /// <summary>
    /// 绘制二维点。
    /// </summary>
    /// <param name="g">原点在左下方,y轴向上,x轴向右的画布。</param>
    public void Draw(Graphics g)
    {
        g.FillEllipse(Brushes.Black, (int)X, (int)Y, Radius, Radius);
    }

    /// <summary>
    /// 优先按照 <see cref="Y"/> 比较,相同时再按照 <see cref="X"/> 比较。
    /// </summary>
    /// <param name="other">需要比较的另一个对象。</param>
    /// <returns>如果 <paramref name="other"/> 较小则返回 -1,反之返回 1,相等返回 0。</returns>
    public int CompareTo(Point2D other)
    {
        if (Y < other.Y)
            return -1;
        if (Y > other.Y)
            return 1;
        if (X < other.X)
            return -1;
        if (X > other.X)
            return 1;

        return 0;
    }

    /// <summary>
    /// 按照 X 顺序比较。
    /// </summary>
    private class XOrder : Comparer<Point2D>
    {
        public override int Compare(Point2D x, Point2D y)
        {
            if (x.X < y.X)
            {
                return -1;
            }

            if (x.X > y.X)
            {
                return 1;
            }

            return 0;
        }
    }

    /// <summary>
    /// 按照 Y 顺序比较。
    /// </summary>
    private class YOrder : Comparer<Point2D>
    {
        public override int Compare(Point2D x, Point2D y)
        {
            if (x.Y < y.Y)
            {
                return -1;
            }
            if (x.Y > y.Y)
            {
                return 1;
            }

            return 0;
        }
    }

    /// <summary>
    /// 按照极径顺序比较。
    /// </summary>
    private class ROrder : Comparer<Point2D>
    {
        public override int Compare(Point2D x, Point2D y)
        {
            var delta = (x.X * x.X + x.Y * x.Y) - (y.X * y.X + y.Y * y.Y);
            if (delta < 0)
            {
                return -1;
            }

            if (delta > 0)
            {
                return 1;
            }

            return 0;
        }
    }

    /// <summary>
    /// 按照 atan2 值顺序比较。
    /// </summary>
    private class Atan2Order : Comparer<Point2D>
    {
        private readonly Point2D _parent;

        public Atan2Order(Point2D parent)
        {
            _parent = parent;
        }
        public override int Compare(Point2D x, Point2D y)
        {
            var angle1 = _parent.AngleTo(x);
            var angle2 = _parent.AngleTo(y);
            if (angle1 < angle2)
            {
                return -1;
            }

            if (angle1 > angle2)
            {
                return 1;
            }
            return 0;
        }
    }

    /// <summary>
    /// 按照极角顺序比较。
    /// </summary>
    private class PolarOrder : Comparer<Point2D>
    {
        private readonly Point2D _parent;

        public PolarOrder(Point2D parent)
        {
            _parent = parent;
        }
        public override int Compare(Point2D q1, Point2D q2)
        {
            var dx1 = q1.X - _parent.X;
            var dy1 = q1.Y - _parent.Y;
            var dx2 = q2.X - _parent.X;
            var dy2 = q2.Y - _parent.Y;

            if (dy2 >= 0 && dy2 < 0)
            {
                return -1;
            }

            if (dy2 >= 0 && dy1 < 0)
            {
                return 1;
            }
            if (dy1 == 0 && dy2 == 0)
            {
                if (dx1 >= 0 && dx2 < 0)
                {
                    return -1;
                }

                if (dx2 >= 0 && dx1 < 0)
                {
                    return 1;
                }
                return 0;
            }
            return -Ccw(_parent, q1, q2);
        }
    }

    /// <summary>
    /// 按照距离顺序比较。
    /// </summary>
    private class DistanceToOrder : Comparer<Point2D>
    {
        private readonly Point2D _parent;

        public DistanceToOrder(Point2D parent)
        {
            _parent = parent;
        }
        public override int Compare(Point2D p, Point2D q)
        {
            var dist1 = _parent.DistanceSquareTo(p);
            var dist2 = _parent.DistanceSquareTo(q);

            if (dist1 < dist2)
            {
                return -1;
            }

            if (dist1 > dist2)
            {
                return 1;
            }
            return 0;
        }
    }

    /// <summary>
    /// 构造一个以到当前点的极角为关键字的升序比较器。
    /// </summary>
    /// <returns>以到当前点的极角为关键字的升序比较器。</returns>
    public Comparer<Point2D> Polor_Order()
    {
        return new PolarOrder(this);
    }

    /// <summary>
    /// 构造一个以到当前点的 Atan2 为关键字的升序比较器。
    /// </summary>
    /// <returns>以到当前点的 Atan2 为关键字的升序比较器。</returns>
    public Comparer<Point2D> Atan2_Order()
    {
        return new Atan2Order(this);
    }

    /// <summary>
    /// 构造一个以到当前点的距离为关键字的升序比较器。
    /// </summary>
    /// <returns>以到当前点的距离为关键字的升序比较器。</returns>
    public Comparer<Point2D> DistanceTo_Order()
    {
        return new DistanceToOrder(this);
    }

    /// <summary>
    /// 比较 <paramref name="obj"/> 是否与自身相等。
    /// </summary>
    /// <param name="obj">要判别相等的另一个对象,</param>
    /// <returns>相等则返回 <c>true</c>,否则返回 <c>false</c>。</returns>
    public override bool Equals(object obj)
    {
        if (obj == this)
        {
            return true;
        }
        if (obj == null)
        {
            return false;
        }
        if (obj.GetType() != GetType())
        {
            return false;
        }
        var that = (Point2D)obj;
        return Math.Abs(X - that.X) < float.Epsilon * 5 && Math.Abs(Y - that.Y) < float.Epsilon * 5;
    }

    /// <summary>
    /// 获取点的坐标字符串。
    /// </summary>
    /// <returns>形如 "(<see cref="X"/>, <see cref="Y"/>)" 的字符串。</returns>
    public override string ToString()
    {
        return "(" + X + ", " + Y + ")";
    }

    /// <summary>
    /// 获得二维点的哈希值。
    /// </summary>
    /// <returns>哈希值。</returns>
    public override int GetHashCode()
    {
        var hashX = X.GetHashCode();
        var hashY = Y.GetHashCode();
        return 31 * hashX + hashY;
    }
}

Main #

Console.WriteLine("Type the value of N:");
var n = int.Parse(Console.ReadLine());
var pointList = new List<Point2D>();
var random = new Random();

if (n <= 2)
{
    Console.WriteLine("Make sure there are 2 points at least");
    return;
}

// random.NextDouble() 返回一个 0~1 之间的 double 值
for (var i = 0; i < n; i++)
{
    var x = random.NextDouble();
    var y = random.NextDouble();
    pointList.Add(new Point2D(x, y));
}

var min = pointList[0].DistanceTo(pointList[1]);
for (var i = 0; i < n; i++)
{
    for (var j = i + 1; j < n; j++)
    {
        var temp = pointList[i].DistanceTo(pointList[j]);
        Console.WriteLine($"Checking Distance({i}, {j}): {temp}");
        if (temp < min)
        {
            min = temp;
        }
    }
}

Console.WriteLine($"\nThe minimal distance is {min}")

另请参阅 #

Geometry 库