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

目录:

文章目录

  • Step 1 产生50亿 URL
  • Step 2 将50亿URL大文件哈希为10000个小文件
    • Step 2.1 字符串哈希函数BKDRHash
    • Step 2.2 获取文件大小
    • Step 2.3 获取某一目录下指定后缀的所有文件
  • Step 3 使用set将小文件进行求交操作,最终得到相同URL

方案1:每个文件50亿个URL,每个URL最长64个字节,可以估计每个文件安的大小为5000,000,000 ×64bit=320,000,000,000bit ≈ 300,000G,远远大于内存限制的4G,同时需要大硬盘(这里不考虑分布式计算)。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。

  • 遍历文件a,对每个url求取hash(url)%100,000hash(url)\%100,000hash(url)%100,000,然后根据所取得的值将url分别存储到100,000个小文件(记为a0,a1,a2,...,a99998,a99999a_0,a_1,a_2, ..., a_{99998},a_{99999}a0​,a1​,a2​,...,a99998​,a99999​)中。这样每个小文件的大约为3G。

  • 遍历文件b,采取和a相同的方式将url分别存储到10000小文件中(记为b0,b1,b2,...,b99998,b99999b_0,b_1,b_2, ..., b_{99998},b_{99999}b0​,b1​,b2​,...,b99998​,b99999​)。这样处理后,所有可能相同的url都在对应的小文件(a0v.s.b0,a1v.s.b1,...,a99999v.s.b99999a_0 v.s. b_0, a_1 v.s. b_1, ..., a_{99999} v.s. b_{99999}a0​v.s.b0​,a1​v.s.b1​,...,a99999​v.s.b99999​)中,不对应的小文件不可能有相同的url。然后我们只要求出10000对小文件中相同的url即可。

  • 求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。

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

读者反馈@crowgns:

  1. hash后要判断每个文件大小,如果hash分的不均衡有文件较大,还应继续hash分文件,换个hash算法第二次再分较大的文件,一直分到没有较大的文件为止。这样文件标号可以用A1-2表示(第一次hash编号为1,文件较大所以参加第二次hash,编号为2)
  2. 由于1存在,第一次hash如果有大文件,不能用直接set的方法。建议对每个文件都先用字符串自然顺序排序,然后具有相同hash编号的(如都是1-3,而不能a编号是1,b编号是1-1和1-2),可以直接从头到尾比较一遍。对于层级不一致的,如a1,b有1-1,1-2-1,1-2-2,层级浅的要和层级深的每个文件都比较一次,才能确认每个相同的uri。

以上是网上流传最广的思路,基于以上想法。我主要有两点想法,

  • 其一,优秀的哈希算法是经过密码学家证明推敲的,不会随着取模操作而造成大范围冲突。本文将使用到《字符串哈希算法——BKDRHash》算法。

  • 其二,对于以上读者反馈中需要二次哈希的地方(这种概率极小)。可以将整个过程视为一场递归——即,将一次哈希中大小超过阈值的文件暂不处理(假设一次哈希后,所得文件a9527>10Ga_{9527} > 10Ga9527​>10G,那么这里先不处理它,而直接查询对应的b9527b_{9527}b9527​,如果对应的b不存在,那么可以丢弃a9527a_{9527}a9527​。否则,在后续处理中,对于a9527a_{9527}a9527​使用另外一种哈希算法重新哈希,同时对b9527b_{9527}b9527​也必须使用同一种哈希算法重新哈希,整个过程转化为了递归)。

以下是对上述算法的实现,

Step 1 产生50亿 URL

实际操作中,自定义N值大小即可,量力(硬盘)而行。这里没有产生50亿URL。

//[a,b]
#define random(a,b) ((rand()%(b-a+1))+a)
#define N 500000
string url  = "-0123456789abcdefghijklmnopqrstuvwxyz";
void generateUrl(string file)
{ofstream out(file);int n = 0;if (out.is_open()){for (int i = 0; i < N; ++i){int size = random(1,64);//64bitstring s = "https://www.";for (int j = 0, l = 1; j < size; ++j){s += url[random(l,36)];//1+10+26-1=36l = (s[s.size()-1] == '-' || j >= size-1) ? 1: 0;}s+=".com/";out << s <<endl;}out.close();}
}

Step 2 将50亿URL大文件哈希为10000个小文件

具体牵涉到不少其他函数,下文将给出。

bool split_big_file(string file_name, string suffix, string store_path, unsigned long count_to_split)
{if (!file_name.size())return false;ifstream in(file_name);if (!in.is_open())return false;string line;while (getline(in, line)){string split_file_name = store_path;split_file_name += to_string(bkdr_hash(line.c_str()) % count_to_split);split_file_name += suffix;ofstream out(split_file_name, ios::app);if (!out.is_open()){in.close();return false;}out << line << endl;out.close();}in.close();return true;
}

Step 2.1 字符串哈希函数BKDRHash

更多的哈希函数,可以参阅上文中的链接。

unsigned long bkdr_hash(const char* str)
{unsigned int seed = 131;unsigned int hash = 0;while (*str){hash = hash*seed+(*str++);}return (hash & 0x7FFFFFFF);
}

Step 2.2 获取文件大小

获取文件大小的主要作用是——其一,使程序更具有鲁棒性,可以适应于任意大小的文件拆分,保证拆分后的小文件不超过指定内存大小。其二,判断拆分后的文件是否满足要求。

unsigned long get_file_size(string file_name)
{if (!file_name.size())return 0;struct stat file_info;return stat(file_name.c_str(),&file_info) ? 0 : file_info.st_size;
}

Step 2.3 获取某一目录下指定后缀的所有文件

这里仿照JAVA写了个endsWith函数,用于过滤后缀。

bool str_ends_with(string s, string sub)
{return s.rfind(sub)==(s.length()-sub.length());
}vector<string> get_folder_file_name_list(string folder, string ends_with)
{struct dirent *ptr = NULL;DIR *dir = opendir(folder.c_str());vector<string> files_name;while ((ptr=readdir(dir))!=NULL){if (ptr->d_name[0] == '.')continue;if (str_ends_with(ptr->d_name, ends_with))files_name.push_back(ptr->d_name);}closedir(dir);return files_name;
}

Step 3 使用set将小文件进行求交操作,最终得到相同URL

bool write_same_url_to_file(string folder, string same_url_file_name)
{vector<string> files_name_a = get_folder_file_name_list(folder, ".a.txt");vector<string> files_name_b = get_folder_file_name_list(folder, ".b.txt");vector<string>::iterator iter;ofstream out(same_url_file_name, ios::app);if (!out.is_open())return false;for (int i = 0; i < files_name_a.size(); ++i){string s = files_name_a[i];s[s.size()-5] = 'b';if (get_file_size(files_name_a[i]) <= PER_FILE_SIZE \&& (iter = find(files_name_b.begin(), files_name_b.end(),s))\!= files_name_b.end()\&& get_file_size(*iter) <= PER_FILE_SIZE){set<string> a_set = get_file_hash_set(folder+files_name_a[i]);set<string> b_set = get_file_hash_set(folder+*iter);set<string> same_url_set = get_same_url_set(a_set, b_set);set<string>::iterator it = same_url_set.begin();for (; it != same_url_set.end(); ++it){out << *it << endl;cout<<*it<<endl;}}}out.close();return true;
}

可运行的程序获取地址:https://github.com/qingdujun/algorithm/tree/master/others/50billion-url

©qingdujun 2018-9-3 于 北京 海淀

References:
[1] https://blog.csdn.net/v_JULY_v/article/details/6685962 ,2018-9-3

海量数据实战(0)从两个文件50亿数据中找出相同的URL相关推荐

  1. 从50亿图文中提取中文跨模态新基准Zero,奇虎360全新预训练框架超越多项SOTA

    ©作者 | 奇虎360人工智能研究院.清华大学 来源 | 机器之心 对于中文社区来说,本文提出的大规模跨模态基准数据集无疑很有价值. 视觉语言预训练(VLP)主要学习视觉与自然语言之间的语义对应关系. ...

  2. 找出一个字符串中出现次数最多的字_海量数据中找出前k大数(topk问题)

    在海量数据中找出出现频率最好的前k个数,或者从海量数据中找出最大的前k个数,这类问题通常被称为top K问题. 针对top K类问题,通常比较好的方案是分治+Trie树/hash+小顶堆(就是上面提到 ...

  3. 从海量数据中找出中位数

    题目:在一个文件中有 10G 个整数,乱序排列,要求找出中位数.内存限制为 2G.只写出思路即可(内存限制为 2G的意思就是,可以使用2G的空间来运行程序,而不考虑这台机器上的其他软件的占用内存). ...

  4. 在数组中找出3个数使得它们和为0

    题目: 给定一个集合S,试找出3个数a, b, c,使得a+b+c=0.也即从集合中找出所有的和为0的3个数. 例如:集合S={-1,0, 1, 2, -1, 4},则满足条件的3个数有2对:(-1, ...

  5. linux误修改文件名恢复,如何在 Linux 中找出最近或今天被修改的文件-linux修改文件名...

    Linux 用户在命令行上遇到的常见问题之一是定位具有特定名称的文件,如果你知道确定的文件名则可能会容易得假设你忘记了白天早些时候创建的文件的名称(在你包含了数百个文件的 home 文件夹中),但现在 ...

  6. 数据结构与算法--有序数组中找出和为s的两个数字

    有序数组中找和为s的两个数字 题目:输入一个递增排序的数组array, 和一个数字s, 在数组中找出两个数,使得这两个数的和是s,如果有多对,输出一对即可. 最简单方案 双循环,每次获取一个数据,和数 ...

  7. php导入qq数据txt代码,/谁有能都实现将excel文件导入到数据中,并在php网页上显示的源码啊,有的发送1091932879@qq.com,谢谢!...

    PHP网页怎么导入Excel的数据 参码如下: // 1.引用ExcelReader类文 require_once 'Excel/reader.php'; // 2.实例化读取Excel类 $data ...

  8. 给定一个整数数组 nums和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那两个整数,并返回它们的数组下标。

    问: /** 给定一个整数数组 nums和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那两个整数,并返回它们的数组下标. 你可以假设每种输入只会对应一个答案.但是,数 ...

  9. 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标

    题目链接:https://leetcode-cn.com/problems/two-sum/solution/liang-shu-zhi-he-by-leetcode-2/ 给定一个整数数组 nums ...

最新文章

  1. p沟道mos管导通条件_10天电子入门-MOS管
  2. 全检体系结构风格浅谈
  3. php怎么爬取亚马逊的数据,使用PHP从Amazon MWS API获取订单数据
  4. linux grep -v反向搜索:不显示目标字符串
  5. oracle重建控制文件流程,ORACLE之重建控制文件
  6. RMAN SET NEWNAME
  7. python分词和词频统计
  8. 又发生频繁FGC,这次是谁的锅
  9. 解决: Spring Boot报错 This application has no explicit mapping ... a fallback
  10. Java教程:Java字符串替换实例
  11. Linux操作系统使用基础06:文件与文件系统的压缩与打包
  12. qt通过http连接mysql_Qt如何利用MySQL连接远程数据库?
  13. python_正则表达式匹配ip
  14. password php,password.php
  15. 误将系统装入D盘 原来数据如何恢复
  16. 485转以太网通讯测试软件,485转TCPIP转换模块 串口转以太网模块
  17. 核信百度空间互踩工具v1.0.0 免费绿色版下载
  18. (转)解析智能推荐系统开发中十大关键要素
  19. 以潘金莲和西门公子为例讲述Java静态代理和动态代理
  20. 德国研究生分数计算机,申请德国研究生看什么成绩

热门文章

  1. UE4 PPT滚动播放材质
  2. 高中电子技术——比较器构成的自锁开关电路
  3. stc c语言编程,谁有STC 的 EEPROM C程序
  4. TCP/IP之大明王朝邮差
  5. IObit Unlocker删除/解锁占用的文件
  6. Android齐步走-2(2016-7-1)
  7. 精读4:一个和钱打交道的数据分析行业
  8. CentOS8 tingzhuxuan 不在 sudoers 文件中。此事将被报告。
  9. C++四则运算字符串解析(附动图以及动图制作方法)
  10. 怎么发表计算机论文,潮州发表计算机论文写作方法,怎么发表论文