在无序列表的多个子范围中查找中位数

时间:2013-08-12 03:41:21

标签: algorithm math median

E.g。给定N个元素的无序列表,找到子范围0..100,25..200,400..1000,10..500的中位数,... 我没有看到任何比通过每个子范围和运行标准中位数查找算法更好的方法。

一个简单的例子:[5 3 6 2 4] 0..3的中位数为5。 (不是4,因为我们要求原始列表的前三个元素的中位数)

4 个答案:

答案 0 :(得分:1)

INTEGER ELEMENTS:

如果元素的类型是整数,那么最好的方法是让每个数字的存储桶位于任何子范围内,其中每个存储桶用于计算输入元素中找到的相关整数的数量(例如,bucket[100]存储输入序列中有多少100个。基本上你可以通过以下步骤实现它:

  1. 为每个数字创建存储桶位于任何子范围内。
  2. 遍历所有元素,对于每个数字n,如果我们有bucket[n],则bucket[n]++
  3. 根据存储在存储桶中的聚合值计算中位数。
  4. 换句话说,假设您有一个子范围[0, 10],并且您想计算中位数。存储桶方法基本上计算输入中有多少0个,以及输入中有多少1个等等。假设有n个数字位于范围[0, 10]中,则中位数是n/2个最大元素,可以通过查找i来识别bucket[0] + bucket[1] ... + bucket[i]大于或等于n/2bucket[0] + ... + bucket[i - 1]小于n/2

    关于这一点的好处是,即使您的输入元素存储在多个机器(即分布式案例)中,每台机器也可以维护自己的存储桶,只需要聚合值来通过Intranet。

    您还可以使用涉及多次传递的分层桶。在每次传递中,bucket[i]计算输入中元素的数量位于特定范围内(例如,[i * 2^K, (i+1) * 2^K]),然后通过确定介质位于每个后面的哪个存储桶来缩小问题空间的范围。步骤,然后在下一步中将K减少1,然后重复,直到您可以正确识别介质。


    浮点元素

    整个元素可以适合内存:

    如果您的整个元素可以适合内存,首先对N元素进行排序,然后为每个子范围查找中位数是最佳选择。如果子范围的数量小于logNThe linear time heap solution在这种情况下也会很有效。

    整个元素无法放入内存但存储在一台机器中:

    通常,external sort通常需要三次磁盘扫描。因此,如果子范围的数量大于或等于3,则首先对N个元素进行排序,然后通过仅从磁盘加载必要元素来查找每个子范围的中位数是最佳选择。否则,只需对每个子范围执行扫描并在子范围内拾取这些元素就更好了。

    整个元素存储在多台计算机中: 由于找到中位数是一个整体算子,意味着你无法根据输入的几个部分的中位数推导出整个输入的最终中位数,因此很难用少数句子描述其解决方案,但有研究(见this作为一个例子)一直专注于这个问题。

答案 1 :(得分:0)

我认为随着子范围数量的增加,你会很快发现排序然后检索你想要的元素数字会更快。

在实践中,因为您可以调用高度优化的排序例程。

理论上,也许在实践中也是如此,因为既然你正在处理整数,你就不需要支付n log n进行排序 - 参见http://en.wikipedia.org/wiki/Integer_sorting

如果你的数据实际上是浮点而不是NaN,那么实际上允许你对它们使用整数排序 - 来自 - http://en.wikipedia.org/wiki/IEEE_754-1985#Comparing_floating-point_numbers - 二进制表示具有特殊属性,不包括NaNs ,任何两个数字都可以比较符号和幅度整数(虽然现代计算机处理器不再直接适用):如果符号位不同,负数先于正数(除了负零和正零应该是被认为是相等的),否则,相对顺序与字典顺序相同,但两个负数相反;字节序问题适用。

所以你可以检查NaNs和其他有趣的东西,假装浮点数是符号+幅度整数,当负数减去时纠正负数的排序,然后视为正常2s补码有符号整数,排序,然后反转这个过程。

答案 2 :(得分:0)

我的想法:

  • 将列表排序为数组(使用任何适当的排序算法)

  • 对于每个范围,使用二进制搜索找到范围的开始和结束的索引

  • 通过简单地添加指数并除以2来找出中位数(即范围的中位数[x,y]arr[(x+y)/2]

预处理时间:通用排序算法的O(n log n)(如快速排序)或所选排序程序的运行时间

每个查询的时间:O(log n)

动态列表:

以上假设列表是静态的。如果可以在查询之间自由添加或删除元素,则修改后的Binary Search Tree可以正常工作,每个节点都会保留其后代数量的计数。这将允许与动态列表相同的运行时间。

答案 3 :(得分:0)

答案最终将是“依赖”。有多种方法,在您遇到的大多数情况下,其中任何一种方法都可能适用。问题是每个输入对不同的输入都会有不同的表现。对于一类输入可能表现更好,另一类输入对不同类别的输入表现更好。

作为一个例子,当您必须测试的范围数大于log(N)时,对范围的极值进行排序然后执行二进制搜索然后直接计算中值的方法将非常有用。另一方面,如果范围的数量小于log(N),则可以更好地将给定范围的元素移动到数组的开头,并使用线性时间选择算法来查找中值。

所有这些归结为分析以避免过早优化。如果您实施的方法不会成为系统性能的瓶颈,那么相对于简化程序中存在瓶颈的那些部分,找出如何改进它并不是一个有用的练习。