面试官特别喜欢问的一个问题是对大数据的处理。自己在面试之前也看过一些方法,如July大神等人的文章,但是实际面试时感觉答的还是不够好,感觉是因为自己没有真正实现这个问题。下面我就用最简单的方法模拟一个简单的大数据排序问题。
问题描述:有一个大文件(多个也可以)无法一次读入内存,需要对大文件中的内容进行排序(这里只考虑最简单的无符号整型的情况)。
方法概括:一行一行读取大文件,使用哈希将每一行的数据存入到一个小文件中,并且保证每个小文件中的数据是属于某个区间的(其实就是桶排序的过程)。然后依次读取每个小文件(假设小文件是可以一次性读入内存的),对其内容进行排序(针对这个问题,可能基数排序比较好,我使用的是最简单的快排)。最后将每个排序小文件的结果合并即可。
下面是实现的代码:
/**该项目模拟一个大数据排序的解决方案*/
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <random>
#include <algorithm>
using namespace std;
//模拟生成一个大数据文件,文件中是随机数,取值范围是[0, 99999]
class large_data_generater
{
public:
large_data_generater() : filename("large_data.txt") {}
large_data_generater(string name) : filename(name) {}
void generate()
{
ofstream output(filename);
uniform_int_distribution<unsigned> u(0, 99999);
default_random_engine e; //可以生成均匀分布的unsigned类型的数据
cout << filename << endl;
for(int i=0; i<100; ++i)
{
for(int j=0; j<100; ++j)
{
output << u(e) << " "; //生成下一个随机数
}
output << endl; //一个100行,每行100个随机数
}
output.close();
}
private:
string filename;
};
//对大数据进行排序的类
class sort_large_data
{
public:
sort_large_data() : filename("large_data.txt") {}
sort_large_data(string name) : filename(name) {}
void hash() //该方法将大文件分割成小文件,分别进行存储
{
ifstream input(filename); //无序大文件
if(!input)
{
cerr << "Can not read file!" << endl;
exit(1);
}
ofstream output[10];
for(int i=0; i<10; ++i) //保存哈希结果的小文件
{
int p = filename.find('.');
string small_file = filename.substr(0, p) + char(i + '0') + ".txt";
output[i].open(small_file, ios::app);
}
string line;
while(getline(input, line))
{
istringstream iss(line);
unsigned int ui;
while(iss >> ui)
{
auto num = ui / 10000; //使用最简单的哈希方法,可以将大文件分成10个小文件,并且每个文件中的数据属于某个区间
output[num] << ui << " ";
}
}
input.close();
for(int i=0; i<10; ++i)
output[i].close();
}
void sort() //该方法读取每个小文件,进行排序后写到一个大文件中
{
int p = filename.find('.');
string sortfile = filename.substr(0, p) + "_sort.txt"; //保存排序结果的文件
ofstream output(sortfile);
ifstream input[10];
for(int i=0; i<10; ++i) //打开小文件
{
int p = filename.find('.');
string small_file = filename.substr(0, p) + char(i + '0') + ".txt";
input[i].open(small_file);
if(!input[i])
{
cerr << "Can not read file!" << endl;
exit(2);
}
}
for(int i=0; i<10; ++i) //将每个小文件读入内存,排序,存储
{
string line;
vector<unsigned int> nums;
while(getline(input[i], line))
{
istringstream iss(line);
unsigned int ui;
while(iss >> ui)
nums.push_back(ui);
}
std::sort(nums.begin(), nums.end()); //使用标准库函数进行排序
for(auto n : nums)
output << n << " ";
output << endl;
}
output.close();
for(int i=0; i<10; ++i)
input[i].close();
}
private:
string filename;
};
int main()
{
/**1:生成大数据,当然这里只是模拟大数据,假设large_data.txt很大,无法一次读入内存,并且需要对其进行排序。*/
large_data_generater ldg;
ldg.generate();
/**2:通过hash的方法对大文件进行分割,并且保证hash到每个小文件的数据属于同一个区间,这里也只是进行模拟。*/
sort_large_data sld;
sld.hash();
/**3:对分割好的小文件进行排序,每个小文件是可以读到内存的。*/
sld.sort();
return 0;
}
这个应该是对大数据进行排序最naive的方法了,之后争取把bitmap、外排序的一些方法都尝试一下!