算法,求1亿个数的中位数

st0078 2009-07-15 11:38:45
内存只能容纳100W个数,现在有1亿数,混序,然后求这一亿个数的中位数,中位数(就是一个有序数组的中间数字,数组长度为偶数就是两个,奇数则只有一个)
我想了半天最后只想出一个超没效率的方法,所以请各位大虾能够指点一下.


...全文
5065 45 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
45 条回复
切换为时间正序
请发表友善的回复…
发表回复
whitefoxx 2012-08-13
  • 打赏
  • 举报
回复
位图,如果内存放不下那么多bit,可以多次用位图
Felix_Ding 2012-05-29
  • 打赏
  • 举报
回复
另外,如果你觉得在N个大数据之外,再存N个大数据太耗费文件系统空间,也可以不要那些对每个哈希桶的新文件,但是这样每次迭代,就要针对长度始终为N的原始数据读取比较了,这时文件空间耗费和文件写入次数均降到了0,而比较次数会上升至N([log2 N]+1)logm N,这里第一个log以2为底,第二个log以M为底,其复杂度为O(N*logN),也还是可以接受的,毕竟文件系统,如果是硬盘之类的,读写会很耗时间的。楼主斟酌。
Felix_Ding 2012-05-29
  • 打赏
  • 举报
回复 1
找中位数并不一定需要排序吧,其实通过统计counting的办法就可以实现。

这里只使用M个桶,每个桶初始化数字为0,这里事先需要知道1亿数据的值域,然后将这个值域均分到M个桶上,每个桶分别代表某段值域上的数据个数,另外,对M个桶,再分别建立M个空文件。

然后读一遍原始数据,每次对每一个对应值域的桶+1,并讲那个数据追加到对应那个桶的文件中,这样就得到了每段值域上的数据个数,并分类到了M个文件中,由此可以判断中位数在哪个桶里。这就完成了一次迭代,那么下一次迭代就对中位数所在桶对应的文件进行操作,依次类推,直到文件里数据少于M,用M个桶过一遍就得到了中位数。

如果有N个数据,N很大,每次迭代使用M个桶,那么迭代次数k就等于log m N,即以M为底数的N的对数。这种办法的文件写入次数、数字比较次数、内存写入(赋值)次数均为:(M^(k+1)-1)/(M-1) * ([logM]+1),这里[]表示向下取整,log以2为底数,算法复杂度为O(N/k*logN),这里log以2为底。

那么取多少个桶最快呢?根据O(N/k*logN)的复杂度,要是该值尽量小,就要使k值尽量大,即:使M值尽量小(因为M^k=N),M最小只能取2,因为1个桶是无法迭代的,只能最少2个桶(到此颇有二分法的意味)。这时:可以使时间复杂度降到logN,相当低了吧~
aboutin 2011-05-16
  • 打赏
  • 举报
回复
[Quote=引用 41 楼 jonsenelizee 的回复:]

last line:
find(min, max); ==> find_median(sum, min, max);
[/Quote]
这个算法不错!!!


JohnsonElizeee 2010-10-28
  • 打赏
  • 举报
回复
last line:
find(min, max); ==> find_median(sum, min, max);
JohnsonElizeee 2010-10-28
  • 打赏
  • 举报
回复

内存只能容纳100W个数,
现在有1亿数,混序,
然后求这一亿个数的中位数,
中位数(就是一个有序数组的中间数字,数组长度为偶数就是两个,奇数则只有一个)

/*****************************************************************************/
/* this program should find out the median(s) in N(logN)/(logB) no matter there
/* is duplicated data or not.
/*
/* with memory limitation: 4MB memory available.
/* I would like to express the idea with code.
/* here is just the pseudocode with out any optimization for easy understanding.
/*
/* maybe, there is no need to check sum == N/2 or N/2+1, but this will get the
/* median(s) as soon as possible with out more recursive invoking.
/*
/* sorry for any logical problem. please let me know if you found any.
/*****************************************************************************/


int N = 100000000;// total data number is N
int B = 1000000; // number of buckets.
int min, max;
scan data from 0 to N-1 get min and max; // O(N);

void find_median(int num=0, int min, int max)
{
if(min == max) {
if(N%2) {
print medians are min and max;
}
else print median is min; // show you the result
return; // exit
}

/* count all the data in O(N)*/
int m = max-min > B ? (max-min) / B : 1;
int cnt[B] = {0}; // count the data read.
while(!EOF) {
int data = read_data()-min;
if(data >= min && data <= max) cnt[data/m]++;
}

int sum = num, median_1, median_2, i = 0;
while(sum < N/2) sum += cnt[i++];
i--; // median is in the range of [i*m, (i+1)*m-1]

/* get median(s) when sum is N/2 */
if(sum == N/2) {
if(N%2) { // N is even and there are two medians.
median_1 = (i+2)*m-1;
median_2 = i*m;
while(!EOF) {
int data = read_data()-min;
if(data/m == i) {
median_2 = (data > median_2 ? data : median_2);
}
if(data/m == i+1) {
median_1 = (data < median_1 ? data : median_1);
}
}
pintf medians are median_1 and median_2;
return;
}
}
else { // N is an odd number and there is one median.
median_1 = (i+2)*m-1;
while(!EOF) {
int data = read_data();
if(data/m == i+1) median_1 = (data < median_1 ? data : median_1);
}
print median is median_1;
return;
}

/* get median(s) when sum is N/2+1 */
if(sum == N/2+1) {
if(N%2) { // N is even and there are two medians.
median_1 = i*m;
median_2 = i*m;
while(!EOF) {
int data = read_data();
if(data/m == i) {
median_1 = max;
median_2 = (data > median_2 ? data : median_2);
}
}
pintf medians are median_1 and median_2;
return;
}
}
else {
median_2 = i*m; // get (N/2+1)th data.
while(!EOF) {
int data = read_data();
if(data/m == i) median_2 = (data > median_2 ? data : median_2);
}
print median is median_2;
return;
}

/* recursively to find out the median(s) */
min = (i+1)*m-1;
max = i*m;
// get min and max for next processing
while(!EOF)
{
int data = read_data()-min;
if(data/m == i)
{
min = (data < min ? data : min);
max = (data > max ? data : max);
}
}
find(min, max);
}
peter1_jiang 2009-08-09
  • 打赏
  • 举报
回复
因为你不知道每个整数出现的次数,位图法在这里并不合适
mm6688 2009-07-20
  • 打赏
  • 举报
回复
弱弱的问一下,不知道老师给你的题目中有没有列出1亿个数来?单是输入1亿个数都要好长好长一段岁月!
st0078 2009-07-20
  • 打赏
  • 举报
回复
太失望了,唉,有谁可以介绍一个搞算法的论坛,
licry01 2009-07-19
  • 打赏
  • 举报
回复
mark
assiwe 2009-07-19
  • 打赏
  • 举报
回复
求中位数就是排序嘛 你们在研究什么呢?
crafet 2009-07-19
  • 打赏
  • 举报
回复
数据这么多,应该是文件操作了吧
tuoba123 2009-07-19
  • 打赏
  • 举报
回复
顶一下!!!
ggggjatihc 2009-07-19
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 donkey301 的回复:]
如果是整数的话,这种可以利用位图的方法来做。
先把这1亿个数加载到内存里,对应数的bit位置1,这就相当于已经把这1亿个数排好序了,找中位数就很容易了。
[/Quote]

中位数又没说不能重复,BIT置1只能说明存在那个数,又不能统计那个数有多少个。
donkey301 2009-07-19
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 zenny_chen 的回复:]
引用 16 楼 donkey301 的回复:
如果是整数的话,这种可以利用位图的方法来做。
先把这1亿个数加载到内存里,对应数的bit位置1,这就相当于已经把这1亿个数排好序了,找中位数就很容易了。


楼主说了,只能放100W个数,如何将1亿个数存入内存啊?

而且,即便用一个比特表示的话也要1亿个比特,这也需要1250万个字节,仍然很难满足内存容量限制要求。

[/Quote]
恩,没考虑清楚,那就先把中位数定位到一个100w长度的范围内,具体就是:
设内存最大值是M,
那空间可以分为[0,M],[M,2M],[2M,3M]....
把1亿个数每个除M求出落在上述各个范围内的个数.
这时也就知道中位数应该在哪个范围了.而且知道应该取第几个bit为1的数.
james_li85 2009-07-18
  • 打赏
  • 举报
回复
o(n)
不停的用一个数去判断比这个数大的有多少
在分情况去判断左边和右边的数
na2650945 2009-07-18
  • 打赏
  • 举报
回复
学习了。
popil1987 2009-07-18
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 donkey301 的回复:]
如果是整数的话,这种可以利用位图的方法来做。
先把这1亿个数加载到内存里,对应数的bit位置1,这就相当于已经把这1亿个数排好序了,找中位数就很容易了。
[/Quote]
这需要看是什么数据,有两点是不可以用位图的。
第一,有重复数据,如果有重复数据,
第二,不知道最大数是多少,位图不是根据数据多少分配大小,而是最大的数。

还有一点,就是在分配bitmap的时候要注意大端小端的问题,详细资料可参考《编程珠玑》
  • 打赏
  • 举报
回复
本来如果不是内存不足的花,改进的快排,找中位数还是很有效率的。
  • 打赏
  • 举报
回复
多路归并+外排可以解决。
但是效率不怎么样。
加载更多回复(25)

65,186

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧