python两种方法实现从1000万个随机数中找出top n元素(附c语言版)
来源:互联网 发布:node excelexport 编辑:程序博客网 时间:2024/06/04 18:14
转载请注明地址:http://blog.csdn.net/echoutopia/article/details/51731269
很早之前看到一道面试题:
有一个长度为1000w个数组,每个元素互不重复,找出其中top n元素。
我感觉重复或者不重复都差不多,所以没管不重复这个条件,但是如果使用位图排序,那么会把重复的剔除掉。
使用位图另外一个限制是位图数组的大小由最大的数决定,但是我个人有个思路就是确定一个数MAX,然后创建两个或多个位图数组,大于MAX的数就减去MAX放在其他位图数组中。
我把1000w个结果放在了文件中,方便重复利用,生成代码:
import randomwith open("random_number.txt","w") as f: for i in range(1,10000000): f.write("%s\n" % random.randint(1,10000000))
看大家讨论大概有两种实现方式:
第一种,设置一个n个元素的数组,这里把要排序的数组称为大数组,n个元素的数组称为小数组,
在遍历大数组时,淘汰掉小数组里最小的数,最后将小数组排序便是排序结果。代码:
def stack_sort(): n_list = [] with open("random_number.txt","r") as f: for i in f: number = int(i.strip()) if len(n_list) == 100: min_element = min(n_list) if number > min_element: n_list[n_list.index(min_element)] = number else: n_list.append(number) print n_list执行代码:
time python sort_10_million.py结果:
[10000000, 9999998, 9999997, 9999995, 9999994, 9999992, 9999991, 9999989, 9999988, 9999987, 9999985, 9999984, 9999983, 9999982, 9999981, 9999978, 9999975, 9999974, 9999973, 9999970, 9999969, 9999967, 9999965, 9999964, 9999963, 9999961, 9999959, 9999958, 9999957, 9999956, 9999955, 9999952, 9999951, 9999950, 9999949, 9999948, 9999947, 9999944, 9999943, 9999941, 9999939, 9999937, 9999936, 9999935, 9999934, 9999932, 9999929, 9999927, 9999925, 9999924, 9999923, 9999922, 9999920, 9999917, 9999915, 9999914, 9999913, 9999912, 9999911, 9999909, 9999908, 9999907, 9999906, 9999905, 9999904, 9999903, 9999902, 9999901, 9999900, 9999899, 9999897, 9999895, 9999894, 9999893, 9999892, 9999890, 9999889, 9999887, 9999884, 9999883, 9999879, 9999878, 9999875, 9999873, 9999871, 9999870, 9999869, 9999868, 9999867, 9999866, 9999865, 9999864, 9999862, 9999861, 9999858, 9999856, 9999855, 9999854, 9999853, 9999852, 9999851, 9999850, 9999847, 9999846, 9999845, 9999843, 9999841]
real0m23.115suser0m22.504ssys0m0.564s凭感觉时间主要花在了字符串处理(转换为int)和寻找小数组最小元素和最小元素的数组下标去了
第二种,是使用位图来实现排序。
一个4字节的int有32个bit,每一bit都可以表示一个数字。具体实现:
使用一个数组,每个元素都是4字节(python的int类型长度可变,我们只要使用4个字节就行了),长度为要排序的数字个数(我们这1000w个)整除32再加1,这样就能把1000w个数字都存在这个数组里面了。怎么存呢?使用整除和余数。将大数组里面的每个数字整除32,得到的数字决定放到小数组哪个元素里面去,等于小数组的下标,再用这个数字除以32得到的余数来决定具体放到那个元素的哪个bit。例如:
big_list[0] = 72,那么index= 72 // 32 = 2。72 % 32 = 8,所以 small_list[2] = small_list[2] | (1 << 8 & 1)。
将大数组遍历一遍,这样就能为每个数字分配一个bit了,而且大的数字在小数组中下标比较大,而在同一元素内,位数越高的bit所代表的数字越大。这就意味着遍历一次就将所有的数字排序了,想取top多少都行,而且时间复杂度为O(N)。只是内存占用稍大,1000w个数字,小数组就得有312501个元素,每个元素4个字节,大概占1.2M内存,感觉还行(python不止这么多,python的列表实现还有很多额外的开销。)
代码:
def map_sort(n): map_list = [] list_len = 10000000//32+1 for i in range(0,list_len): map_list.append(0) # print len(map_list) with open("random_number.txt","r") as f: for i in f: number = int(i.strip()) integer = number // 32 mod = number % 32 map_list[integer] = map_list[integer] | (1 << mod) sorted_list = [] for i in range(list_len-1,-1,-1): for j in range(31,-1,-1): if (map_list[i] >> j) & 1 == 1 and len(sorted_list) < 100: origin_number = i*32 + j sorted_list.append(origin_number) print sorted_list执行代码:
time python sort_10_million.py执行结果:
[10000000, 9999998, 9999997, 9999995, 9999994, 9999992, 9999991, 9999989, 9999988, 9999987, 9999985, 9999984, 9999983, 9999982, 9999981, 9999978, 9999975, 9999974, 9999973, 9999970, 9999969, 9999967, 9999965, 9999964, 9999963, 9999961, 9999959, 9999958, 9999957, 9999956, 9999955, 9999952, 9999951, 9999950, 9999949, 9999948, 9999947, 9999944, 9999943, 9999941, 9999939, 9999937, 9999936, 9999935, 9999934, 9999932, 9999929, 9999927, 9999925, 9999924, 9999923, 9999922, 9999920, 9999917, 9999915, 9999914, 9999913, 9999912, 9999911, 9999909, 9999908, 9999907, 9999906, 9999905, 9999904, 9999903, 9999902, 9999901, 9999900, 9999899, 9999897, 9999895, 9999894, 9999893, 9999892, 9999890, 9999889, 9999887, 9999884, 9999883, 9999879, 9999878, 9999875, 9999873, 9999871, 9999870, 9999869, 9999868, 9999867, 9999866, 9999865, 9999864, 9999862, 9999861, 9999858, 9999856, 9999855, 9999854, 9999853, 9999852, 9999851, 9999850, 9999847, 9999846, 9999845, 9999843, 9999841]real0m10.210suser0m9.184ssys0m1.012s
速度提升很多(具体时间看电脑配置,我公司的电脑就只要6秒),而且随着N增大,第二种方法耗时不会怎么增加,但是第一种与第二种方法耗时差距会越来越大,因为第一种方法寻找最小数开销挺大的。
比如top n n取1000,第二种方法
real0m9.714suser0m9.572ssys0m0.124s第一种方法:
real2m51.504suser2m51.116ssys0m0.192s两者差了将近20倍,恐怖
n等于1000这里我把print去掉了,因为print1000个会耗时不少,影响结果,所以这里n=1000时比n=100时耗时少
使用第二种排序,如果数据不重复,那么位图数组每个元素的没个bit都代表一个数字,如果数据有重复,那么有些位就空着,会有一点浪费。
本来想试试c语言,把1000w个数全放在数组里看看有多快,但是我的c语言很菜,嫌麻烦,所以还是从文件读取的,而且没用缓存,直接一行一行读取的,就更慢了,
,执行结果:
9999882999988099998799999878999987799998759999874999987399998729999871999987099998699999867999986699998659999864999986399998609999859real<span style="white-space:pre"></span>0m0.676suser<span style="white-space:pre"></span>0m0.580ssys<span style="white-space:pre"></span>0m0.084s
上面很多数字省略。
代码:
#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAX_SIZE 312501void main(){ unsigned long small_array[MAX_SIZE] = {0}; int sorted_array[100]; char number[10]; FILE *fp; int integer,mod,tmp,i,j; int k=0; if(NULL == (fp = fopen("random_number.txt","r"))){ printf("error\n"); exit(1); } while (!feof(fp)){ fgets(number,10,fp); tmp = atoi(number); integer = tmp/32; mod = tmp%32; small_array[integer] = small_array[integer] | (1 << mod); } for(i=MAX_SIZE-1;i>=0;i--){ for (j=31;j>=0;j--){ if ((((small_array[i] >> j) & 1) == 1) &&sorted_array[99] == 0){ sorted_array[k] = i*32 + j; k++; } } } for(i=0;i<100;i++){ printf("%d\n",sorted_array[i]); }}
- python两种方法实现从1000万个随机数中找出top n元素(附c语言版)
- 从n个元素中找出第K小的数 利用快排的思想来实现
- java中从1000万个随机数中查找出相同的10万个随机数花的最少时间
- 从n个元素的列表中查找最大值(C实现)
- 组合(从长度为n的字符串中取m个字符)---java两种实现方法
- 从有n个元素的数组中找出出现次数大于n/3次的元素
- 从长度为N的数组中找出所有M个元素组合的优化算法
- 考察最大元素:从n个整数找出最大者
- 生产N个不重复的随机数(从一个数组中随机取N个元素)
- n个数里找出前m个数(或者 从10亿个浮点数中找出最大的1万个)
- 编写程序,在O(n)时间内从数组x[0..n-1]中找出第k个最小的元素?
- 递归方法从m个元素中取出n个元素的算法 ------分析
- 从N个元素中取k个元素子集的c++实现
- 从N个元素中取k个元素子集的减治法实现
- 从一个长度为n的数组中找出前k个最小值的最优实现
- 求N!的C的两种算法实现,求阶乘.从失败中寻找自我
- 在由N个正整数的集合S中,找出最大元素C,满足C=A + B
- 在由N个正整数的集合S中,找出最大元素C,满足C=A + B
- Chromium网页输入事件处理机制简要介绍和学习计划
- fread读取模式的选择
- 文章标题
- [代码实例][C++]MD5算法
- Java面向对象
- python两种方法实现从1000万个随机数中找出top n元素(附c语言版)
- Java之类的封装
- beego autorouter 使用遇到的问题
- biharm_solve_with_factor
- override 和 new
- 【C++11学习笔记】返回类型后置语法
- java怎么从控制台输入的几种方法
- CSharp知识点收集
- iOS 使用第三方库CocoaAsyncSocket进行Tcp通讯