给定a、b两个文件,各存放50亿个url,每个url各占64字节
给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?
两种方法:
一、采用Bloom filter,假设布隆过滤器的错误率为0.01,则位数组大小m约为输入元素个数n的13倍,此时需要的哈希函数k约为8个。
元素个数:n = 5G
位数组大小:m = 5G * 13 = 65G = 650亿 即需要650亿个bit位才能达到错误率0.01
而我们拥有的内存可容纳bit位个数:4G * 8bit = 32G bit = 320亿,按此实现错误率大于0.01。
布隆过滤器:https://blog.****.net/qq_41946557/article/details/102593912
二、采用分治的思想
假如每个url大小为10bytes,那么可以估计每个文件的大小为50G×64=320G,远远大于内存限制的4G,所以不可能将其完全加载到内存中处理,可以采用分治的思想来解决。
Step1:遍历文件a,对每个url求取hash(url)%1000,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,...,a999,每个小文件约300M);
Step2:遍历文件b,采取和a相同的方式将url分别存储到1000个小文件(记为b0,b1,...,b999);
巧妙之处:这样处理后,所有可能相同的url都被保存在对应的小文件(a0vsb0,a1vsb1,...,a999vsb999)中,不对应的小文件不可能有相同的url。然后我们只要求出这个1000对小文件中相同的url即可。
Step3:求每对小文件ai和bi中相同的url时,可以把ai的url存储到hash_set/hash_map中。然后遍历bi的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
【补充】
1、为什么使用hash?是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
也就是说将url通过hash函数变成一个固定长度的值,(url有的挺长的!)然后假如有一部分数值%1000后,等于0,那么放入到a0中,等于1的放入到a1中,以此类推。。。
【注】:
-
1、Hash取模是一种等价映射,不会存在同一个元素分散到不同小文件中的情况,即这里采用的是mod1000算法,那么相同的url在hash取模后,只可能落在同一个文件中,不可能被分散的。因为如果两个url相等,那么经过Hash(url)之后的哈希值是相同的,将此哈希值取模(如模1000),必定仍然相等。
-
2、那到底什么是hash映射呢?简单来说,就是为了便于计算机在有限的内存中处理big数据,从而通过一种映射散列的方式让数据均匀分布在对应的内存位置(如大数据通过取余的方式映射成小树存放在内存中,或大文件映射成多个小文件),而这个映射散列方式便是我们通常所说的hash函数,设计的好的hash函数能让数据均匀分布而减少冲突。尽管数据映射到了另外一些不同的位置,但数据还是原来的数据,只是代替和表示这些原始数据的形式发生了变化而已。
mini版代码实现:https://blog.****.net/qq_41946557/article/details/102711573
根据https://blog.****.net/qq_41946557/article/details/102708186思想去写!!!
将文件中的信息当成我们文本中的每行文件,然后两个文件中分别是100条信息,然后进行读取 hash(url) %5
首先准备两个文件:xxoo.txt ooxx.txt
取模并且存储到5个小文件中。代码清晰易懂。适合初学。
-
package com.henu;
-
import java.io.*;
-
/**
-
* @author George
-
* @description 大文件拆分小文件
-
* 将文件中的信息当成我们文本中的每行文件,然后两个文件中分别是100条信息,然后进行读取 hash(url) %5
-
**/
-
public class HashClass {
-
public static void main(String[] args) throws IOException {
-
//读取第一个文件 ooxx
-
BufferedReader obr = new BufferedReader(new FileReader("d://ooxx.txt"));
-
BufferedWriter obw1 = new BufferedWriter(new FileWriter("d://ooxx1.txt",true));
-
BufferedWriter obw2 = new BufferedWriter(new FileWriter("d://ooxx2.txt",true));
-
BufferedWriter obw3 = new BufferedWriter(new FileWriter("d://ooxx3.txt",true));
-
BufferedWriter obw4 = new BufferedWriter(new FileWriter("d://ooxx4.txt",true));
-
BufferedWriter obw5 = new BufferedWriter(new FileWriter("d://ooxx5.txt",true));
-
String oline = "";
-
while((oline = obr.readLine()) != null){
-
// System.out.println(toHash(line));
-
// System.out.println(oline);
-
int x = toHash(oline);
-
System.out.println(x);
-
if (x == 0){
-
obw1.write(oline);
-
obw1.write("\r\n");
-
}else if(x == -1){
-
obw2.write(oline);
-
obw2.write("\r\n");
-
}else if(x == -2){
-
obw3.write(oline);
-
obw3.write("\r\n");
-
}else if(x == -3){
-
obw4.write(oline);
-
obw4.write("\r\n");
-
}else{
-
obw5.write(oline);
-
obw5.write("\r\n");
-
}
-
}
-
obw1.close();
-
obw2.close();
-
obw3.close();
-
obw4.close();
-
obw5.close();
-
obr.close();
-
//读取第二个文件 xxoo
-
BufferedReader xbr = new BufferedReader(new FileReader("d://xxoo.txt"));
-
BufferedWriter xbr1 = new BufferedWriter(new FileWriter("d://xxoo1.txt",true));
-
BufferedWriter xbr2 = new BufferedWriter(new FileWriter("d://xxoo2.txt",true));
-
BufferedWriter xbr3 = new BufferedWriter(new FileWriter("d://xxoo3.txt",true));
-
BufferedWriter xbr4 = new BufferedWriter(new FileWriter("d://xxoo4.txt",true));
-
BufferedWriter xbr5 = new BufferedWriter(new FileWriter("d://xxoo5.txt",true));
-
String xline = "";
-
while((xline = xbr.readLine()) != null){
-
// System.out.println(toHash(xline));
-
int x = toHash(xline);
-
if (x == 0){
-
xbr1.write(xline);
-
xbr1.write("\r\n");
-
}else if(x == -1){
-
xbr2.write(xline);
-
xbr2.write("\r\n");
-
}else if(x == -2){
-
xbr3.write(xline);
-
xbr3.write("\r\n");
-
}else if(x == -3){
-
xbr4.write(xline);
-
xbr4.write("\r\n");
-
}else{
-
xbr5.write(xline);
-
xbr5.write("\r\n");
-
}
-
}
-
xbr1.close();
-
xbr2.close();
-
xbr3.close();
-
xbr4.close();
-
xbr5.close();
-
xbr.close();
-
}
-
// 将字符串转成hash值
-
public static int toHash(String key) {
-
int arraySize = 5; // 数组大小一般取质数
-
int hashCode = 0;
-
for (int i = 0; i < key.length(); i++) { // 从字符串的左边开始计算
-
int letterValue = key.charAt(i) - 96;// 将获取到的字符串转换成数字,比如a的码值是97,则97-96=1
-
// 就代表a的值,同理b=2;
-
hashCode = ((hashCode << 5) + letterValue) % arraySize;// 防止编码溢出,对每步结果都进行取模运算
-
}
-
return hashCode;
-
}
-
}
* 进行比较,将ooxx1 - xxoo1
* ooxx2 - xxoo2
* ooxx3 - xxoo3
* * ooxx4 - xxoo4
* ooxx5 - xxoo5
方法:将ooxx的对应文件放入set集合中。然后读取xxoo的文件,如果包含,则输出!!!!
-
package com.henu;
-
import java.io.BufferedReader;
-
import java.io.FileReader;
-
import java.io.IOException;
-
import java.util.HashSet;
-
/**
-
* @author George
-
* @description 进行比较
-
**/
-
public class HashClassEnd {
-
public static void main(String[] args) throws IOException {
-
BufferedReader br = null;
-
HashSet<String> set = new HashSet<String>();
-
String line = "";
-
for (int j = 1; j < 6; j++) {
-
String oPath = "d://ooxx" + j + ".txt";
-
String xPath = "d://xxoo" + j + ".txt";
-
br = new BufferedReader(new FileReader(oPath));
-
while ((line = br.readLine()) != null){
-
set.add(line);
-
}
-
br = new BufferedReader(new FileReader(xPath));
-
while((line = br.readLine()) != null){
-
if (set.contains(line)){
-
System.out.println(line);
-
}
-
}
-
}
-
br.close();
-
}
-
}
输出结果:
"220764-7013" "2014/9/22 13:25:00.000" "0" "0" ""
"220764-7267" "2014/9/22 10:45:00.000" "0" "0" ""
"220764-7266" "2014/9/22 11:45:00.000" "2" "0" ""