确定Rectangle是否比另一个Rectangle更接近Circle

时间:2014-07-30 17:43:53

标签: algorithm computational-geometry geometry rectangles

假设我们有两个矩形A,B和一个圆R.我们想确定是否对于任何三个点r,a,b这样“r中的r \ n,A中的a \,B中的b \”, “dist(r,a)< dist(r,b)”将永远保持? dist()返回两点之间的欧几里德距离。换句话说,我们想测试A是否比R更接近于R。

请注意,矩形和圆形在高维空间中可能是超矩形和超球形,矩形是轴平行的。

是否存在在多项式时间内运行的测试算法(关于维D的数量)? O(D)可能吗?

PS:测试应该是完整的,即没有误报或真阴性。

3 个答案:

答案 0 :(得分:0)

首先,您可以考虑圈子的中心。如果中心更接近一个矩形,则圆圈更近。接下来,将您的中心点与定义矩形的平面进行比较,以将其分割成几个可能的体积之一(基本上,矩形的多少平面位于正确的一侧)。对于剩余的平面(它不位于正确的一侧),找到从点到平面的每条轴的距离,并将其视为距离向量。它的标准是从圆心到矩形的欧氏距离。这个解决方案应该是O(d),并且非常好用,因为你的矩形是轴对齐的。

答案 1 :(得分:0)

首先,由于我们在N维空间中工作,让我们称这些对象为NBoxNBall。这些定义了N维体积。

假设框的轴与欧几里德空间的规范单位向量对齐,则NBox可以表示为两个N维双数组MinMax沿坐标轴,或作为坐标轴上Range<double>投影的单个N维列表。 NBall可以表示为单个N维双数组Center和双Radius。一个NPoint,一个N维点,如此(在c#中):

public interface INDimensional
{
    int Dimension { get; }
}

public class NPoint : INDimensional
{
    readonly double[] point;

    public NPoint(double[] point)
    {
        if (point == null)
            throw new ArgumentNullException();
        if (point.Length < 1)
            throw new ArgumentException();
        this.point = point.ToArray(); // copy
    }

    public int Dimension { get { return point.Length; } }

    public double this[int i] { get { return point[i]; } }

    public IList<double> Coordinates { get { return Array.AsReadOnly(point); } }
}

public class NBall : INDimensional
{
    readonly NPoint center;
    readonly double radius;

    public NBall(double[] center, double radius)
    {
        if (Math.Abs(radius) < Accuracy.AbsoluteDoubleTolerance)
            this.radius = 0;
        else if (radius < 0)
            throw new ArgumentException();
        else
            this.radius = radius;
        this.center = new NPoint(center);
        this.radius = radius;
    }

    public int Dimension { get { return center.Dimension; } }

    public double this[int i] { get { return center[i]; } }

    public NPoint Center { get { return center; } }

    public double Radius { get { return radius; } }
}

public class NBox : INDimensional
{
    readonly double[] min;
    readonly double[] max;

    static void ComputeExtremes(double[] point1, double[] point2, out double[] min, out double[] max)
    {
        if (point1 == null || point2 == null)
            throw new NullReferenceException();
        if (point1.Length != point2.Length)
            throw new ArgumentException();
        min = new double[point1.Length];
        max = new double[point1.Length];
        for (int i = 0; i < point1.Length; i++)
        {
            // Handle double-precision rounding issues where point1[i] and point2[i] so nearly equal that they somehow got inverted.
            min[i] = Math.Min(point1[i], point2[i]);
            max[i] = Math.Max(point1[i], point2[i]);
        }
    }

    public NBox(double[] min, double[] max)
    {
        ComputeExtremes(min, max, out this.min, out this.max);
    }

    public int Dimension { get { return min.Length; } }

    public Range<double> this[int i] { get { return new Range<double>(min[i], max[i]); } }

    public IList<double> Min { get { return Array.AsReadOnly(min); } }

    public IList<double> Max { get { return Array.AsReadOnly(max); } }
}

首先,给定一个NPoint,NBox上最近的点是什么?它可以通过坐标协调完成。要获得最近点的第i个坐标值,请查看它是否落在框的第i个范围内。如果在内部,坐标值是该点的坐标值。如果更小,则坐标值是范围最小值。如果更大,则为最大范围。

public static class NDimensionalExtensions 
{
    public static bool FindClosest(this Range<double> range, double other, out double closestParameter)
    {
        if (other <= range.Min)
        {
            closestParameter = range.Min;
            return false;
        }
        if (other >= range.Max)
        {
            closestParameter = range.Max;
            return false;
        }
        closestParameter = other;
        return true;
    }
}

public class NBox 
{
    public bool FindClosest(NPoint other, out NPoint closestPoint)
    {
        if (other.Dimension != Dimension)
            throw new ArgumentException();
        double[] closest = new double[other.Dimension];
        bool allInside = true;
        for (int i = 0; i < Dimension; i++)
        {
            allInside = allInside && this[i].FindClosest(other[i], out closest[i]);
        }
        closestPoint = new NPoint(closest);
        return allInside;
    }
}

其次,给定一个NBox,与另一个NBox最近的点是什么?嗯,这不是唯一定义的,因为如果框沿着轴在某个范围内重叠,则可以选择范围重叠内的任何坐标值。但是,由于我们只对最终计算距离感兴趣,这没关系,我们可以选择重叠的中心:

public static class NDimensionalExtensions 
{
    public static bool FindClosest(this Range<double> range, Range<double> other, out double closestParameter)
    {
        if (other.Max <= range.Min)
        {
            closestParameter =range.Min;
            return false;
        }
        else if (other.Min >= range.Max)
        {
            closestParameter = range.Max;
            return false;
        }
        // return in the middle of the overlap
        closestParameter = 0.5 * Math.Max(range.Min, other.Min) + 0.5 * Math.Min(range.Max, other.Max);
        return true;
    }
}

public class NBox 
{
    public bool FindClosest(NBox other, out NPoint closestPoint)
    {
        if (other.Dimension != Dimension)
            throw new ArgumentException();
        double[] closest = new double[other.Dimension];
        bool allInside = true;
        for (int i = 0; i < Dimension; i++)
        {
            allInside = allInside && this[i].FindClosest(other[i], out closest[i]);
        }
        closestPoint = new NPoint(closest);
        return allInside;
    }
}

现在我们知道如何计算盒子和点之间的最近点,很容易获得盒子,点和球之间的距离。两个点和/或框之间的距离是最近点之间的距离,如果它们相交则为零。 NBall与框或点之间的距离是最近点之间的距离,小于半径,或者如果差异为负(交叉点)则为零。

public interface INDimensional
{
    double GetDistance(NBox other);
}

public class NPoint : INDimensional
{
    public double GetDistance(NPoint other)
    {
        if (Dimension != other.Dimension)
            throw new ArgumentException();
        double distSq = 0;
        for (int i = 0; i < Dimension; i++)
        {
            var dot = (this[i] - other[i]);
            distSq += dot * dot;
        }
        return Math.Sqrt(distSq);
    }

    public double GetDistance(NBox other)
    {
        NPoint closestPoint;
        if (other.FindClosest(this, out closestPoint))
            return 0;
        return GetDistance(closestPoint);
    }
}

public class NBall : INDimensional
{
    public double GetDistance(NBox other)
    {
        NPoint closestPoint;
        var allInside = other.FindClosest(Center, out closestPoint);
        if (allInside)
            return 0;
        double distance = closestPoint.GetDistance(Center) - Radius;
        if (distance < 0)
            distance = 0;
        return distance;
    }
}

public class NBox : INDimensional
{
    public bool FindClosest(NBox other, out NPoint closestPoint)
    {
        if (other.Dimension != Dimension)
            throw new ArgumentException();
        double[] closest = new double[other.Dimension];
        bool allInside = true;
        for (int i = 0; i < Dimension; i++)
        {
            allInside = allInside && this[i].FindClosest(other[i], out closest[i]);
        }
        closestPoint = new NPoint(closest);
        return allInside;
    }

    public double GetDistance(NBox other)
    {
        NPoint myClosest, boxClosest;
        if (FindClosest(other, out myClosest))
            return 0;
        if (other.FindClosest(this, out boxClosest))
            return 0;
        return myClosest.GetDistance(boxClosest);
    }
}

根据GetDistance(NBox other)上的INDimensional方法,您现在可以找出INDimensional个对象集合中哪个最接近给定NBox

这可以通过使用距离平方BTW进行优化。

所以,所有这些看起来都是O(N)。

但是,如果框沿着轴而不是规范坐标轴轴对齐怎么办?在这种情况下,有必要将所有点转换为轴空间,或者采用带有轴的点积来产生正确的间隔。并且这个将是O(N平方),因为将N维点转换为另一个坐标系需要N平方乘法。

我没有定义Range<double>课程,你可以自己做。我还没有真正测试过上面的代码,因为你对算法感兴趣而不是实现。最后,这是Accuracy类的存根:

public static class Accuracy
{
    public static double AbsoluteDoubleTolerance
    {
        get
        {
            return 100 * double.Epsilon;
        }
    }
}

答案 2 :(得分:0)

我可以想到一个涉及矩形(或超立方体)的所有面和顶点的算法。因此,不幸的是,维度的数量呈指数关系。但至少它应该是正确的。

引理1

  

给定一个点P.给定矩形内的最近点是P本身或重新旋转边界/表面上的另一个点。

引理2

  

给定一个点P.给定矩形内的最远点是顶点(角)之一。

因此,如果我们想检查矩形A是否比矩形B更接近圆R,那么我们搜索一个计数器示例。如果我们找不到,A更接近。

假设存在这样的反例(r,a,b)。然后我们可以将点移动得更远,甚至更近,并获得一个更“强大”的反例。 我们只检查这些“最强”的潜在反例。 因此,如果R中有一个点r,其中A中的最远点不比B中的最近点更接近,则A不会更接近R.但是如果我们找不到这样的点r,则A更接近于R,根据jojer的条件。

如引理1所述,我们只检查B的每个表面(2d的边缘)上的b点。 根据引理2,我们只检查A顶点的a点。

如果我们现在检查所有这些点对所有这些b点,将空格分成两半,并显示圆圈完全处于更靠近a的一半,那么就没有反例。

空间划分是2个超平面和超抛物线的复合(如果我们检查A的顶点与B的表面)参见示例:http://imgur.com/hf2cJ0c

因此,我们构造所有这些分割平面/抛物面并测试圆心的方向距离是否大于其半径。 如果是这样,则可能没有三重(r,a,b)作为反例。

整个过程应该只是对一些二次形式的评估,并且由顶点 - 顶点 - 对和顶点 - 表面 - 对的数量决定,它们在维度的数量上呈指数。