题目
有一个包含20亿个全是32位整数的大文件,在其中找到出现次数最多的数,内存限制为2GB。
解答:
想要在很多整数中找到初夏次数最多的数,通常的做法是用哈希表对出现的每一个数据做词频统计,哈希表的key是某一个整数,value是这个词出现的次数。就本题来说,一共20亿个整数,用32位int型是完全足以表示的,32位int最大数据为2147483647,所以并不会溢出。因为一个哈希表需要的key需要4Bit空间,value也要4Bit空间,所以最大可能需要160亿Bit空间,即16GB空间,远远超过了2GB内存需求,因此需要分割,将大问题转化为小问题。
有一个包含20亿个全是32位整数的大文件,在其中找到出现次数最多的数,内存限制为2GB。
解答:
想要在很多整数中找到初夏次数最多的数,通常的做法是用哈希表对出现的每一个数据做词频统计,哈希表的key是某一个整数,value是这个词出现的次数。就本题来说,一共20亿个整数,用32位int型是完全足以表示的,32位int最大数据为2147483647,所以并不会溢出。因为一个哈希表需要的key需要4Bit空间,value也要4Bit空间,所以最大可能需要160亿Bit空间,即16GB空间,远远超过了2GB内存需求,因此需要分割,将大问题转化为小问题。
解决办法就是使用哈希函数将包含20亿个数的大文件使用哈希函数分成16个小文件,根据哈希函数的性质,同一个数据不可能被分割到不同的文件上,同时每个小文件中不同的数一定不会大于2亿个。当然了,哈希函数要选取合适,否者每个文件中数据的个数差异会很大,假设分割的小文件个数为N,那么将每个数对N求于,将余数为m的数存到m.txt文件里,经过试验,这种做法保证了每个文件里的数据量大致一致。
#include<stdlib.h>
#include<string>
#include<map>
using namespace std;
long long NMAX = 2e7;
const int N = 16;//生成16个小文件
void create(string str)//生成NMAX个数据
{
srand(unsigned(time(0)));
ofstream fout(str);
unsigned int num = 0;
while (NMAX > 0)
{
num = rand() | (rand() << 16);//注意rand()生成的最大值只有0x7fff,因此要生成32位整数,把其左移16位
fout << num << " ";
--NMAX;
}
fout.close();
}
int hash(int key)//哈希函数
{
return key%N;
}
void split(string name)
{
char ch[100];
ofstream f[N];
ifstream fin(name);
int num = 0;
for (int i = 0; i < N; i++)//打开N个文件,供写入数据
{
_itoa_s(i, ch, 10);
string str(ch);
str = str + ".txt";
f[i].open(str);
}
while (fin >> num)
{
int h = ::hash(num);
f[h] << num << " ";
}
fin.close();
for (int i = 0; i < N; i++)f[i].close();
}
void chose(int i, pair<int,int>&p)//挑出第i个文件里的出现次数最多的数
{
char ch[100];
_itoa_s(i, ch, 10);
string str(ch);
str = str + ".txt";
ifstream fin(str);//打开di个文件
map<int, int>m;//key值记录数据,value记录出现的次数
int key;
while (fin >> key)m[key]++;
map<int, int>::iterator iter = m.begin();
for (; iter != m.end(); iter++)
{
if (iter->second>p.second)//如果该数据出现的次数多,则替换
{
p.first = iter->first;
p.second = iter->second;
}
}
fin.close();
}
int main()
{
clock_t start, end;
pair<int, int>p(0, 0);//记录出现次数最多的数,及其频次
string name("test.txt");
create(name);
start = clock();
split(name);
for (int i = 0; i < N; i++)chose(i, p);
end = clock();
cout << p.first << " " << p.second << endl;
cout << "time=" << (end - start)*1000.0 / CLOCKS_PER_SEC << "ms" << endl;
}