Python模拟P2P大文件传输

一、需求及应用场景 考虑到我手上的服务器逐渐的增多,有时候需要大规模的部署同一个文件,例如因为方便使用systemtap这个工具定位问题,需要把手上几百台服务器同时安装kernel-debuginfo这个包,原有的方式采用一个源服务器,采用rsync或者s...
继续阅读 »


一、需求及应用场景


考虑到我手上的服务器逐渐的增多,有时候需要大规模的部署同一个文件,例如因为方便使用systemtap这个工具定位问题,需要把手上几百台服务器同时安装kernel-debuginfo这个包,原有的方式采用一个源服务器,采用rsync或者scp之类的文件传输方式只能做到一个点往下分发这个文件,这个时候下发的速度就会比较的慢,基于以上原因,我写了一个基于bt协议传输文件的小工具,实际测试,传输到10个机房,70多台机器传输一个240M的这个内核文件,到所有的机器,源采用限速2m/s的上传速度,测试的结果大概只要140s,就可以全部传输完毕,这个效率是非常之高,如果不限速的情况下速度会更快,下面把这个程序开源出来。


二、代码


#!/usr/bin/env python
import libtorrent as lt
import sys
import os
import time
from optparse import OptionParser
import socket
import struct
import fcntl
def get_interface_ip(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s',
ifname[:15]))[20:24])
def ip2long(ip):
return reduce(lambda a,b:(a<<8)+b,[int(i) for i in ip.split('.')])
def get_wan_ip_address():
interfaces = set(['eth0', 'eth1', 'eth2', 'eth3', 'em1', 'em2', 'em3', 'em4'])
ip = ''
for i in interfaces:
try:
ip = get_interface_ip(i)
if (ip2long(ip) < ip2long('10.0.0.0') or ip2long(ip) > ip2long('10.255.255.255')) \
and (ip2long(ip) < ip2long('172.16.0.0') or ip2long(ip) > ip2long('172.33.255.255')) \
and (ip2long(ip) < ip2long('192.168.0.0') or ip2long(ip) > ip2long('192.168.255.255')):
return ip
except:
pass
return ip
def make_torrent(path, save):
fs = lt.file_storage()
lt.add_files(fs, path)
if fs.num_files() == 0:
print 'no files added'
sys.exit(1)
input = os.path.abspath(path)
basename = os.path.basename(path)
t = lt.create_torrent(fs, 0, 4 [i] 1024 [/i] 1024)
t.add_tracker("http://10.0.1.5:8760/announce")
t.set_creator('libtorrent %s' % lt.version)
lt.set_piece_hashes(t, os.path.split(input)[0], lambda x: sys.stderr.write('.'))
sys.stderr.write('\n')
save = os.path.dirname(input)
save = "%s/%s.torrent" % (save, basename)
f=open(save, "wb")
f.write(lt.bencode(t.generate()))
f.close()
print "the bt torrent file is store at %s" % save
def dl_status(handle):
while not (handle.is_seed()):
s = handle.status()
state_str = ['queued', 'checking', 'downloading metadata', \
'downloading', 'finished', 'seeding', 'allocating', 'checking fastresume']
print '\ractive_time: %d, %.2f%% complete (down: %.1f kb/s up: %.1f kB/s peers: %d, seeds: %d) %s' % \
(s.active_time, s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \
s.num_peers, s.num_seeds, state_str[s.state]),
sys.stdout.flush()
time.sleep(1)
def seed_status(handle, seedtime=100):
seedtime = int(seedtime)
if seedtime < 100:
seedtime = 100
while seedtime > 0:
seedtime -= 1
s = handle.status()
state_str = ['queued', 'checking', 'downloading metadata', \
'downloading', 'finished', 'seeding', 'allocating', 'checking fastresume']
print '\rseed_time: %d, %.2f%% complete (down: %.1f kb/s up: %.1f kB/s peers: %d, seeds: %d) %s' % \
(s.active_time, s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \
s.num_peers, s.num_seeds, state_str[s.state]),
sys.stdout.flush()
time.sleep(1)
def remove_torrents(torrent, session):
session.remove_torrent(torrent)
def read_alerts(session):
alert = session.pop_alert()
while alert:
#print alert, alert.message()
alert = session.pop_alert()
def download(torrent, path, upload_rate_limit=0, seedtime=100):
try:
session = lt.session()
session.set_alert_queue_size_limit(1024 * 1024)
sts = lt.session_settings()
sts.ssl_listen = False
sts.user_agent = "Thunder deploy system"
sts.tracker_completion_timeout = 5
sts.tracker_receive_timeout = 5
sts.stop_tracker_timeout = 5
sts.active_downloads = -1
sts.active_seeds = -1
sts.active_limit = -1
sts.auto_scrape_min_interval = 5
sts.udp_tracker_token_expiry = 120
sts.min_announce_interval = 1
sts.inactivity_timeout = 60
sts.connection_speed = 10
sts.allow_multiple_connections_per_ip = True
sts.max_out_request_queue = 128
sts.request_queue_size = 3
sts.use_read_cache = False
session.set_settings(sts)
session.set_alert_mask(lt.alert.category_t.tracker_notification | lt.alert.category_t.status_notification)
session.set_alert_mask(lt.alert.category_t.status_notification)
ipaddr = get_wan_ip_address()
#print ipaddr
if ipaddr == "":
session.listen_on(6881, 6881)
else:
session.listen_on(6881, 6881, ipaddr)
limit = int(upload_rate_limit)
if limit>=100:
session.set_upload_rate_limit(limit*1024)
session.set_local_upload_rate_limit(limit*1024)
print session.upload_rate_limit()
torrent_info = lt.torrent_info(torrent)
add_params = {
'save_path': path,
'storage_mode': lt.storage_mode_t.storage_mode_sparse,
'paused': False,
'auto_managed': True,
'ti': torrent_info,
}
handle = session.add_torrent(add_params)
read_alerts(session)
st = time.time()
dl_status(handle)
et = time.time() - st
print '\nall file download in %.2f\nstart to seeding\n' % et
sys.stdout.write('\n')
handle.super_seeding()
seed_status(handle, seedtime)
remove_torrents(handle, session)
assert len(session.get_torrents()) == 0
finally:
print 'download finished'
if __name__ == '__main__':
usage = "usage: %prog [options] \n \
%prog -d -f -s \n \
or \n \
%prog -m -p -s \n"
parser = OptionParser(usage=usage)
parser.add_option("-d", "--download", dest="download",
help="start to download file", action="store_false", default=True)
parser.add_option("-f", "--file", dest="file",
help="torrent file")
parser.add_option("-u", "--upload", dest="upload",
help="set upload rate limit, default is not limit", default=0)
parser.add_option("-t", "--time", dest="time",
help="set seed time, default is 100s", default=100)
parser.add_option("-p", "--path", dest="path",
help="to make torrent with this path")
parser.add_option("-m", "--make", dest="make",
help="make torrent", action="store_false", default=True)
parser.add_option("-s", "--save", dest="save",
help="file save path, default is store to ./", default="./")
(options, args) = parser.parse_args()
#download(sys.argv[1])
if len(sys.argv) != 6 and len(sys.argv) != 4 and len(sys.argv) != 8 and len(sys.argv) != 10:
parser.print_help()
sys.exit()
if options.download == False and options.file !="":
download(options.file, options.save, options.upload, options.time)
elif options.make == False and options.path != "":
make_torrent(options.path, options.save)


三、使用


1、环境准备
需要在所有的os上面安装一个libtorrent的库,下载地址:http://code.google.com/p/libtorrent/downloads/list (国内需使用代理访问)

记得编译的时候带上./configure –enable-python-binding,然后mak,make install,进入binding目录,make,make install就可以运行这个小的工具。当然大规模部署不可能采用每一台都去编译安装的方式,只要把编译出来的libtorrent.so libtorrent-rasterbar.so.7的文件跟bt.py这个文件放到同一个目录,另外写一个shell脚本。
lib=`dirname $0`
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$lib
python bt.py -d -f <种子文件> -s <文件保存路径> -t <做种时间> -u <限制上传速度>

2、使用方法
在源服务器上生成种子文件:
python bt.py -m -p <要发布的文件或者文件夹> -s <种子保存地址>

在源服务器上发布文件:
python bt.py -d -f <种子文件> -s <文件保存路径> -t <做种时间> -u <限制上传速度>
其中做种时间默认设置是100s,上传速度默认不限制,限制速度的单位是kb。
 
只要有一台机器完成了,就自动作为种子,在下载的过程中也会上传,任何一台机器都可以作为源服务器,当然了这里面还有中心的tracker服务器,脚本当中,我搭建了一个tracker源服务器,放到10.0.1.5端口是8760上面,当然大家也可以采用opentracker这个软件自己搭建一个tracker服务器,修改其中的源代码对应部分,另外考虑到发布都是私有文件,代码当作已经禁止了dht,如果还想更安全,就自己搭建一个私有的tracker server,具体搭建方法就使用一下搜索引擎,查找一下搭建的方法!

目前基本做到可以使用,后续考虑更简单一点,采用磁力链接的方式,这样就可以做到不用每台都要拷贝一个种子文件,采用一个单独的命令行就可以发布整个文件。

在使用实用中进行大文件传输时,可以再结合ansible、saltstack等自动化运维工具需要在多台主机之间传大文件时,可以通过该文件加快传输速度,增加网络利用率。 收起阅读 »

Python input和raw_input的区别

使用input和raw_input都可以读取控制台的输入,但是input和raw_input在处理数字时是有区别的 纯数字输入 当输入为纯数字时 []input返回的是数值类型,如int,float[/][]raw_inpout返回的是字符串类型,stri...
继续阅读 »
使用input和raw_input都可以读取控制台的输入,但是input和raw_input在处理数字时是有区别的


纯数字输入


当输入为纯数字时
    []input返回的是数值类型,如int,float[/][]raw_inpout返回的是字符串类型,string类型[/]

cb.png


输入字符串为表达式


input会计算在字符串中的数字表达式,而raw_input不会。
如输入 "57 + 3":
input会得到整数60
raw_input会得到字符串"57 + 3"
cb1.png

输入字符串结果如下:
cb3.png

通过上面的实验我们知道input它会根据用户输入变换相应的类型,而且如果要输入字符和字符串的时候必须要用引号包起来,而raw_input则是不管用户输入什么类型的都会转变成字符型.


python input的实现


python input的文档,你可以发现input其实是通过raw_input来实现的,原理很简单,就下面一行代码:
def input(prompt):
return (eval(raw_input(prompt)))
收起阅读 »

TVR时光机

TVR将技术与艺术完美融合,打造国内最好的VR游戏。   TVR时光机虚拟现实是北大几位优秀研究生及专业游戏行业人士共同创立,主要在做的是基于头戴式显示设备的虚拟现实内容制作,目标是打造最优秀的虚拟现实内容。主营业务《Finding》是一款轻度休闲的虚拟现...
继续阅读 »
TVR将技术与艺术完美融合,打造国内最好的VR游戏。
 
TVR时光机虚拟现实是北大几位优秀研究生及专业游戏行业人士共同创立,主要在做的是基于头戴式显示设备的虚拟现实内容制作,目标是打造最优秀的虚拟现实内容。主营业务《Finding》是一款轻度休闲的虚拟现实游戏(Immersive game)。它包含动作和解谜元素,配合虚拟现实设备,给玩家带来强烈的视觉冲击和前所未有的体验。
 
Web端链接 http://time-vr.com 
iPhone下载链接  https://itunes.apple.com/cn/app/id561750702
 
融资情况:
天使轮   2015.02   融资金额: ¥ 数百万 融资估值: 未披露     投资方:IDG资本
公司全称:北京时光梦幻科技有限公司  
法定代表:王世元
注册资本:28 万元 人民币
注册日期:2015-03-12
vr.png

vr2.png

他们正在36kr融资:https://rong.36kr.com/company/170680/overview
可以关注哦! 收起阅读 »

Python编码风格小结

一、缩进统一 缩进统一,一般可以提高我们代码的可读性,开发过程中协作统一的规范。相对于其他语言python的缩进相对要求比较严格,如果缩进不对会造成代码的错误。比如我们可以使用tab/两个空格/四个空格等作为python开发过程中的一个标准,但是为了合作协...
继续阅读 »


一、缩进统一


缩进统一,一般可以提高我们代码的可读性,开发过程中协作统一的规范。相对于其他语言python的缩进相对要求比较严格,如果缩进不对会造成代码的错误。比如我们可以使用tab/两个空格/四个空格等作为python开发过程中的一个标准,但是为了合作协同我们还是以四个空格作为标准为好!
我们那些地方需要考虑缩进呢?


二、变量规范


    []标识符的第一个字符必须是字母表中的字母(大写或小写)或者一个下划线(‘ _ ’)[/][]标识符名称的其他部分可以由字母(大写或小写)、下划线(‘ _ ’)或数字(0-9)组成。[/][]标识符名称是对大小写敏感的。例如,myname和myName不是一个标识符。注意前者中的小写n和后者中的大写N。[/][]有效 标识符名称的例子有i、__my_name、name_23和a1b2_c3。[/][]无效 标识符名称的例子有2example、this is spaced out和my-name。[/]

三、注释风格

Python中单引号字符串和双引号字符串都是相同的。注意尽量避免在字符串中的反斜杠以提高可读性。
    []‘hello world!’       //单行注释[/][] “ hello , my name’s xx”   //单行注释[/][]‘’’ Hello, my name’s xx,    This is the first time to be here, it’s nice to see you guys.’’’               //多行注释[/]

收起阅读 »

Airmotionlabs移动健康先行者

生活的改变,从一个口罩开始,AURA清吸口罩正在京东众筹,改变从你开始!    
继续阅读 »
Airmotion.jpg

生活的改变,从一个口罩开始,AURA清吸口罩正在京东众筹,改变从你开始!
 




  收起阅读 »

利用Python实现域名查询和whois查询

一、域名查询 万网提供了域名查询接口,接口采用HTTP协议: 接口URL:http://panda.www.net.cn/cgi-bin/check.cgi 接口参数:area_domain,接口参数值为标准域名,例:52bong.com 调用举例:htt...
继续阅读 »


一、域名查询


万网提供了域名查询接口,接口采用HTTP协议:
接口URL:http://panda.www.net.cn/cgi-bin/check.cgi
接口参数:area_domain,接口参数值为标准域名,例:52bong.com
调用举例:
http://panda.www.net.cn/cgi-bin/check.cgi?area_domain=52bong.com
返回:


200
52bong.com
211 : Domain exists
返回结果说明:
200 返回码,200表示返回成功
52bong.com 表示当前查询的域名
211 : Domain exists 返回结果的原始信息,主要有以下几种

original=210 : Domain name is available 表示域名可以注册
original=211 : Domain exists 表示域名已经注册
original=212 : Domain name is invalid 表示查询的域名无效
original=213 : Time out 查询超时


Python实现


1.1 查询已经被注册的域名
>>> import urllib2
[quote]>> req=urllib2.urlopen('http://panda.www.net.cn/cgi-bin/check.cgi?area_domain=52bong.com')
>>> print (req.read().decode())
返回结果:不可用,已经被注册
domain1.png

1.2 查询没有被注册的域名
>>> req2=urllib2.urlopen('http://panda.www.net.cn/cgi-bin/check.cgi?area_domain=52bongxxx.com')
>>> print (req2.read().decode())
返回结果:可用,未被注册
domain2.png

1.3 查询不加后缀的域名
>>> req3=urllib2.urlopen('http://panda.www.net.cn/cgi-bin/check.cgi?area_domain=52bong')
>>> print (req3.read().decode())
返回结果:超时
domain3.png


二、whois查询


由于没有找到像域名查询接口那样好的API,这里直接抓取站长之家的whois查询页面(http://whois.chinaz.com/)
>>> whois = urllib2.urlopen('http://whois.chinaz.com/qclouds.com.cn')
>>> print (whois.read().decode())
在返回的结果中有这样一段html代码,这段信息就是查询的whois信息
html.png
[/quote] 收起阅读 »

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 273: ordinal not in range(128)

今天利用python urllib2去站长之家查询域名的whois,然后出现如下错误:>>> whois = urllib2.urlopen('http://whois.chinaz.com/qclouds.com.cn') [quote]>> print (...
继续阅读 »
今天利用python urllib2去站长之家查询域名的whois,然后出现如下错误:
>>> whois = urllib2.urlopen('http://whois.chinaz.com/qclouds.com.cn')
[quote]>> print (whois.read().decode())
Traceback (most recent call last):
File "", line 1, in
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 273: ordinal not in range(128)
原因分析:
python的str默认是ascii编码,和unicode编码冲突,就会报这个标题错误。
通过搜集网上的资料,自己多次尝试,问题总算是解决了,在代码中加上如下几句即可:
import sys
reload(sys)
sys.setdefaultencoding('utf8')
不知道还有更好的办法吗?
修复后,结果如下:
pycode.png

http://docs.python.org/howto/unicode.html  这个是python的unicode编码API文档,英文好的同学可以看一下,加深理解。
参考资料:https://groups.google.com/forum/#!topic/python-cn/9I73RUUnQPY 


混淆了 python2 里边的 str 和 unicode 数据类型


你需要的是让编码用实际编码而不是 ascii
对需要 str->unicode 的代码,可以在前边写上
import sys
reload(sys)
sys.setdefaultencoding(‘utf8′)
把 str 编码由 ascii 改为 utf8 (或 gb18030)
python3 区分了 unicode str 和 byte arrary,并且默认编码不再是 ascii[/quote] 收起阅读 »

八大持续集成工具

下面依次介绍8大持续集成工具 一、Hudson Hudson 是一个可扩展的持续集成引擎,主要用于: []持续、自动地构建/测试软件项目,如CruiseControl与DamageControl。[/][]监控一些定时执行的任务。[/] Huds...
继续阅读 »
下面依次介绍8大持续集成工具


一、Hudson


hudson.png

Hudson 是一个可扩展的持续集成引擎,主要用于:
    []持续、自动地构建/测试软件项目,如CruiseControl与DamageControl。[/][]监控一些定时执行的任务。[/]

Hudson的特性如下:
    []易于安装-只要把hudson.war部署到servlet容器,不需要数据库支持。[/][]易于配置-所有配置都是通过其提供的web界面实现。[/][]集成RSS/E-mail/IM-通过RSS发布构建结果或当构建失败时通过e-mail实时通知。[/][]生成JUnit/TestNG测试报告。[/][]分布式构建支持-Hudson能够让多台计算机一起构建/测试。[/][]文件识别- Hudson能够跟踪哪次构建生成哪些jar,哪次构建使用哪个版本的jar等。[/][]插件支持-Hudson可以通过插件扩展,你可以开发适合自己团队使用的工具。[/]

二、CruiseControl

CruiseControl.png
CruiseControl是一个针对持续构建程序(项目持续集成)的框架,它包括一个email通知的插件,Ant和各种各样的CVS工具。CruiseControl提供了一个Web接口, 可随时查看当前的编译状况和历史状况。

三、Continuum

contuion.png
Apache Continuum 是最新的 CI 服务器之一,也是值得关注的一个新进入者。Continuum 的安装和配置很简单:只要下载和释放 ZIP 文件,运行命令行程序,就可以运行了。基于 Web 的界面使得配置项目很容易。而且,还不需要安装 Web 服务器,因为 Continuum 内置了 Jetty Web 服务器。并且,Continuum 可以作为 Windows 服务运行,还在应用程序的某些部分嵌入了上下文敏感的文档,从而提供了很多帮助。
Apache Continuum的特性:
    []与Maven 2.x的紧密集成[/][]与Maven SCM的紧密集成,支持Subversion/CVS/Starteam/Clearcase/Perforce[/][]基于web的容易使用的安装和配置接口[/][]Quartz-based scheduling[/][]简单的添加新的项目工程的方法[/][]用于 integration, automation and remoting的XML-RPC interface[/][]邮件提醒和IM(即时通讯)提醒IRC/Jabber/MSN[/]

四、QuickBuild

quickbuild.png
QuickBuild 是一个持续集成和发布管理的服务器软件,它提供了一个统一的控制台用来管理这些发布的信息。

五、Bamboo

bamboo.png
Atlassian Bamboo 是一款持续集成构建服务器软件(Build Server)。
Bamboo的特点:
    []简单的用户界面 容易安装 – 顺利的话,5分钟内就可以让运行起来! 自动检测你的设置 – 如果您的Server上使用了Maven,Ant或者Java设置, Bamboo会自动检测他们; [/][]连续的日志 – 监测你的build的colour coded日志;[/][] 容易显示所有项目[/]

六、Jenkins

jenkines.png
Jenkins 的前身是 Hudson 是一个可扩展的持续集成引擎。主要用于:[list=1][]持续、自动地构建/测试软件项目,如CruiseControl与DamageControl。[/][]监控一些定时执行的任务。[/]


七、TeamCity


teamcity.png

cityteam.png
TeamCity是一款功能强大的持续集成(Continue Integration)工具,包括服务器端和客户端,目前支持Java,.Net项目开发。 TeamCity提供一系列特性可以让团队快速实现持续继承:IDE工具集成、各种消息通知、各种报表、项目的管理、分布式的编译等等,所有的这些,都是 让你的团队快速享有持续继承带来的效率提升、高质量的软件保障。


八、CI-Eye


cieye.png
CI-Eye 是一个强大的持续集成构建,无需安装和设置,CI-Eye 通过 REST API 跟很多不同的 CI 服务器交互,当前支持 Hudson, Jenkins, 以及 TeamCity. CI-Eye 在一个独立的 Web 应用中运行。

收起阅读 »

UPYUN 架构与运维大会 ·上海站PPT分享

分享PPT下载地址:http://pan.baidu.com/s/1beusTS​  开源技术社区分享 欢迎订阅微信公众号:
继续阅读 »
sh_upyun.png

upyun_sh.png

分享PPT下载地址:http://pan.baidu.com/s/1beusTS​ 
开源技术社区分享 欢迎订阅微信公众号:
opsk.jpg
收起阅读 »

数据分析师常见的10道面试题及答案

1、海量日志数据,提取出某日访问百度次数最多的那个IP。 首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有个2^32个IP。同样可以采用映射的方法,比如模1000,把整个大文件映射为1000个小文件,再...
继续阅读 »


1、海量日志数据,提取出某日访问百度次数最多的那个IP。


首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有个2^32个IP。同样可以采用映射的方法,比如模1000,把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用hash_map进行频率统计,然后再找出频率最大的几个)及相应的频率。然后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。
或者如下阐述:
算法思想:分而治之+Hash
1.IP地址最多有2^32=4G种取值情况,所以不能完全加载到内存中处理; 

2.可以考虑采用“分而治之”的思想,按照IP地址的Hash(IP)24值,把海量IP日志分别存储到1024个小文件中。这样,每个小文件最多包含4MB个IP地址;
3.对于每一个小文件,可以构建一个IP为key,出现次数为&#118alue的Hash map,同时记录当前出现次数最多的那个IP地址; 

4.可以得到1024个小文件中的出现次数最多的IP,再依据常规的排序算法得到总体上出现次数最多的IP;


2、搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字


假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G。
典型的Top K算法,还是在这篇文章里头有所阐述,文中,给出的最终算法是:
第一步、先对这批海量数据预处理,在O(N)的时间内用Hash表完成统计(之前写成了排序,特此订正。July、2011.04.27); 

第二步、借助堆这个数据结构,找出Top K,时间复杂度为N‘logK。即,借助堆结构,我们可以在log量级的时间内查找和调整/移动。因此,维护一个K(该题目中是10)大小的小根堆,然后遍历300万的Query,分别和根元素进行对比所以,我们最终的时间复杂度是:O(N) + N’*O(logK),(N为1000万,N’为300万)。
或者:采用trie树,关键字域存该查询串出现的次数,没有出现为0。最后用10个元素的最小推来对出现频率进行排序。


3、有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。


方案:顺序读文件中,对于每个词x,取hash(x)P00,然后按照该值存到5000个小文件(记为x0,x1,…x4999)中。这样每个文件大概是200k左右。 

如果其中的有的文件超过了1M大小,还可以按照类似的方法继续往下分,直到分解得到的小文件的大小都不超过1M。
对每个小文件,统计每个文件中出现的词以及相应的频率(可以采用trie树/hash_map等),并取出出现频率最大的100个词(可以用含100个结点的最小堆),并把100个词及相应的频率存入文件,这样又得到了5000个文件。下一步就是把这5000个文件进行归并(类似与归并排序)的过程了。


4、有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。


还是典型的TOP K算法,解决方案如下: 

方案1:
  顺序读取10个文件,按照hash(query)的结果将query写入到另外10个文件(记为)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
  
   找一台内存在2G左右的机器,依次对用hash_map(query,query_count)来统计每个query出现的次数。利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(记为)。
  对这10个文件进行归并排序(内排序与外排序相结合)。 
方案2:
  一般query的总量是有限的,只是重复的次数比较多而已,可能对于所有的query,一次性就可以加入到内存了。这样,我们就可以采用trie树/hash_map等直接来统计每个query出现的次数,然后按出现次数做快速/堆/归并排序就可以了。 

方案3:
  与方案1类似,但在做完hash,分成多个文件后,可以交给多个文件来处理,采用分布式的架构来处理(比如MapReduce),最后再进行合并。


5、 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?


方案1:可以估计每个文件安的大小为5G×64=320G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。
  遍历文件a,对每个url求取hash(url)00,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,…,a999)中。这样每个小文件的大约为300M。
  遍历文件b,采取和a相同的方式将url分别存储到1000小文件(记为b0,b1,…,b999)。这样处理后,所有可能相同的url都在对应的小文件(a0vsb0,a1vsb1,…,a999vsb999)中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
  求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。 

方案2:如果允许有一定的错误率,可以使用Bloom filter,4G内存大概可以表示340亿bit。将其中一个文件中的url使用Bloom filter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否与Bloomfilter,如果是,那么该url应该是共同的url(注意会有一定的错误率)。
Bloom filter日后会在本BLOG内详细阐述。


6、在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数。


方案1:采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存2^32 * 2 bit=1 GB内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。

方案2:也可采用与第1题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。


7、腾讯面试题:给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?


与上第6题类似,我的第一反应时快速排序+二分查找。以下是其它更好的方法: 

方案1:oo,申请512M的内存,一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。 
方案2:这个问题在《编程珠玑》里有很好的描述,大家可以参考下面的思路,探讨一下:
  又因为2^32为40亿多,所以给定一个数可能在,也可能不在其中;
  这里我们把40亿个数中的每一个用32位的二进制来表示
  假设这40亿个数开始放在一个文件中。
  然后将这40亿个数分成两类:
1.最高位为0
2.最高位为1
  并将这两类分别写入到两个文件中,其中一个文件中数的个数<=20亿,而另一个>=20亿(这相当于折半了);
  与要查找的数的最高位比较并接着进入相应的文件再查找
  再然后把这个文件为又分成两类:
1.次最高位为0
2.次最高位为1
  并将这两类分别写入到两个文件中,其中一个文件中数的个数<=10亿,而另一个>=10亿(这相当于折半了);
  与要查找的数的次最高位比较并接着进入相应的文件再查找。
  …….
  以此类推,就可以找到了,而且时间复杂度为O(logn),方案2完。
  附:这里,再简单介绍下,位图方法:
  使用位图法判断整形数组是否存在重复
  判断集合中存在重复是常见编程任务之一,当集合中数据量比较大时我们通常希望少进行几次扫描,这时双重循环法就不可取了。
  位图法比较适合于这种情况,它的做法是按照集合中最大元素max创建一个长度为max+1的新数组,然后再次扫描原数组,遇到几就给新数组的第几位置上1,如遇到5就给新数组的第六个元素置1,这样下次再遇到5想置位时发现新数组的第六个元素已经是1了,这说明这次的数据肯定和以前的数据存在着重复。这种给新数组初始化时置零其后置一的做法类似于位图的处理方法故称位图法。它的运算次数最坏的情况为2N。如果已知数组的最大值即能事先给新数组定长的话效率还能提高一倍。
  欢迎,有更好的思路,或方法,共同交流。


8、怎么在海量数据中找出重复次数最多的一个?


方案1:先做hash,然后求模映射为小文件,求出每个小文件中重复次数最多的一个,并记录重复次数。然后找出上一步求出的数据中重复次数最多的一个就是所求(具体参考前面的题)。


9、上千万或上亿数据(有重复),统计其中出现次数最多的钱N个数据。


方案1:上千万或上亿的数据,现在的机器的内存应该能存下。所以考虑采用hash_map/搜索二叉树/红黑树等来进行统计次数。然后就是取出前N个出现次数最多的数据了,可以用第2题提到的堆机制完成。


10、一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。


方案1:这题是考虑时间效率。用trie树统计每个词出现的次数,时间复杂度是O(n[i]le)(le表示单词的平准长度)。然后是找出出现最频繁的前10个词,可以用堆来实现,前面的题中已经讲到了,时间复杂度是O(n[/i]lg10)。所以总的时间复杂度,是O(n[i]le)与O(n[/i]lg10)中较大的哪一个。
  附、100w个数中找出最大的100个数。
  
方案1:在前面的题中,我们已经提到了,用一个含100个元素的最小堆完成。复杂度为O(100w*lg100)。

方案2:采用快速排序的思想,每次分割之后只考虑比轴大的一部分,知道比轴大的一部分在比100多的时候,采用传统排序算法排序,取前100个。复杂度为O(100w[i]100)。 
方案3:采用局部淘汰法。选取前100个元素,并排序,记为序列L。然后一次扫描剩余的元素x,与排好序的100个元素中最小的元素比,如果比这个最小的要大,那么把这个最小的元素删除,并把x利用插入排序的思想,插入到序列L中。依次循环,知道扫描了所有的元素。复杂度为O(100w[/i]100)。
收起阅读 »