放置n个点,同时最大化最小距离

时间:2017-05-10 12:05:01

标签: java algorithm

给定距离d(从0d)和2点se之间,其中无法放置任何积分(放置se上的点数没问题,不允许在它们之间放置点数。)

放置n点,使每个点之间的距离尽可能大(尽可能均匀地分布)。

输出2点之间的最小距离。

图形表示,将n点放在黑线上(它是一条1维线),以便每两个点之间的最小距离尽可能大(绝对误差最高为{允许{1}}。

Graphic representation

示例:

  • d = 7,n = 2,s = 6,e = 7,输出为:7.0000000000
  • d = 5,n = 3,s = 5,e = 5,输出为:2.5000000006
  • d = 3,n = 3,s = 0,e = 1,输出为:1.5000000007
  • d = 9,n = 10,s = 5,e = 6,输出为:1.0000000001
  • d = 6,n = 2,s = 1,e = 6,输出为:6.0000000000
  • d = 5,n = 3,s = 4,e = 5,输出为:2.5000000006

我的方法:

我尝试单独查看间隔,在第一和第二个间隔(10^(-4)lengthOfInterval和{{分配点(理想分布n / 0) 1}}到s)并检查点数总和为e的所有分布,我将存储一个(分布,最大最小距离)对并选择具有最大最小距离的对。我不知道如何使用d容差(这部分甚至在代码中看起来怎么样?)并且不确定我的方法是否正确。欢迎提出任何建议。

我坚持这个问题:/

4 个答案:

答案 0 :(得分:1)

您可以使用二进制搜索覆盖点之间可能的间隙大小(从0d),以收敛到最大的最小间隙大小。

要确定任何给定间隙大小的可行性,您基本上会尝试从左侧和右侧放置点,看看中间的间隙是否足够大:

  • 确定s左侧可以放置的点数(s/gapSize + 1)。
  • 确定需要在e的右侧放置多少个点 (n - points on left)。
  • 确定每一方向内的距离。
  • 检查右边的点是否适合间距[e, d]以及每边之间是否至少有gap size差异。

此代码:(注意我使用的是间隙数而不是点数,这比点数少1,因为它会导致更简单的代码)

double high = d, low = 0, epsilon = 0.000001;
while (low + epsilon < high)
{
    double mid = (low + high)/2;
    int gapsOnLeft = (int)(s/mid); // gaps = points - 1
    if (gapsOnLeft + 1 > n)
        gapsOnLeft = n - 1;
    int gapsOnRight = n - gapsOnLeft - 2; // will be -1 when there's no point on the right
    double leftOffset = mid*gapsOnLeft;
    // can be > d with no point on the right, which makes the below check work correctly
    double rightOffset = d - mid*gapsOnRight;
    if (leftOffset + mid <= rightOffset && rightOffset >= e)
        low = mid;
    else
        high = mid;
}
System.out.println(low);

Live demo

时间复杂度为O(log d)

你的方法的问题在于很难弄清楚点之间的间隙有多大,所以你不知道在(s, e)的任何一侧应该有多少点。最终得到一个最佳解决方案,并在se非常接近并且它们相距很远时正确处理这两种情况。

答案 1 :(得分:0)

二进制搜索

如果给出任意对l的最小间隔距离b / w,则很容易找到可以放置的点数。

如果l = d,那么最多只能放置2个点。
.. ...... ....

所以只需在l上进​​行二元搜索。

粗略的实现是这样的。

low,high=0.00001,d
while(high-low>eps):
    m = (low+high)/2
    if((no. of points placed s.t any pair is at most m units away) >=n):
        low=mid
    else:
        high=mid

答案 2 :(得分:0)

TL; DR:您的方法并不总是有效(并且您没有尽可能快地执行此操作)查看有效的方法的第3个要点(并使用给定的方法) 10 ^( - 4)。)

  • 如果[s, e]很小并且放置得很好,那么最佳值只是在整个细分上均匀分布,最佳值现在为d/(n-1)。但是,您必须检查se之间没有任何元素。

  • 如果se足够#34;您的方法有效。&#34;

您可以通过查看两个细分受众群之间的最佳分割O(1)来快速完成,而不是您的建议:如果您放置n11<=n1<=n-1)元素在左侧,您希望最大化min(s/(n1-1), (d-e)/(n-n1-1))(其中一个数量可能为+infinity,但另一个数量不是s/(x-1) = (d-e)/(n-x-1)x获得该函数的最大值,只计算n1的对应值,其最低值或最高值是best = min(s/(n1-1), (d-e)/(n-n1-1))的最佳值。获得的距离为n1然后您将0点放在左侧,从best开始,以距离n-n1分隔,并在右侧开始d,开始在best左转,由sp分隔。

如果左边的最后一点和右边的第一个点之间的距离小于最佳点,那么你有问题,这种方法不起作用。

  • 复杂的情况是前两种方法失败时:洞很小而且放置不好。那么可能有很多方法可以解决这个问题。一种是使用二分搜索来找到两个连续点之间的最佳空间。给定候选空间sp,尝试在0开始的行上分配点,间隔(last on the left + sp),尽可能多地保留在s以下。在高于e及sp以上时,在右侧执行相同操作。如果您已成功放置至少n个点,则sp太小。否则,它太大了。

因此,您可以使用二分搜索来找到最佳mid,如下所示:从sp开始,可能在[max(s,d-e)/(n-1),d /(n-1)]中。在每个步骤中,获取可能的细分[x, y]的中间mid。检查实际最佳值是高于还是低于[mid, y]。根据您的情况,请在[x, mid]y-x < 10^(-4)中查找最佳值。停止iff persist.demo.hdmirotation=portrait ro.sf.hwrotation=270 (I have tried 90 too) windowsmgr.support_rotation_270=true

实际上也可以通过这种方法找到前两种情况,因此您不需要实现它们,除非您希望在可能的情况下获得精确的最佳值(即在前两种情况下)。

答案 3 :(得分:0)

这非常棘手,除了简单的情况(没有点落在差距中):

double dMin = d / (n - 1.0);
if (Math.ceil(e / dMin - 1) * dMin <= s)
    return dMin;

让我们继续处理边缘情况,在一侧放置一个点,在另一侧放置其余点:

dMin = Math.min((d - e) / (n - 2.0), e); // one point at 0
double dm = Math.min(s / (n - 2.0), d - s); // one point at d
if (dm > dMin) // 2nd configuration was better
    dMin = dm;

最后两边都有两点或多点:

// left : right = (x - 1) : (n - x - 1)
// left * n - left * x - left = right * x - right
// x * (left + right) = left * n - left + right
// x = (left * n - left + right) / (left + right) = (left * n) / (left + right) - 1 
int x = s * n / (d - e + s) - 1;
if (x < 2)
    x = 2;
for (int y = x; y <= x + 2 && y < n - 1; y++) {
    double dLeft = s / (y - 1.0);
    double dRight = (d - e) / (n - y - 1.0);
    dm = Math.min(dLeft, dRight);
    if (dm > e - s) { // dm bigger than gap 
        if (dLeft > dRight)
            dLeft = e / ((double) y);
        else
            dRight = (d - s) / ((double) n - y);
        dm = Math.min(dLeft, dRight);
    }
    if (dm > dMin)
        dMin = dm;
}

这将是 O(1)空间和时间,但如果检查所有情况,我不是100%正面。如果有效,请告诉我。 针对所有测试用例进行测试。以上适用于n >= 2,如果n等于2,它将被第一次检查捕获。