【问题标题】:Understanding "median of medians" algorithm了解“中位数”算法
【发布时间】:2012-03-18 08:16:59
【问题描述】:

我想通过以下示例了解“中位数的中位数”算法:

我们有 45 个不同的数字,分为 9 组,每组 5 个元素。

48 43 38 33 28 23 18 13 8

49 44 39 34 29 24 19 14 9 

50 45 40 35 30 25 20 15 10

51 46 41 36 31 26 21 16 53

52 47 42 37 32 27 22 17 54
  1. 第一步是对每个组进行排序(在这种情况下,它们已经排序)
  2. 递归的第二步,找到中位数的“真实”中位数(50 45 40 35 30 25 20 15 10),即该集合将分为 2 组:

    50 25
    
    45 20 
    
    40 15
    
    35 10
    
    30
    

    对这两组进行排序

    30 10
    
    35 15 
    
    40 20
    
    45 25
    
    50
    

中位数是 40 和 15(如果数字是偶数,我们取左中位数) 所以返回值是 15 但是中位数的“真实”中位数(50 45 40 35 30 25 20 15 10)是 30,而且有 5 个元素少于 15,远小于 wikipedia 中提到的 45 个元素的 30%

所以T(n) <= T(n/5) + T(7n/10) + O(n) 失败了。

顺便说一下,在维基百科的例子中,我得到的递归结果是 36。但是,真正的中位数是 47。

所以,我认为在某些情况下,这种递归可能不会返回真正的中位数。我想了解我的错误在哪里。

【问题讨论】:

  • @kaoD:网站社区政策,“承认问题是家庭作业。”见:meta.stackexchange.com/a/10812
  • @kaoD:发布家庭作业问题本质上没有什么问题,但它会影响大多数成员回答问题的方式。所以应该这样说,以及已经取得了哪些进展。答案通常是试图引导,而不是解决。
  • @Orbling 有关系吗?无论这个问题背后的原因是什么,smnvhn(以及其他人)都将能够从一个好的答案中学习。我认为这个问题本身已经表明 smnvhn 已经对此进行了一些思考。因此,如果这确实是一项家庭作业,发布者将通过任何发布的评论或答案了解更多信息。
  • @Orbling 不,这不是家庭作业,我只是在阅读 Thomas H. Cormen、Charles E. Leiserson、Ronald L. Rivest 和 Clifford Stein 所著的“算法简介”这本书来回答这个问题.
  • @smnvhn:因为它看起来像一本书的问题,它也是一本有趣的书,你可以理解我为什么认为这是家庭作业。询问是标准做法,只是为了在这种情况下清楚 - 没有冒犯的意思,谢谢你的澄清。 :-)

标签: algorithm selection median-of-medians


【解决方案1】:

问题在于您所说的找到中位数的真实中位数的步骤。在您的示例中,您有这些中位数:

50 45 40 35 30 25 20 15 10

这个数据集的真正中位数是 30,而不是 15。你不会通过将组分成五个块并取这些中位数的中位数来找到这个中位数,而是通过递归调用这个较小的选择算法团体。您的逻辑中的错误是假设通过将上述序列分成两个块来找到该组的中位数

50 45 40 35 30

25 20 15 10

然后找到每个块的中位数。相反,中位数算法将在完整数据集50 45 40 35 30 25 20 15 10 上递归调用自身。在内部,这会将组分成五个块并对它们进行排序等,但这样做是为了确定分区步骤的分区点,并且在这个分区步骤中,递归调用将找到中位数的真实中位数,在这种情况下将是 30。如果在原始算法中使用 30 作为中值作为分割步骤,则确实得到了非常好的分割。

希望这会有所帮助!

【讨论】:

  • 我无法理解您试图区分 smnvhn 的错误和“内部分成五个块”之间的区别的部分。它们有何不同?在您描述了他的错误之后,您能否继续使用 smnvhn 的示例?我的理解是,在对新数组进行递归之后,数组将再次按照 smnvhn 所说的那样分成五个一组,因此它会在下一次递归中再次传递 [40, 15] ,因此再次返回 15。
  • 此外,在此示例中查找分区将无济于事,因为数组已经排序,因此无论您选择 9 个元素中的哪一个,您的数组都将保持不变。
  • @templatetypedef,请您详细说明一下。看起来递归方法是错误的,因为它与作者在问题中尝试的完全相同。
  • @templatetypedef 我不小心对你的答案投了反对票。 Stack Overflow 不允许我现在通过消息“除非编辑此答案,否则您的投票现在已锁定”来恢复。你能做一些小的编辑,以便我可以投票吗?
【解决方案2】:

这是中位数算法的中位数pseudocode(稍作修改以适合您的示例)。维基百科中的伪代码未能描述 selectIdx 函数调用的内部工作原理。

我已将 cmets 添加到代码中以进行解释。

// L is the array on which median of medians needs to be found.
// k is the expected median position. E.g. first select call might look like:
// select (array, N/2), where 'array' is an array of numbers of length N

select(L,k)
{

    if (L has 5 or fewer elements) {
        sort L
        return the element in the kth position
    }

    partition L into subsets S[i] of five elements each
        (there will be n/5 subsets total).

    for (i = 1 to n/5) do
        x[i] = select(S[i],3)

    M = select({x[i]}, n/10)

    // The code to follow ensures that even if M turns out to be the
    // smallest/largest value in the array, we'll get the kth smallest
    // element in the array

    // Partition array into three groups based on their value as
    // compared to median M

    partition L into L1<M, L2=M, L3>M

    // Compare the expected median position k with length of first array L1
    // Run recursive select over the array L1 if k is less than length
    // of array L1
    if (k <= length(L1))
        return select(L1,k)

    // Check if k falls in L3 array. Recurse accordingly
    else if (k > length(L1)+length(L2))
        return select(L3,k-length(L1)-length(L2))

    // Simply return M since k falls in L2
    else return M

}

以你为例:

将在 45 个元素的整个数组上调用中位数函数的中位数,例如(k = 45/2 = 22):

median = select({48 49 50 51 52 43 44 45 46 47 38 39 40 41 42 33 34 35 36 37 28 29 30 31 32 23 24 25 26 27 18 19 20 21 22 13 14 15 16 17 8 9 10 53 54}, 45/2)
  1. 第一次调用M = select({x[i]}, n/10) 时,数组{x[i]} 将包含以下数字:50 45 40 35 30 20 15 10。 在此调用中,n = 45,因此选择函数调用将是 M = select({50 45 40 35 30 20 15 10}, 4)

  2. 第二次调用M = select({x[i]}, n/10),数组{x[i]}将包含以下数字:40 20。 在此呼叫中,n = 9,因此呼叫将是 M = select({40 20}, 0)。 此选择调用将返回并分配值M = 20

    现在,说到您有疑问的地方,我们现在将数组 L 划分为 M = 20k = 4

    记住数组L 这里是:50 45 40 35 30 20 15 10

    根据L1 &lt; ML2 = ML3 &gt; M的规则,数组将被划分为L1, L2L3。因此:
    L1: 10 15
    L2: 20
    L3: 30 35 40 45 50

    由于k = 4,它大于length(L1) + length(L2) = 3。因此,现在将通过以下递归调用继续搜索:
    return select(L3,k-length(L1)-length(L2))

    翻译为:
    return select({30 35 40 45 50}, 1)

    结果将返回 30。 (由于 L 有 5 个或更少的元素,因此它将返回第 k 个元素,即排序数组中的第一个位置,即 30)。

现在,M = 30 将在对 45 个元素的整个数组的第一个 select 函数调用中接收,并且将数组 LM = 30 分开的相同分区逻辑将应用于最终获得中位数中位数。

呸!我希望我足够详细和清楚地解释中位数算法的中位数。

【讨论】:

  • 我认为这个答案至少值得投票。
  • 我寻找中位数计算的中位数并找到了这个线程。我试图在 java 中重建伪代码,但由于第二次调用 select 中的数组长度而出现异常...有人可以解释 x[i] 和 {x[i]} 的含义吗?它应该有什么尺寸?谢谢!
  • 被否决,因为变量都是一个字母,因此使代码更难理解。
  • @RickMacGillis 我认为单字母变量在这里是一件好事。当然,开头的注释应该解释变量Mx,但除此之外,这类似于任何数学书。在任何数学书籍中,您也永远不会看到 1 + an_unknown_value = 3 而不是 1 + x = 3
  • 不敢相信这个算法是 O(n)!
猜你喜欢
  • 2019-02-26
  • 2012-01-18
  • 2012-09-14
  • 2015-06-22
  • 2019-06-08
  • 2021-08-04
  • 2010-10-02
  • 1970-01-01
  • 2012-06-28
相关资源
最近更新 更多