python两种方法实现从1000万个随机数中找出top n元素(附c语言版)
转载请注明地址: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)] = numberelse: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]
real 0m23.115s
user 0m22.504s
sys 0m0.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]real 0m10.210s
user 0m9.184s
sys 0m1.012s
速度提升很多(具体时间看电脑配置,我公司的电脑就只要6秒),而且随着N增大,第二种方法耗时不会怎么增加,但是第一种与第二种方法耗时差距会越来越大,因为第一种方法寻找最小数开销挺大的。
比如top n n取1000,第二种方法
real 0m9.714s
user 0m9.572s
sys 0m0.124s
第一种方法:
real 2m51.504s
user 2m51.116s
sys 0m0.192s
两者差了将近20倍,恐怖
n等于1000这里我把print去掉了,因为print1000个会耗时不少,影响结果,所以这里n=1000时比n=100时耗时少
使用第二种排序,如果数据不重复,那么位图数组每个元素的没个bit都代表一个数字,如果数据有重复,那么有些位就空着,会有一点浪费。
本来想试试c语言,把1000w个数全放在数组里看看有多快,但是我的c语言很菜,嫌麻烦,所以还是从文件读取的,而且没用缓存,直接一行一行读取的,就更慢了,
,执行结果:
9999882
9999880
9999879
9999878
9999877
9999875
9999874
9999873
9999872
9999871
9999870
9999869
9999867
9999866
9999865
9999864
9999863
9999860
9999859real<span style="white-space:pre"> </span>0m0.676s
user<span style="white-space:pre"> </span>0m0.580s
sys<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语言版)相关推荐
- java中从1000万个随机数中查找出相同的10万个随机数花的最少时间
偶然在群里看到有人问到大数据查询,自己也就想了小艾改如何解决,从从1000万个随机数中查找出相同的10万个随机数花的最少时间, 谈到效率,自然是hashmap莫属. import java.util. ...
- python求近似值_python 已知一个字符,在一个list中找出近似值或相似值实现模糊匹配...
已知一个元素,在一个list中找出相似的元素 使用场景: 已知一个其它来源的字符串, 它有可能是不完全与我数据库中相应的字符串匹配的,因此,我需要将其转为适合我数据库中的字符串 使用场景太绕了, 直接 ...
- Python两种方法求解登楼梯问题(京东2016笔试题)
问题:假设一段楼梯共15个台阶,小明一步最多能上3个台阶,那么小明上这段楼梯一共有多少种方法? 解析:从第15个台阶上往回看,有3种方法可以上来(从第14个台阶上一步迈1个台阶上来,从第13个台阶上一 ...
- python两种方法读取、修改文件的创建时间、修改时间、访问时间
看到网上有人出于特种目前,需要修改文件的创建时间和修改时间(访问时间是只要在操作系统里打开文件,系统就会自动更改最后的访问时间,因此此时间无意义,于是在网上查阅结合自己的经验,归纳 一下可行方案,在 ...
- python爬虫一:必应图片(从网页源代码中找出图片链接然后下载)
这里讲解最简单的爬虫:从网页源代码中找出图片链接然后下载 代码: #coding=utf-8 #必应图片爬虫 import re import os import urllib.request url ...
- python两种方法解决线程冲突问题
本博文源于python基础炫酷技能,主要讲述python的线程的问题.大家学到多线程的时候只要搞明白两种东西即可,第一线程通信,另一个叫做线程冲突.本博文就以一种打印数字的方法浅析线程冲突的解决方案! ...
- python实现决策树算法sklearn_GitHub - cbyonder/lihang_algorithms: 用python和sklearn两种方法实现李航《统计学习方法》中的算法...
lihang_algorithms 用python和sklearn实现李航老师的<统计学习方法>中所提到的算法 实验数据:MNIST数据集,这里用kaggle中处理好的数据 官方下载地址: ...
- python两种方法实现线性规划
Python实现线性规划的计算 线性规划的目标函数既可以是求最大值,也可以是求最小值,约束条件可以是小于等于也可以是大于等于,但为了方便起见,MATLAB和python都规定线性规划标准型为: z=m ...
- win8.1系统快速关机的两种方法
win8.1系统快速关机的两种方法 前几天刚刚重装了一遍系统,觉得以前的win8.1的系统用起来还挺顺手的,这次也就装8.1的吧,装完后一切都好,但是晚上关机的时候发现一点跟我上次系统不一样的地方,就 ...
- python hist直方图拟合曲线_详解用Python为直方图绘制拟合曲线的两种方法
直方图是用于展示数据的分组分布状态的一种图形,用矩形的宽度和高度表示频数分布,通过直方图,用户可以很直观的看出数据分布的形状.中心位置以及数据的离散程度等. 在python中一般采用matplotli ...
最新文章
- 大数据量下的集合过滤—Bloom Filter
- python编程难吗-都说python很简单 真的很好学么?
- android camera(三):camera V4L2 FIMC
- 选择指定的MySQL数据库
- 计算机控制课程设计体会,计算机控制技术课程设计报告
- python总结函数图像_PIL使用小结(crop和paste函数)
- jeesite如何已生成数据的数据源_如何在postman中自动生成接口请求数据,这个功能你需要知道,可结合浏览器和两大抓包工具使用...
- react-native 金币彩带雨下落动画
- inner join、 left join 、right join、full outer join之间的区别
- 4米乘以12米CAD图_【超干货】CAD铺装排版下料之路径阵列
- Sqlite可视化工具sqliteman安装
- 冯诺依曼体系结构示意图
- AI洞观 | 一文读懂2018安博会四大趋势
- Power BI DAX 编写利器 —— DaxStudio 的简单用法
- ARM汇编语言编程入门实践
- Winform下ToolStrip承载自定义控件或 Windows 窗体控件。
- 什么是工作流开源框架?可提高办公效率吗?
- 简单探寻GCC编译器背后的故事
- Hadoop流程---从tpch到hive
- GN_2_使用GN编译自己写的程序