【问题标题】:Why is the median-of-medians algorithm described as using O(1) auxiliary space?为什么中位数算法被描述为使用 O(1) 辅助空间?
【发布时间】:2016-04-06 08:38:33
【问题描述】:

Wikipedia lists the median-of-medians algorithm as requiring O(1) auxiliary space.

但是,在算法的中间,我们对大小为n/5 的子数组进行递归调用以找到中位数的中位数。当这个递归调用返回时,我们使用返回的中位数作为基准对数组进行分区。

这个算法不是将O(lg n) 激活记录作为递归的一部分推送到运行时堆栈吗?据我所知,这些寻找中位数的连续中位数的递归调用不能进行尾调用优化,因为我们在递归调用返回后做了额外的工作。因此,该算法似乎需要O(lg n) 辅助空间(就像快速排序一样,由于运行时堆栈使用的空间,维基百科将其列为需要O(lg n) 辅助空间)。

是我遗漏了什么,还是维基百科的文章有误?

(注意:我指的递归调用是维基百科页面上的return select(list, left, left + ceil((right - left) / 5) - 1, left + (right - left)/10)。)

【问题讨论】:

  • @Nuclearman 这是一个公平的观点,但是pivot 函数会调用select,,所以我们不能忽略select 所需的空间。 Wikipedia 文章将这两个函数描述为相互递归。如果我们忽略对select 的调用,我们最终不会得到中位数的中位数。相反,我们最终得到n/5 中位数为 5。
  • @Nuclearman Quickselect 不需要堆栈,因为它可以进行尾调用优化。它可以进行尾调用优化,因为我们在递归中基本上只遍历一个从根到叶的路径。我们不需要记住我们的历史。 Median-of-medians 不能以相同的方式优化,因为它在每个级别都有多个递归调用。换句话说,我们在算法执行期间遍历了多个从根到叶的路径。我们需要记住我们的历史,这样当我们到达一片叶子时,我们就知道该往哪里回去。我们不能在恒定空间中进行这样的遍历。
  • @John 我目前也在处理这个问题,并且同意你的所有观点。我 asked the author 维基百科的 O(1) 声明,希望我们能得到答案。
  • @StefanPochmann 谢谢斯特凡。我首先根据 Leetcode 的 Wiggle Sort II 开始对此进行调查,您似乎也在查看 :) 感谢您与原作者联系。不知道这是一个选择!
  • @John 是的,leetcode 也是如此。我几乎可以肯定这不是巧合。但巧合的是,您当前的 3137 点是 1337 的字谜 :-)

标签: algorithm recursion space-complexity median-of-medians


【解决方案1】:

虽然我不能排除 O(1) 是可能的,但维基百科的信息似乎是一个错误。

  • 那里显示的实现需要 O(log n) 而不仅仅是 O(1)。
  • 如何用 O(1) 实现它绝对不明显,也没有任何解释/参考。
  • 我问过originally added that informationhe replied 的作者,他不记得了,这可能是一个错误。
  • 一个paper from 2005 致力于解决 O(n) 时间和 O(1) 额外空间的选择问题,他说 BFPRT(又名 Median of Medians)使用 Θ(log n) 额外空间。另一方面,论文的主要结果是 O(n) 时间和 O(1) 额外空间是可能的,并且作为证明提出的两种算法之一是 BFPRT 的一些“仿真”。所以从这个意义上说是可能的,但我认为仿真让它成为一种不同的算法,并且 O(1) 不应该归因于“常规”BFPRT。至少不是没有解释。
    (感谢 Yu-HanLyu 在 cmets 中展示该论文及更多内容)

【讨论】:

  • 最后一点假设输入是只读的。如果输入是可修改的,则可以就地进行中位数查找。
  • @Yu-HanLyu 你确定吗?该论文的介绍说有两种类型的恒定工作空间算法,一种允许修改输入。它列出了就地堆排序和快速排序作为示例。也就是说,我想如果我们排除一些输入数字,我们确实可以使中位数算法只占用 O(1) 额外空间,用最后的数字替换它们,然后滥用该端来存储递归信息。这也是您的想法,还是有一种方法可以至少重新排列输入而不会丢失数据?
  • 我不知道中位数算法是否可以就地完成。但是,我相信有一些就地选择算法。见link.springer.com/chapter/10.1007%2FBFb0015429cai.sk/ojs/index.php/cai/article/viewArticle/345link.springer.com/chapter/10.1007%2F3-540-19487-8_2我猜最后一个接近你的想法,本文还提到中位数算法使用 O(log n) 额外空间。
  • 但是 in-place 版本可能比原始版本更慢,例如快速排序的无堆栈版本比原始快速排序慢。
  • @Yu-HanLyu 抱歉,想先看看这些论文。但我现在删除了最后一点,明天将添加一个替代品(现在太累了,无法制定它)。
【解决方案2】:

是 O(1)。

假设我们从一个长度为 n 的数组开始,我们打算找到排序列表的第 k 个元素。

在第一次调用中位数之后会吐出一个较小的数组,现在我们需要对这个较小数组的第i个元素求值。请注意,这个较小数组的第 i 个元素是结果,因此我不需要将任何信息传递回之前的调用。

在快速排序中,我需要将已排序的小数组放回正确的位置,因此会发生递归。中位数的中位数,在循环(尾递归)之后,我将得到答案。

递归深度 = O(1)

【讨论】:

  • 找到对数组进行分区的支点需要 O(lg n) 内存。在 1) 找到枢轴和 2) 围绕该枢轴对数组进行分区之前,您无法拆分出较小的数组。你是对的,一旦你对数组进行了分区,你就不需要将任何信息传递回原来的调用者,但是你忽略了一个事实,即找到枢轴需要 O(lg n) 内存。
猜你喜欢
  • 2017-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 2020-11-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多