`
Mojarra
  • 浏览: 130783 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

两亿数据的交集

阅读更多

前几天在论坛里看到一个帖子说百度的一道面试题,两个文件里各约有两亿行数据,每行只有一个数字,问如何求两个文件中数据的交集。

 

最近对大数据的处理比较感兴趣,所有思考了一下这个问题,对于JVM来说,两亿数据是非常多的,直接用数组来处理,是行不通的,另外,两亿的数据,效率也是一个重要的考量度。本来可以借助Hash的方法来解决这个问题,但因为每行只有一个数据,也就是只有数字0~9, 那么可以采用一个简便的方法,读取文件中的数字,假如是1, 那么给一个int数组的第1位加1,其他的数字如此类推,假如是2,给这个int数组的第2位加1, 直到所有的数据读取完毕。另外一个文件也采用相同的方法,最后,比较这两个int数组的相同位置上的元素,取当中小的作为交集的个数,比如a[9] = 100, b[9]=80, 那么说明在这两个文件中,有80个数字9是有交集的。最终的交集结果,用这样的统计形式表示,

{0 -- 1000}, 数字0在两个文件中有交集,并且是1000次

{1 -- 999} 

....

{8 -- 2000}

{9 -- 80}

 

基本的思路有了,问题是如何读取两亿个数字呢?即使数据采用分行方式保存,用readLine方法读取,两个文件,总共要读取4亿次,效率肯定是很低的,必须要用buffer,分析buffer,得出数字。从题目本身来说,一行只有一个数字是很好分析buffer的,考量到一定的扩展,本程序假定一行是一个数据,给出一个通用的解析方法。

 

首先定义一个基本的类,很简单,定义数据量,buffer的大小,和数据的最大值,比如10,表示文件里的数据是从0~9, 如果是100, 则是0~99。1000, 则表示0~999,这个最大值也决定了读取数据到目标数组的最大长度。

 

public class Data {
	int counter = 200000000;
	int bs = 0x80000;
	int max = 10;
}

 

DataFinder类,从两个文件输入流中读取数据并且统计到数组,并求交集。因为考虑到每次读取到buffer中的bytes不一定是0x0a结尾,需要考虑这个0x0a在下一次读取的buffer中的情况,需要对上次结尾的一些bytes和本次读取的头几个bytes进行合并。取到一组正确的表示一个数据的bytes后,先转换成String,在转换成integer,根据这个integer来给数组中的一个特定位增加计数器。后来证明,这个思路中,需要花精力去处理的是IO及分析数据部分。

package data.comparison;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class DataFinder extends Data {

	private int[] readData(InputStream is) throws IOException {
		int[] ds = new int[max];
		byte[] buff = new byte[bs];
		int bc = 0;
		byte[] delta = null;
		while ((bc = (is.read(buff))) != -1) {
			int p = 0;
			int i = 0;
			if (delta != null) {
				while (buff[i++] != 0x0a) {
				}
				int len = i - p - 1;
				byte[] bn = new byte[delta.length + len];
				System.arraycopy(delta, 0, bn, 0, delta.length);
				System.arraycopy(buff, 0, bn, delta.length, len);
				p = i;
				int n = Integer.parseInt(new String(bn));
				ds[n]++;
				delta = null;
			}

			while (i < bc & p < bc) {
				if (buff[i] == 0x0a || i == bc - 1) {
					int len = i == bc - 1 ? bc - 1 - p: i - p;
					byte[] bn = new byte[len];
					if (len==0){
						System.out.println();
					}
					System.arraycopy(buff, p, bn, 0, len);
					int n = Integer.parseInt(new String(bn));
					ds[n]++;
					p = i + 1;
				}
				i++;
			}

			if (i == bc && buff[i - 1] != 0x0a) {
				delta = new byte[i - p];
				System.arraycopy(buff, p, delta, 0, i - p);
			}
		}
		return ds;
	}

	public void compare(InputStream in1, InputStream in2) throws IOException {
		long t = System.currentTimeMillis();
		StringBuilder result = new StringBuilder(32);
		int[] ds1 = this.readData(in1);
		int[] ds2 = this.readData(in2);

		for (int i = 0; i < ds1.length & i < ds2.length; i++) {
			result.append(String.format("{number: %d, occur: %d}%n", i,
					ds1[i] < ds2[i] ? ds1[i] : ds2[i]));
		}
		System.out.println(result.toString());
		System.out.println("--- done ---");
		System.out.format("comparison costs %d ms%n", System.currentTimeMillis() - t);
	}

	public static void main(String args[]) throws IOException {
		InputStream in1 = new FileInputStream("file1");
		InputStream in2 = new FileInputStream("file2");
		new DataFinder().compare(in1, in2);
	}

}
 

最后,再写一个随即创造数据的类,

package data.comparison;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random;

public class DataCreator extends Data {

	public void random(String file) throws IOException {
		long t = System.currentTimeMillis();
		FileOutputStream writer = new FileOutputStream(file);
		int c = 0;
		while (c < counter) {
			Random random = new Random();
			StringBuilder builder = new StringBuilder(200000);
			for (int i = 0; i < bs & c < counter; i++, c++) {
				builder.append(random.nextInt(max)).append("\n");
			}
			writer.write(builder.toString().getBytes());
		}
		writer.flush();
		writer.close();

		System.out.format("cost %d ms%n", System.currentTimeMillis() - t);
	}

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		DataCreator dc = new DataCreator();
		dc.random("file1");
		dc.random("file2");
	}
}

 

在I5 2.3G/4G/5400转的硬盘/Mac OS 10.7/JDK6.0.29的软硬件环境上,使用buffer方式后,性能还是比较可以的,随机写两个文件,只需要10秒不到的时间,可以接受。

cost 9313 ms
cost 8884 ms

 求两个文件的交集,共花了63秒

{number: 0, occur: 20001948}
{number: 1, occur: 20000771}
{number: 2, occur: 19996245}
{number: 3, occur: 19996415}
{number: 4, occur: 19997276}
{number: 5, occur: 19992915}
{number: 6, occur: 19997709}
{number: 7, occur: 19997344}
{number: 8, occur: 19995041}
{number: 9, occur: 19998757}

--- done ---
comparison costs 63365 ms

 

这个方法把众多的数据转换成一个较小的数组,采用计数器的方法来求交集,算法上很简练,同时也避免了JVM heap开销过大,在读取数据时,另辟蹊径,采用buffer读文件,再从buffer中分析数据,从而避免了IO的问题。 那么,对于这个面试题,还有没有更好的思路呢?

 

[全文完]

2
0
分享到:
评论

相关推荐

    常用大数据量,海量数据处理方法,算法总结

    1. 给你 A,B 两个文件,各存放 50 亿条 URL,每条 URL 占用 64 字节,内存限制是 4G,让你找出 A,B 文件共同的 URL。 2. 海量日志数据,提取出某日访问百度次数最多的那个 IP。 3. 如何根据输入元素个数 n,确定位数...

    海量数据处理总结(大量数据处理)

    Bloom Filter是一种用于检验元素是否在一个集合中的概率型数据结构,特别适用于大数据环境下进行快速判断元素是否存在的情况,尤其在数据字典构建、数据去重或集合交集计算方面表现出色。其基本原理是利用位数组和多...

    大数据量,海量数据 处理方法总结

    **适用范围**:数据字典、判重、集合求交集等场景。 **优化建议**:根据输入元素个数n,合理设定位数组m的大小及hash函数数量k,以达到最小的错误率。通常情况下,k = (ln2)*(m/n),错误率不大于E时,m ≥ n*lg(1/E...

    大数据量,海量数据_处理方法总结

    **适用范围**:Bloom Filter常用于实现数据字典、数据判重或集合求交集等功能。 **基本原理**: - 使用一个位数组和多个独立的哈希函数来实现。 - 将元素通过哈希函数映射到位数组中的特定位置,并将这些位置标记为1...

    面试中的大数据处理

    Bloom filter是一种常见的大数据处理方法,适用范围包括:实现数据字典、进行数据的判重、或者集合求交集。其基本原理是使用位数组+k个独立的hash函数,将hash函数对应的值的位数组置1,查找时如果发现所有hash函数...

    海量数据处理的方法

    - 给定两个文件A和B,各存放50亿条URL,每条URL占用64字节。在内存限制为4GB的情况下,找出A和B文件中的共同URL。可以通过构建Bloom Filter来降低内存消耗,尽管这可能会导致误报率的增加。 #### 二、Hash **定义*...

    大数据处理方法

    布隆过滤器主要用于实现数据字典、数据去重以及集合求交集等场景。 **基本原理及要点:** - **核心结构:** 位数组 + K个独立哈希函数 - **操作流程:** 将数据通过K个哈希函数映射到位数组中,若数据存在则将其...

    东北证券-计算机行业深度报告-数据安全:快速崛起的数字经济之“盾”-230605.pdf

    在国内市场,DLP(数据丢失防护)和数据库安全是最大的两个子市场,市场规模均超过10亿元。 数据安全需求的增长主要源于数据量的爆炸性增长、多样化的数据使用场景、数据交易需求的增加以及数据互联的需求。预计到...

    十道海量数据处理面试题(卷).doc

    7. 上亿数据统计出现次数最多的 N 个数据: 使用优先队列(最小堆)或者 Top-K 算法,每次新数据出现时更新堆。 8. 判断一个数是否在 40 亿个数中: 类似于第6题,可以构建布隆过滤器,但考虑到误判率要求更低,...

    十道海量数据处理面试题(卷).docx

    5. **找出两个文件的共同url** 利用布隆过滤器(Bloom Filter)对每个文件的url进行哈希处理,先判断是否存在交集,若没有交集则直接返回。若有交集,再采用更精确的方法,如Sort-Intersection或Hash Join,找出...

    Redis中常见的数据结构.pptx

    集合操作如`SADD`用于添加元素,`SMEMBERS`用于查看集合的所有成员,`SDIFF`, `SINTER`, `SUNION`则分别用于计算两个或更多集合的差集、交集和并集。由于集合内部使用哈希表实现,因此添加、删除和查找操作的时间...

    Bitmap大数据查找算法

    这种算法广泛应用于搜索引擎、推荐系统、数据库索引等领域,能够有效地处理十亿级别的数据量。在实际应用中,还需要考虑如何适应动态数据变化、节省存储空间以及并行处理等问题,这往往需要结合其他数据结构和算法来...

    2015年4月HTML5游戏月度数据分析报告.pdf

    - **HTML5游戏与原生游戏用户重叠度**:数据显示,原生游戏与HTML5游戏用户之间的重叠度达到了59%,这表明两者之间存在显著的交集。此外,原生游戏用户与HTML5游戏用户的比例为5:3,显示出HTML5游戏在用户群体中的...

    07-clustering.pdf

    在高维空间中,数据点通常由多个特征(维度)来表示,例如,在一个包含2亿个“天空物体”的目录中,每个物体的辐射特性可以通过7个不同的频率带(维度)来描述。聚类的目标就是将这些物体根据它们的特性分成若干类别...

    qingyan520#The-C-PLus-PLus-Language#哈希+海量数据处理1

    1.给定两个文件1,分别由100亿个query,我们只有1G内存,如何找到两个文件的交集,分别给出精确算法和近似算法 2.如何扩展BloomFilter使得他支

    redis入门级别

    持久化有 RDB(快照)和 AOF(Append Only File)两种方式,确保在服务重启后能够恢复数据。主从复制保证数据的冗余和高可用性,而 Redis 集群则可以实现数据的分布式存储和负载均衡。 在实际应用中,Redis 可以...

    大厂面试系列二.pdf

    将多个集合合并成没有交集的集合,可以通过并查集或哈希表等数据结构实现,使得每个集合中的元素互不相同。 平面内11个点连成48条直线,根据组合数学中的组合公式C(n, 2) = n*(n-1)/2计算得出;而可以连成的三角形...

Global site tag (gtag.js) - Google Analytics