首页 > 一个手机号码剔重的问题

一个手机号码剔重的问题

问题

QQ群上有人问了这样一个问题,
现有2个待推广手机号码数据文件,A文件1000W行,B文件是100W,文件中每行记录只有手机号,号码有重复,
请设计高效方案先对A、B数据文件分别进行号码剔重,再找出B文件中在A文件存在的号码,
请写出核心设计思想,并编写代码完整实现。(注:请用纯c实现,不许采用数据库、Memcached等第三方中间件,号码数据文件请自行模拟生成) 
答题要求:请同时提供Word设计文档,和程序源代码(如程序中使用了第三方Jar请注明,jar包请不要上传),打包上传。

思路及分析

我想了一下,看看《编程珠玑》,然后我决定用位图(Bit Map)来解决这个问题。 
有些朋友可能还不了解位图(Bit Map),在这里我先用一个例子来说明。
假设我有一个0到31的集合,集合里面的元素不重复,比如这样{0,3,1,5,2,19,7,8,31,21,10}。通过位图,我可以将这样的集合表示为11110001101000000001010000000001, 其中1表示该数值为下标的数存在在集合中,比如第一个1表示0存在集合中,第二个1表示1存在集合中,等等。通过这样做,我们起码可以得到两个好处
1) 节省空间--我们可以用二进制一个位来存储存储两个信息,一是存不存在,而是存在的数是多少(通过一个bit就可以得到这么多信息,真了不起)。
2) 排序--从左到右遍历这个为图,我们可以得到排序的集合,比如上例中,我们可以得到集合 {0,1,2,3,7,8,10,19,21,31}
如果将所有这电话号码用位图表示,那么需要9999999999个bit (10个9, 考虑到手机号码的第一位都是1)。 9999999999 bit = (9999999999/8) byte = (9999999999 / (8 * 1024)) KB = (9999999999 / 8 *1024*1024) M = 1192M
嗯....,1000万条手机号码装到内存中只用114M左右的内存空间,如果用位图表示,看起来用的内存更多。行不行啊.... 但是考虑到手机号码的前3位都差不多,而且总数最多30个,所有可能考虑把存放1000万条记录的手机号码的大文件拆分一下,这样就位图的位数就降2个数量级了。所以如果我们要在内存中装入手机号码后8位,需要99999999(8个9)个bit,算一下
99999999 bit = (99999999/8) byte = (99999999 / (8 * 1024)) KB = (99999999 / 8 *1024*1024) M = 11.92M
这样好办了,内存够用了。

号码剔重步骤

根据以上的思路,第一步要大文件进行拆分, 以下是Java代码
package art.programming.algorithm;import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;import org.junit.Test;public class BigFileSpliter {private Map fileWriterMap = new HashMap();public void split(File file) throws IOException{
InputStream is;
BufferedReader br;
String line;
is = new FileInputStream(file);
br = new BufferedReader(new InputStreamReader(is));while((line = br.readLine()) != null){
//System.out.println(line);
append(line.substring(0,3)+".txt", line);
}
closeAllWriters();
br.close();
is.close();
}public void append(String fileName, String appendStr) throws IOException{if (!fileWriterMap.containsKey(fileName)){
File file = new File(fileName);
if(!file.exists()){
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fileWriterMap.put(fileName, fw);
}
fileWriterMap.get(fileName).write(appendStr);
fileWriterMap.get(fileName).write('
');
}public void closeAllWriters(){
for(Writer fw : fileWriterMap.values()){
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}public List getAllFileNames(){
Set keys = fileWriterMap.keySet();
List klist = new ArrayList();
for (String key : keys){
klist.add(key);
}Collections.sort(klist);
return klist;
}@Test
public void testSplit() throws IOException{
new BigFileSpliter().split(new File("cellphone.txt"));
}
}

第二步,将文件按照文件名排序,依次读取文件中的电话号码

第三步,将电话号码放到位图中,如果对应的位是1,那么就不要放了(这样就去重了)
第四步,遍历位图,位的值为1的输出到文件中,如果算出的数不足8为10进制的数,前面补0, 比如838,前面补0,变成00000838
以下的Java程序是第二到第四步的实现
package art.programming.algorithm;import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;public class BigFileSort {private final static int RANGE = 32;
private int[] bitMap;private File outputFile;
private File inputFile;
private int bitMapLen = (99999999/RANGE) + RANGE;
FileWriter fw;public BigFileSort(String inputFileName, String outputFileName) throws IOException{
bitMap = new int[bitMapLen];
outputFile = new File(outputFileName);
if (outputFile.exists()){
outputFile.delete();
}
outputFile.createNewFile();
inputFile = new File(inputFileName);
fw = new FileWriter(outputFile);
}public void sortAndOutput() throws IOException{
BigFileSpliter bigFileSpliter = new BigFileSpliter();
bigFileSpliter.split(inputFile);
for (String fileName : bigFileSpliter.getAllFileNames()){
readAndSetBitMap(fileName);
output(fileName.substring(0, 3));
}
//Clean up
fw.close();
}private void readAndSetBitMap(String fileName) throws IOException{
InputStream is;
BufferedReader br;
String line;
is = new FileInputStream(fileName);
br = new BufferedReader(new InputStreamReader(is));
while((line = br.readLine()) != null){
long tmp = Long.parseLong(line.substring(3, 11));
int mod = (int) tmp / RANGE;
int offset = (int) tmp % RANGE;
int bit = bitMap[(int)mod];
if (getBit(bit, offset) != 1){
bit = setBit(bit, offset, 1);
bitMap[(int)mod] = bit;
}else{
System.out.println(fileName.subSequence(0, 3) + toStringRep(tmp) + " duplicates!");
}
}
br.close();
is.close();
}private void output(String prefix) throws IOException{for (int i=0; i< bitMapLen; i++){
for (int offset=0; offset){
if (getBit(bitMap[i], offset) == 1){
long tmp = i * RANGE + offset;
String cellphoneNum = toStringRep(tmp);
fw.write(prefix+cellphoneNum);
fw.write("
");
}
}
}for (int i=0; i< bitMapLen; i++){
bitMap[i] = 0;
}
}private String toStringRep(long tmp){
String cellphoneNum = String.valueOf(tmp);
if (cellphoneNum.length()<8){
StringBuilder sb = new StringBuilder();
for (int j=0; j< 8 - cellphoneNum.length(); j++){
sb.append('0');
}
sb.append(cellphoneNum);
cellphoneNum = sb.toString();
}
return cellphoneNum;
}private int getBit(int signinNum, int bitIndex) {
if( (signinNum & ( 1 << bitIndex)) != 0){
return 1;
}
return 0;
}private int setBit(int num, int bitIndex, int zeroOrOne){
if (zeroOrOne == 1){
num = num | (1 << bitIndex);
}else{
num = num - (1 << bitIndex);
}
return num;
}//整体测试以下
        @Test
public void testSort() throws IOException{
long begin = System.currentTimeMillis();
new BigFileSort("cellphone.txt", "sortedCellphone.txt").sortAndOutput();
System.out.println(System.currentTimeMillis() - begin);
}    }

整个过程,事件花费大约60秒左右

手机号码交集

将A、B文件分别剔重,可以得到电话号码排好序的两个文件。两个文件顺序对比就可以找出交集了。

转载于:https://www.cnblogs.com/cando/p/3201351.html

更多相关:

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...

  • 有一天,我写了一个自信满满的自定义组件myComponent,在多个页面import使用了,结果控制台给我来这个 我特么裤子都脱了,你给我来这个提示是几个意思 仔细一看 The Component 'MyComponentComponent' is declared by more than one NgModule...

  • 创建一个带路由的项目,依次执行下面每行代码 ng n RouingApp --routingcd RouingAppng g c components/firstng g c components/secondng g m components/second --routing    代码拷贝: import {NgModul...

  •       cnpm install vue-quill-editor cnpm install quill-image-drop-module cnpm install quill-image-resize-module 执行上面的命令安装,然后在main.js下面加入 //引入quill-editor编辑器import...

  • 首先要理解Vue项目加载顺序: index.html → main.js → App.vue → nav.json→ routes.js → page1.vue index.html建议加入样式

  • 简单记录平时画图用到的python 便捷小脚本 1. 从单个文件输入 绘制坐标系图 #!/usr/bin/python # coding: utf-8 import matplotlib.pyplot as plt import numpy as np import matplotlib as mpl import sysf...

  • importjava.security.SecureRandom;importjavax.crypto.Cipher;importjavax.crypto.SecretKey;importjavax.crypto.SecretKeyFactory;importjavax.crypto.spec.DESKeySpec;//结果与DES算...

  • 题目:替换空格 请实现一个函数,把字符串 s 中的每个空格替换成"%20"。 输入:s = "We are happy." 输出:"We%20are%20happy." 限制: 0 <= s 的长度 <= 10000 解题: 时间复杂度:O(n) 空间复杂度:O(n) class Solution { public:s...

  • 在C++11标准库中,string.h已经添加了to_string方法,方便从其他类型(如整形)快速转换成字面值。 例如: for (size_t i = 0; i < texArrSize; i++)RTX_Shader.SetInt(string("TexArr[") + to_string(i) + "]", 7 + i);...

  • Ubuntu 14.04安装并升级之后,变成楷体字体非常难看,我昨天搞了一晚上,终于理了个头绪,这里整理一下。 经过网上调研,大家的一致看法是,使用开源字体库文泉驿的微黑字体效果比较理想,甚至效果不输windows平台的雅黑字体。下面我打算微黑来美化Ubuntu 14.04. 1.安装文泉驿微黑字体库 sudo aptitude...

  • 使用string时发现了一些坑。 我们知道stl 容器并不是线程安全的,所以在使用它们的过程中往往需要一些同步机制来保证并发场景下的同步更新。 应该踩的坑还是一个不拉的踩了进去,所以还是记录一下吧。 string作为一个容器,随着我们的append 或者 针对string的+ 操作都会让string内部的数据域动态增加,而动态增加的...