大数据问题汇总

来源:互联网 发布:原始传奇翅膀进阶数据 编辑:程序博客网 时间:2024/05/16 00:46

1最基本的,一个数据流(文件),求top k biggest. 

solution:维护大小为K的最小堆,和堆顶比,大于堆顶的加入堆,堆顶相当于准入门槛。如果size  超过K,移除堆顶。

vector<int> topK(string fileName, int k) {ifstream is(fileName);priority_queue<int, vector<int>, greater<int>> pq;for (int x; is >> x;) {pq.push(x);if (pq.size() > k) pq.pop();}vector<int> ans;for (; !pq.empty(); pq.pop()) {ans.push_back(pq.top());}reverse(ans.begin(), ans.end());return ans;}


2一般的是Top K frequent,需要先统计每个数据的个数(频率)。主要看全部unique的数据size(乘上计数值size)是否可以装进内存,可以的话,直接symbol table计数,然后再维护key为频率,大小为K的最小堆。

如果全部unique的数据内存装不下,通过值域划分进行Data Partition,一个区间用一个文件存,这实际就是一个shuffle过程。读一遍原数据,生成若干区间文件,然后每个区间可以独立统计频率,进而用堆求TOP k,还是用一个大小为k的小顶堆。

void shuffle(string fileName, int bucketNum) {ifstream is(fileName);for (int x; is >> x;) {ofstream os(fileName + to_string(abs(x % bucketNum)), ios::app);os << x << endl;}}vector<int> topKFrequent(string fileName, int k) {ifstream is(fileName);int bucketNum = 10;shuffle(fileName, bucketNum);typedef pair<int, int> P;priority_queue<P, vector<P>, greater<P>> pq;for (int i = 0; i < bucketNum; ++i) {map<int, int> count;ifstream is(fileName + to_string(i));for (int x; is >> x; ++count[x]);for (auto &entry : count) {pq.push(make_pair(entry.second, entry.first));if (pq.size() > k) pq.pop();}}vector<int> ans;for (; !pq.empty(); pq.pop()) { ans.push_back(pq.top().second); }reverse(ans.begin(), ans.end());return ans;}


3 两个大文件求intersection, 

如果一个文件可以装进内存,则可以生成一个unique 的set, 另一个文件扫描一遍,set中有的就是交集的元素。但一般是放不下的。也是先shuffle,data partition, 值域划分。字符串数据用hash code作为值。相同的值一定在同一个区间,然后在每个值域集合上求交集

void shuffle(string fileName, int bucketNum) {ifstream is(fileName);for (int x; is >> x;) {ofstream os(fileName + to_string(abs(x % bucketNum)), ios::app);os << x << endl;}}set<int> intersect(string file1, string file2) {int bucketNum = 10;shuffle(file1, bucketNum);shuffle(file2, bucketNum);set<int> ans;for (int i = 0; i < bucketNum; ++i) {set<int> s;ifstream is(file1 + to_string(i));for (int x; is >> x;) {s.insert(x);}is = ifstream(file2 + to_string(i));for (int x; is >> x;) {if (s.find(x) != s.end()) ans.insert(x);}}return ans;}



4,大文件,找一个不在文件中的数,

1)位图, int类型的数可以用4G * 1bit = 512M byte大小的位图

2)如果内存连位图都放不下,还是data partition, 值域划分,在每个值域子集上用位图做

int findNotIn(string fileName) {vector<char> bitMap1(1024 * 1024 * 256), bitMap2(1024 * 1024 * 256 + 1);ifstream is(fileName);for (int x; is >> x;) {if (x >= 0)bitMap1[x / 8] |= 1 << (x % 8);else {unsigned int y = -x;bitMap2[y / 8] |= 1 << (y % 8);}}for (int i = 0; i < bitMap1.size(); ++i) {for (int j = 0; j < 8; ++j) {if (((bitMap1[i] >> j) & 1) == 0) return i * 8 + j;}}for (int i = 0; i < bitMap2.size(); ++i) {for (int j = 0; j < 8; ++j) {if (((bitMap1[i] >> j) & 1) == 0) return i * 8 + j;if (i == bitMap2.size() - 1 && j > 0) break;}}}



5大文件中查询所有的只出现一次的数

1)2位的位图,00代表不存在,01出现一次,10出现多次,11不用

2)整个值域的位图放不下的话,进行值域划分,每个值域子集上做。

vector<int> findOccurOnce(string fileName) {vector<char> bitMap(1024 * 1024 * 1024); //2位的位图,00 not exit, 01 once, 10 more than once, 11 not usedifstream is(fileName);for (int x; is >> x;) {int v = (bitMap[x / 4] >> ((x % 4) * 2)) & 3;if (v == 0)bitMap[x / 4] |= 1 << (x % 4) * 2; //00 -> 01else if (v == 1)bitMap[x / 4] ^= 3 << (x % 4) * 2; // 01 -> 10}vector<int> ans;for (int i = 0; i < bitMap.size(); ++i) {for (int j = 0; j < 4; ++j) {if (((bitMap[i] >> j * 2) & 3) == 1) ans.push_back(i * 4 + j);}}return ans;}

6大文件求中位数

也是值域划分(桶划分),遍历一遍文件得到每个partition/bucket里的数的个数,以及总个数n。然后从第一个区间开始累加个数,当加进某个区间总个数超过n/2,则中位数就在这个区间。再扫一遍文件,这次对那个区间的每个数计数,从这个区间的第一个数开始继续累加,使得总数超过n/2的那个数就是中位数。

int medium(string fileName) {ifstream is(fileName);int buckeSize = 1000, N = 0;vector<int> count(INT_MAX / buckeSize);for (int x; is >> x;) {count[x / buckeSize] += 1;++N;}int i = 0, num = 0;for (;; ++i) {if (num + count[i] >= (N + 1) / 2) break;num += count[i];}is = ifstream(fileName);vector<int> count2(buckeSize);for (int x; is >> x;) {if (x / buckeSize == i) count2[x % buckeSize] += 1;}for (int j = 0; j < buckeSize; ++j) {if (num + count2[j] >= (N + 1) / 2) {return i * buckeSize + j;}num += count2[j];}}


7 流维护中位数

维护一个最大堆代表左半部分,一个最小堆代表右半部份。来一个数,比最大堆堆顶小,放入左堆,否则放入右堆,然后确保两堆大小相差不超过1,


8 多台机器求中位数

slave 实现 int partition(int pivot):给定一个轴进行partition, 并返回比轴小的元素个数

master首先询问各台机器上有多少数,得出总共有多少数N,中位数是第N/2个

master 随机从slave 选择一个数作为轴,让调用各台slave机器上的partition,并累加得到比pivot小的元素总数K,如果K == N/2, 则pivot就是所求。如果K < N/2, 则令每台机器舍弃前半部分,否则舍弃后半部分。然后重复这个过程。


9 外排序

1)生成归并段(待归并的段)

输入原文件流,输出一个个有序小文件,小文件大小主要依赖内存大小,可以进行内排序。

2)merge

a)可以2路归并或多路归并,生成一系列更大的一点的归并段,重复这个过程,直到只有一个归并段。这种方法会产生很多中间文件。

b) 也可以进行全路归并,也就是从初始归并段集合直接生成目标文件,使用一个小顶堆的priority_queue做“门”,门的输入是多路流,即所有的归并段,输出端是一个流,即目标文件。每次把堆顶元素写入输出流。这里元素除了包含原始数据,还要包含管道信息,即是从哪个输入流来的,并且从那个流输入一个数。这种方法的问题是,系统是否允许打开那么多文件,这取决于原始归并段数。




0 0
原创粉丝点击