计算优势点的分而治之算法?

Posted

技术标签:

【中文标题】计算优势点的分而治之算法?【英文标题】:A divide-and-conquer algorithm for counting dominating points? 【发布时间】:2013-10-30 21:24:44 【问题描述】:

假设坐标 (x1,y1) 处的一个点支配 如果 x1 ≤ x2 且 y1 ≤ y2 则另一个点 (x2,y2);

我有一组点 (x1,y1) , ....(xn,yn) 我想找到支配对的总数。我可以通过将所有点相互比较来使用蛮力来做到这一点,但这需要时间 O(n2)。相反,我想使用分而治之的方法在 O(n log n) 时间内解决这个问题。

现在,我有以下算法:

绘制一条垂直线,将点集分成两个相等的 Pleft 和 Pright 子集。作为基本情况,如果只剩下两点,我可以直接比较它们。

递归计算 Pleft 和 Pright

中的支配对数

一些征服步骤?

问题是我看不到“征服”步骤应该在这里。我想计算从 Pleft 到 Pright 交叉的主导对有多少,但如果不比较两者中的所有点,我不知道该怎么做部分,这需要时间 O(n2)。

谁能给我一个关于如何完成征服步骤的提示?

所以 y 坐标的两半是:1,3,4,5,5 和 5,8,9,10,12

我画了分割线。

【问题讨论】:

【参考方案1】:

假设您按 y 坐标升序对两半中的点分别进行排序。现在,查看两半中 y 值最低的点。如果左侧最低点的 y 值低于右侧最低点,则该点受右侧所有点的支配。否则,右边的底点不会支配左边的任何东西。

在任何一种情况下,您都可以从两半中的一个中删除一个点,然后对剩余的排序列表重复该过程。这对每个点做了 O(1) 的工作,所以如果总共有 n 个点,那么 O(n) 的工作(在排序之后)来计算两半中支配对的数量。如果您以前见过,这类似于计算数组中的倒数的算法。

考虑到排序点所需的时间 (O(n log n)),这个征服步骤需要 O(n log n) 时间,给出递归

T(n) = 2T(n / 2) + O(n log n)

根据Master Theorem,这解决了 O(n log2 n)。

但是,您可以加快速度。假设在你开始分治步骤之前,你通过它们的 y 坐标对点进行预排序,做一次 O(n log n) 的工作。使用类似于最接近点对问题的技巧,您可以在每个大小为 n 的子问题上在 O(n) 时间内对每一半中的点进行排序(有关详细信息,请参阅the discussion at this bottom of this page)。这会将重复更改为

T(n) = 2T(n / 2) + O(n)

根据需要求解到 O(n log n)。

希望这会有所帮助!

【讨论】:

好,但我对此感到困惑:“右边的所有点”。这是否意味着左边的这个最低 y 被右边的所有点控制,包括它自己左边的点?示例:左 y = 1,3,4,5,5 ;右 y = 5,8,9,10,12 所以 1 1 小于右 y 中的所有点?但是左边 y 的剩余点3,4,5,5 怎么样?这有点不清楚你的意思是哪个“正确” 另外,我是否应该进行递归调用,直到我在每个子集中只得到 2 分?或者只是将整个列表分成两半,然后只比较 y coord-s? 查看上面的图 - 我使用这个例子 @ERJAN- 这样想。假设您通过绘制一条将它们分成“左”和“右”两半的垂直线来分割这些点。递归计算两半中的支配点数。现在,唯一要考虑的控制点对是一个点在左半边,一个点在右半边的点。右边的任何点已经比左边的任何点有更大的 x 坐标,所以剩下的唯一考虑是右边的点比左边的点有更高的 y 值。我建议的算法提供了一种有效的计数方法...... @ERJAN- ... 跨越分割的支配点数。这能说明问题吗?【参考方案2】:

通过这种方式,您有 O(n^2) 仅用于划分子集... 我的方法会有所不同

    按 X ... O(n.log(n)) 对点进行排序 现在检查 Y 但只检查 X 较大的点(如果您按升序对它们进行排序,则使用较大的索引) 所以现在你有 O(n.log(n)+(n.n/2))

您还可以通过单独的 X 和 Y 测试进一步加快速度,然后组合结果,这将导致 O(n + 3.n.log(n))

    为您的点添加索引属性 其中 index = 0xYYYYXXXXh 是无符号整数类型 YYYY 是 Y 排序数组中点的索引 XXXX 是 X 排序数组中点的索引 如果您有超过 2^16 个点,请使用大于 32 位的数据类型。 按 X 升序对点进行排序,并将其索引的 XXXX 部分设置为 O1(n.log(n)) 按 Y 升序对点进行排序,并将其索引的 YYYY 部分设置为 O2(n.log(n)) 按升序索引 O3(n.log(n)) 对点进行排序 如果 (i 但如果您实际上需要为任何点创建所有对 这需要 O4(n.n/2) 所以这种方法不会节省一点时间 如果任何点只需要一对,那么简单的循环就足够了 O4(n-1) 所以在这种情况下 O(n-1+3.n.log(n)) -> ~O(n+3.n.log(n))

希望它有所帮助,...当然,如果您坚持使用这种细分方法,那么我没有更好的解决方案。

PS。为此,您不需要任何额外的递归,只需 3x 排序,任何点只需一个 uint,因此内存需求不是那么大,甚至应该比递归调用一般的细分递归更快

【讨论】:

【参考方案3】:

此算法在 O(N*log(N)) 中运行,其中 N 是点列表的大小,它使用 O(1) 额外空间。

执行以下步骤:

    按 y 坐标(升序)对点列表进行排序,打破平局 x 坐标(升序)。 以相反的顺序遍历排序列表以计算优势点: 如果当前 x 坐标 >= 迄今为止遇到的最大 x 坐标值 然后增加结果并更新最大值。

这是有效的,因为您确定如果所有具有更大 y 坐标的对的 x 坐标都小于当前点,您就找到了一个支配点。排序步骤使其非常高效。

这是 Python 代码:

def my_cmp(p1, p2):
    delta_y = p1[1] - p2[1]
    if delta_y != 0:
        return delta_y
    return p1[0] - p2[0]

def count_dom_points(points):
    points.sort(cmp = my_cmp)
    maxi = float('-inf')
    count = 0
    for x, y in reversed(points):
        if x >= maxi:
        count += 1
        maxi = x

    return count

【讨论】:

以上是关于计算优势点的分而治之算法?的主要内容,如果未能解决你的问题,请参考以下文章

分而治之算法(MergeSort Algo)计算反转次数

从中间向外循环数组的算法?

分而治之三叉树搜索

使用分而治之的范式提高算法效率

算法图解学习系列--第4章--快速排序

算法图解学习系列--第4章--快速排序