找中位数

92 篇文章 0 订阅

转自:http://hi.baidu.com/wenzheng38/item/c645493bf695f68df4e4adf8


中位数指的是一列数按照从小到大排列,然后取中间的那个(如果数列个数是奇数,取中间的,如果是偶数,取中间两个的平均数)。

给定的是无序的数组,如何快速的找到中位数?
分析:首先给数组进行排序,可以快排或者堆排,然后去中位数。

如果是在内存有限情况下,在海量数据中如何取中位数?
若有很大一组数据,数据的个数是N(每个数占4个字节),内存大小为M个字节,其中M<4*N,使得不能在现有内存情况下通过直接排序找到这N个数的中位数。解决海量数据中取中位数的方法有两种比较简单耗时的是用堆排序,还有一种是改造后基于段的计数:
分析:分区间堆排序:
在现有M大小内存情况下若最多能够造出包含p个数据的堆,则先扫描一次这N个数据找到最小的p个数,耗时O(Nlog(p)),设这p个数中最大的数是a,将堆清空,在第二轮扫描出比a大的中最小的p个数,然后在把a改为记录这p个数中最大的数,依次类推,直到计算到某一轮p个数和之前够造出的数的个数大于N/2,在这p个数中找到所有数中的中位数,耗时的地方是每轮扫描构建堆都要用了O(Nlog(p)),构造的次数为N/(2*p),所以它的时间复杂度是O(N*N*log(p)/(2*p)).



转自:http://hxraid.iteye.com/blog/649831


题目:在一个文件中有 10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。只写出思路即可(内存限制为 2G的意思就是,可以使用2G的空间来运行程序,而不考虑这台机器上的其他软件的占用内存)。

 

 

分析: 既然要找中位数,很简单就是排序的想法。那么基于字节的桶排序是一个可行的方法 (请见《桶排序》):

思想:将整形的每1byte作为一个关键字,也就是说一个整形可以拆成4个keys,而且最高位的keys越大,整数越大。如果高位keys相同,则比较次高位的keys。整个比较过程类似于字符串的字典序。

第一步:把10G整数每2G读入一次内存,然后一次遍历这536,870,912个数据。每个数据用位运算">>"取出最高8位(31-24)。这8bits(0-255)最多表示255个桶,那么可以根据8bit的值来确定丢入第几个桶。最后把每个桶写入一个磁盘文件中,同时在内存中统计每个桶内数据的数量,自然这个数量只需要255个整形空间即可。

代价:(1) 10G数据依次读入内存的IO代价(这个是无法避免的,CPU不能直接在磁盘上运算)。(2)在内存中遍历536,870,912个数据,这是一个O(n)的线性时间复杂度。(3)把255个桶写会到255个磁盘文件空间中,这个代价是额外的,也就是多付出一倍的10G数据转移的时间。 

第二步:根据内存中255个桶内的数量,计算中位数在第几个桶中。很显然,2,684,354,560个数中位数是第1,342,177,280个。假设前127个桶的数据量相加,发现少于1,342,177,280,把第128个桶数据量加上,大于1,342,177,280。说明,中位数必在磁盘的第128个桶中。而且在这个桶的第1,342,177,280-N(0-127)个数位上。N(0-127)表示前127个桶的数据量之和。然后把第128个文件中的整数读入内存。(平均而言,每个文件的大小估计在10G/128=80M左右,当然也不一定,但是超过2G的可能性很小)。

代价:(1)循环计算255个桶中的数据量累加,需要O(M)的代价,其中m<255。(2)读入一个大概80M左右文件大小的IO代价。 

注意,变态的情况下,这个需要读入的第128号文件仍然大于2G,那么整个读入仍然可以按照第一步分批来进行读取。 

第三步:继续以内存中的整数的次高8bit进行桶排序(23-16)。过程和第一步相同,也是255个桶。

第四步:一直下去,直到最低字节(7-0bit)的桶排序结束。我相信这个时候完全可以在内存中使用一次快排就可以了。

 

 

整个过程的时间复杂度在O(n)的线性级别上(没有任何循环嵌套)。但主要时间消耗在第一步的第二次内存-磁盘数据交换上,即10G数据分255个文件写回磁盘上。一般而言,如果第二步过后,内存可以容纳下存在中位数的某一个文件的话,直接快排就可以了。关于快排的效率,可以看看我博客中的数据《基于比较的内部排序总结 》。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 python ,如果要查海量数据的位数,可以使用 heapq 库的函数 nsmallest() 和 nlargest()。 首先,将所有数据读入内存,然后使用 heapq.heapify() 将数据堆化。接下来,调用 nsmallest() 和 nlargest() 来获取最小的 k 个数和最大的 k 个数。最后,根据这 k 个数的大小关系,就可以得出位数。 例如,如果 k=len(data)//2,那么位数就是最小的 k 个数的最大值。 这种方法的时间复杂度是 O(n log n),空间复杂度是 O(n)。 注意,如果要查的数据量太大,无法一次性读入内存,就需要使用其他方法,例如流式计算。 ### 回答2: 对于海量数据位数问题,可以使用堆排序算法来解决。 首先,将海量数据分割成多个小块,每个小块可以使用快速排序算法进行排序,再将排序好的小块使用堆数据结构进行合并。 然后,使用一个最大堆和一个最小堆来存储数据。最大堆保存一半较小的数据,最小堆保存一半较大的数据。具体实现方式如下: 1. 初始化最大堆和最小堆为空堆。 2. 依次读取海量数据,并根据数据的大小来决定插入最大堆还是最小堆。 - 如果最大堆的大小小于最小堆的大小,则将数据插入最大堆,并对最大堆进行调整,保证最大堆的堆顶(即最大值)小于等于最小堆的堆顶(即最小值)。 - 如果最大堆的大小大于最小堆的大小,则将数据插入最小堆,并对最小堆进行调整,保证最小堆的堆顶(即最小值)大于等于最大堆的堆顶(即最大值)。 3. 当读取完全部数据后,根据最大堆和最小堆的大小来决定位数的位置。 - 如果最大堆的大小等于最小堆的大小,则位数为最大堆的堆顶和最小堆的堆顶的平均值。 - 如果最大堆的大小大于最小堆的大小,则位数为最大堆的堆顶。 - 如果最大堆的大小小于最小堆的大小,则位数为最小堆的堆顶。 通过这种方式,不需要将全部数据加载到内存,可以在读取数据的同时进行排序和求解位数,从而适用于海量数据的情况。 ### 回答3: 在Python,处理海量数据位数的一种常见方法是使用堆。 首先,我们需要了解位数的概念。对于一个有序的数据集,位数是指将数据划分为两个等长子集,左子集的所有元素都小于等于右子集的所有元素。如果数据集有奇数个元素,则位数间的那个元素;如果有偶数个元素,则位数间两个元素的平均值。 对于海量数据,我们无法直接将其全部加载到内存进行排序,因此需要使用堆来解决这个问题。堆是一种特殊的树形数据结构,具有以下特点:每个节点的值都大于(或小于)其子节点的值。 我们可以使用两个堆来实现,一个大根堆和一个小根堆。首先,将数据集的前一半数据插入到大根堆,将剩余的一半数据插入到小根堆。这样可以确保大根堆的所有元素都小于小根堆的元素。然后,我们可以根据数据集的大小,采取不同的策略来计算位数。 如果数据集的大小是奇数,位数就是小根堆的堆顶元素。如果数据集的大小是偶数,位数就是大根堆的堆顶元素和小根堆的堆顶元素的平均值。 在实际实现,我们可以使用Python的heapq模块来操作堆。具体的步骤如下: 1. 利用heapq模块的heapify函数,将数据集前一半的元素插入大根堆,将剩余的一半元素插入小根堆。 2. 如果数据集的大小是奇数,直接返回小根堆的堆顶元素。 3. 如果数据集的大小是偶数,返回大根堆的堆顶元素和小根堆的堆顶元素的平均值。 对于海量数据来说,可以将数据分块读取,每次读取一部分数据,然后进行堆的操作。通过这种方式,可以有效地处理海量数据,位数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值