转载请注明地址: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]);}
}
使用第二种排序,如果数据不重复,那么位图数组每个元素的没个bit都代表一个数字,如果数据有重复,那么有些位就空着,会有一点浪费。

本人出于个人兴趣,创建了一个个人公众号,每天筛选国外网友发现的有趣的事情推送到公众号,欢迎关注!

python两种方法实现从1000万个随机数中找出top n元素(附c语言版)相关推荐

  1. java中从1000万个随机数中查找出相同的10万个随机数花的最少时间

    偶然在群里看到有人问到大数据查询,自己也就想了小艾改如何解决,从从1000万个随机数中查找出相同的10万个随机数花的最少时间, 谈到效率,自然是hashmap莫属. import java.util. ...

  2. python求近似值_python 已知一个字符,在一个list中找出近似值或相似值实现模糊匹配...

    已知一个元素,在一个list中找出相似的元素 使用场景: 已知一个其它来源的字符串, 它有可能是不完全与我数据库中相应的字符串匹配的,因此,我需要将其转为适合我数据库中的字符串 使用场景太绕了, 直接 ...

  3. Python两种方法求解登楼梯问题(京东2016笔试题)

    问题:假设一段楼梯共15个台阶,小明一步最多能上3个台阶,那么小明上这段楼梯一共有多少种方法? 解析:从第15个台阶上往回看,有3种方法可以上来(从第14个台阶上一步迈1个台阶上来,从第13个台阶上一 ...

  4. python两种方法读取、修改文件的创建时间、修改时间、访问时间

    看到网上有人出于特种目前,需要修改文件的创建时间和修改时间(访问时间是只要在操作系统里打开文件,系统就会自动更改最后的访问时间,因此此时间无意义,于是在网上查阅结合自己的经验,归纳 一下可行方案,在  ...

  5. python爬虫一:必应图片(从网页源代码中找出图片链接然后下载)

    这里讲解最简单的爬虫:从网页源代码中找出图片链接然后下载 代码: #coding=utf-8 #必应图片爬虫 import re import os import urllib.request url ...

  6. python两种方法解决线程冲突问题

    本博文源于python基础炫酷技能,主要讲述python的线程的问题.大家学到多线程的时候只要搞明白两种东西即可,第一线程通信,另一个叫做线程冲突.本博文就以一种打印数字的方法浅析线程冲突的解决方案! ...

  7. python实现决策树算法sklearn_GitHub - cbyonder/lihang_algorithms: 用python和sklearn两种方法实现李航《统计学习方法》中的算法...

    lihang_algorithms 用python和sklearn实现李航老师的<统计学习方法>中所提到的算法 实验数据:MNIST数据集,这里用kaggle中处理好的数据 官方下载地址: ...

  8. python两种方法实现线性规划

    Python实现线性规划的计算 线性规划的目标函数既可以是求最大值,也可以是求最小值,约束条件可以是小于等于也可以是大于等于,但为了方便起见,MATLAB和python都规定线性规划标准型为: z=m ...

  9. win8.1系统快速关机的两种方法

    win8.1系统快速关机的两种方法 前几天刚刚重装了一遍系统,觉得以前的win8.1的系统用起来还挺顺手的,这次也就装8.1的吧,装完后一切都好,但是晚上关机的时候发现一点跟我上次系统不一样的地方,就 ...

  10. python hist直方图拟合曲线_详解用Python为直方图绘制拟合曲线的两种方法

    直方图是用于展示数据的分组分布状态的一种图形,用矩形的宽度和高度表示频数分布,通过直方图,用户可以很直观的看出数据分布的形状.中心位置以及数据的离散程度等. 在python中一般采用matplotli ...

最新文章

  1. 大数据量下的集合过滤—Bloom Filter
  2. python编程难吗-都说python很简单 真的很好学么?
  3. android camera(三):camera V4L2 FIMC
  4. 选择指定的MySQL数据库
  5. 计算机控制课程设计体会,计算机控制技术课程设计报告
  6. python总结函数图像_PIL使用小结(crop和paste函数)
  7. jeesite如何已生成数据的数据源_如何在postman中自动生成接口请求数据,这个功能你需要知道,可结合浏览器和两大抓包工具使用...
  8. react-native 金币彩带雨下落动画
  9. inner join、 left join 、right join、full outer join之间的区别
  10. 4米乘以12米CAD图_【超干货】CAD铺装排版下料之路径阵列
  11. Sqlite可视化工具sqliteman安装
  12. 冯诺依曼体系结构示意图
  13. AI洞观 | 一文读懂2018安博会四大趋势
  14. Power BI DAX 编写利器 —— DaxStudio 的简单用法
  15. ARM汇编语言编程入门实践
  16. Winform下ToolStrip承载自定义控件或 Windows 窗体控件。
  17. 什么是工作流开源框架?可提高办公效率吗?
  18. 简单探寻GCC编译器背后的故事
  19. Hadoop流程---从tpch到hive
  20. GN_2_使用GN编译自己写的程序

热门文章

  1. 简信CRM:什么样的企业适合引入CRM管理系统?
  2. CRM系统的营销工具亮点
  3. apa引用要在文中吗_APA、MLA格式引用规范
  4. uni-app商城源码/公众号/小程序/APP多端适配
  5. 读取图像数据:通过append方法
  6. 有关CCF的CSP认证
  7. ai人工智能语音分析系统_语音应用搜索正在改变语音AI是领先者
  8. 学生成绩预测模型_学生成绩分析预测
  9. 为什么快捷指令无法将媒体转换为文本_快捷指令自动化可以做什么,不能做什么?...
  10. 基于STM32的CAN通讯测试:让地球仪转起来