求两列数据交集的补集
一同学给我发来一段代码,是求出两列数据的交集的补集,要我看看有没有更高效的算法。
这两列数据(电话号码)是放在EXCEL 里的。于是在网上找了下,看看有没有EXCEL 里的公式
结果整出来个这个:
=INDEX($A:$A,SMALL(IF(COUNTIF($B$1:$B$40,$A$1:$A$40)=0,ROW($A$1:$A$40),65536),ROW(A1)))&""
从($B$1:$B$40 ,$A$1:$A$40)可以看出,这里只是查A,B 列的40行,要是想查更多的,自己改下就是了。
但是这个方法在数据量大的时候,就相当还靠谱了。
于是就写了如下的程序
/*
程序功能: 比较两列数据的差别,找出不同的数据项
*/
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
void readFile(char* fName,vector<unsigned>& colume)
{
unsigned l = 0;
ifstream file(fName);
while( !file.eof() )
{
file >> l;
colume.push_back(l);
}
sort(colume.begin(),colume.end());
vector<unsigned> ::iterator end_unique = unique(colume.begin(),colume.end());
colume.erase(end_unique,colume.end());
file.close();
}
void writeFile( char* fName, vector<unsigned>& colume )
{
ofstream file( fName );
for(int i = 0 ; i < colume.size(); i++)
{
// cout << colume[i] << endl;
file << colume[i] << endl;
}
file.close();
}
void compare( vector<unsigned>& col_1, vector<unsigned>& col_2 )
{
bool match;
for( int i = 0; i < col_1.size(); i++)
{
match = 0; // 匹配元素
for( int j = 0 ; j<col_2.size(); j++)
{
if( col_1[i] == col_2[j])
{
match = true;
break;
}
}
if( !match )
{
cout << col_1[i] << endl;
}
}
}
void compare2 (vector<unsigned>& col_1,vector<unsigned>& col_2 )
{
vector<unsigned>::iterator iter_i = col_1.begin();
vector<unsigned>::iterator iter_j = col_2.begin();
for(int i=0,j=0; i<col_1.size() && j< col_2.size(); )
{
if( col_1[i] == col_2[j] )
{
iter_i = find(col_1.begin(),col_1.end(),col_1[i]);
iter_j = find(col_2.begin(),col_2.end(),col_2[j]);
col_1.erase(iter_i);
col_2.erase(iter_j);
continue;
}
else if ( col_1[i] > col_2[j] )
{
j++;
continue;
}
else if( col_1[i] < col_2[j])
{
i++;
continue;
}
}
}
int main()
{
vector<unsigned> colume_1, colume_2;
readFile("1.txt", colume_1);
readFile("2.txt", colume_2);
compare2( colume_1, colume_2);
writeFile("3.txt",colume_1);
writeFile("4.txt",colume_2);
return 0;
}
其中compare 函数是用的两个for循环。而compare2 是改进过了的是用的摆动数组的思想。
写了一个生成随机手机号的函数,一会用来测试两个compare 方法的效率。
//Generate the Cell phone number
//first parameter : amount of the cell number your want to created.
//second parameter : writh the data to the file your specify
void numberGenerator(unsigned amount,char* fName)
{
srand( (unsigned)time(NULL));
ofstream file( fName );
for(unsigned i= 0;i< amount;i++)
{
file <<"13";
for(int j = 0;j<9;j++)
{
file<<rand()%10;
}
file << endl;
}
file.close();
}
这里面就碰到了一个问题,那就是当我们生成的这个手机号是11位的,在用readfile 里把它读入到vector 时,没有达到我们想像中的结果
这时,存在vector 里的全都是0。 但是,我们要是用的9位数的时候,确是正常的。可能是在 file >> l; 这条语句里有些字符的转换。
下面把改过之后的CODE 贴出来先
/*
程序功能: 比较两列数据的差别,找出不同的数据项
*/
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <string>
using namespace std;
void readFile(char* fName,vector<string>& colume)
{
string l = "";
int count = 0;
ifstream file(fName);
while( !file.eof() )
{
file >> l;
colume.push_back(l);
//count ++;
}
//cout << " count in readfile is : " << count <<endl;
sort(colume.begin(),colume.end());
vector<string> ::iterator end_unique = unique(colume.begin(),colume.end());
colume.erase(end_unique,colume.end());
file.close();
}
void writeFile( char* fName, vector<string>& colume )
{
ofstream file( fName );
for(int i = 0 ; i < colume.size(); i++)
{
// cout << colume[i] << endl;
file << colume[i] << endl;
}
file.close();
}
void compare( vector<string>& col_1, vector<string>& col_2 ,vector<string>& col_3 )
{
bool match;
for( int i = 0; i < col_1.size(); i++)
{
match = 0; // 匹配元素
for( int j = 0 ; j<col_2.size(); j++)
{
if( col_1[i] == col_2[j])
{
match = true;
break;
}
}
if( !match )
{
//cout << col_1[i] << endl;
col_3.push_back(col_1[i]);
}
}
}
void compare2 (vector<string>& col_1,vector<string>& col_2)
{
vector<string>::iterator iter_i = col_1.begin();
vector<string>::iterator iter_j = col_2.begin();
for(int i=0,j=0; i<col_1.size() && j< col_2.size(); )
{
if( col_1[i] == col_2[j] )
{
iter_i = find(col_1.begin(),col_1.end(),col_1[i]);
iter_j = find(col_2.begin(),col_2.end(),col_2[j]);
if(iter_i != col_1.end() && iter_j != col_2.end())
{
col_1.erase(iter_i);
col_2.erase(iter_j);
}
continue;
}
else if ( col_1[i] > col_2[j] )
{
j++;
continue;
}
else if( col_1[i] < col_2[j])
{
i++;
continue;
}
}
}
//Generate the Cell phone number
//first parameter : amount of the cell number your want to created.
//second parameter : writh the data to the file your specify
void numberGenerator(unsigned amount,char* fName)
{
srand( (unsigned)time(NULL));
ofstream file( fName );
for(unsigned i= 0;i< amount;i++)
{
file <<"13";
for(int j = 0;j<9;j++)
{
file<<rand()%10;
}
file << endl;
}
file.close();
}
int main()
{
vector<string> colume_1, colume_2,colume_3,colume_4,colume_5;
numberGenerator(10000,"1.txt");
numberGenerator(10000,"2.txt");
readFile("1.txt", colume_1);
readFile("2.txt", colume_2);
DWORD dwStartTick1 = GetTickCount();
compare2( colume_1, colume_2);
DWORD dwTimeElapsed1 = ((DWORD)GetTickCount() - dwStartTick1);
writeFile("3.txt",colume_1);
writeFile("4.txt",colume_2);
readFile("1.txt", colume_4);
readFile("2.txt", colume_5);
DWORD dwStartTick2 = GetTickCount();
compare(colume_4,colume_5, colume_3);
DWORD dwTimeElapsed2 = ((DWORD)GetTickCount() - dwStartTick2)/1000;
writeFile("5.txt",colume_3);
cout << " compare2 algorithm elapsed time " << dwTimeElapsed1 << " millisecond ."<<endl;
cout << " compare1 algorithm elapsed time " << dwTimeElapsed2 << " second ." << endl;
return 0;
}
接下来想把那个compare2 方法给改写一下,因为之前我们在compare2 里有把数据从vector 里删掉,但是这样是没什么意义的
其实只要把不同的数据写入另一个vector 就可以了。
但是,就是这样做的时候,发现了一个问题: 当往vector<string> 放在元素个数大于9996的时候,会出一个
Debug assertion failed. Expression: Vector subscript out of range
void compare3 (vector<string>& col_1,vector<string>& col_2,vector<string>& col_3)
{
int count =0 ;
for(int i=0,j=0; i<col_1.size() && j< col_2.size(); )
{
if(count > 9996 )
{
cout <<"the vector capacity is : "<< col_3.capacity();
}
if( col_1[i] == col_2[j] )
{
//iter_i = find(col_1.begin(),col_1.end(),col_1[i]);
//iter_j = find(col_2.begin(),col_2.end(),col_2[j]);
/*if(iter_i != col_1.end() && iter_j != col_2.end())
{
col_1.erase(iter_i);
col_2.erase(iter_j);
}*/
i++;
j++;
continue;
}
else if ( col_1[i] > col_2[j] )
{
j++;
count ++;
col_3.push_back(col_2[j]);
cout <<" current account is : "<< count << "\t" << col_2[j]<< endl;
continue;
}
else if( col_1[i] < col_2[j])
{
i++;
count ++;
col_3.push_back(col_1[i]);
cout <<" current account is : "<< count <<"\t" << col_1[i]<< endl;
continue;
}
}
}
上面的问题是下标访问越界,很常见的错误,刚才居然没有发现,小小的BS自己一下下.
把 for(int i=0,j=0; i<col_1.size() && j< col_2.size(); ) 改成 for(int i=0,j=0; i<col_1.size()-1 && j< col_2.size()-1 ; )就可以了。
改过后的函数给贴出来
void compare3 (vector<string>& col_1,vector<string>& col_2,vector<string>& col_3)
{
for(int i=0,j=0; i<col_1.size()-1 && j< col_2.size()-1; )
{
if( col_1[i] == col_2[j] )
{
i++;
j++;
continue;
}
else if ( col_1[i] > col_2[j] )
{
j++;
col_3.push_back(col_2[j]);
continue;
}
else if( col_1[i] < col_2[j])
{
i++;
col_3.push_back(col_1[i]);
continue;
}
}
}
那我们来比较一下这几个不同的方法
compare1 为用两个for 来做遍历的。compare2 为把相同的号码从自己的vector里删掉的。compare3为把不同的号码从vector 里拿出来考到一个新的vector 里的
测试数据
a) 用一个随机生成手机号的方法 13********* ,11位的手机号。 各生成的5000 条放在1.txt 和 2.txt 。 所以总共有10000 条数据,这两个文件里只有10个手机号是相同的。(极端情况下)
b) 用一个随机生成手机号的方法 13********* ,11位的手机号。 各生成的5000 条放在1.txt 和 2.txt 。 所以总共有10000 条数据,这两个文件里只有10个手机号是不相同的。(极端情况下)
测试结果
在测试数据a 的情况下 compare1 用时 1286063 毫秒,compare2 用时 297 毫秒, compare3 用时641毫秒。
在测试数据b 的情况下 compare1 用时 641625 毫秒,compare2 用时 1333657 毫秒, compare3 用时31毫秒。