编程之法之海量数据处理:寻找Top K的热词

Posted xiaoranone

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编程之法之海量数据处理:寻找Top K的热词相关的知识,希望对你有一定的参考价值。

题目:
有100万个关键字,长度小于50个字节。用有效的算法找出Top 10的热词,要求对内存的占用不超过1 MB。

分析:
这是大家面试中都被问道的问题,可以直接使用map-reducer直接解决这个问题。如果不能使用这个架构,我们手动实现,也是采用的这个思想,只是这个用文件代替节点。
1 0 6 ∗ 50 B = 50 ∗ 1 0 6 B = 50 M B 10^6 * 50B = 50 * 10^6B = 50MB 10650B=50106B=50MB
因为要求最多1MB内存,我们需要划分成50个小文件。划分的时候我们需要保证所有相同的字符串在同一个文件中,这样我们就可以单独统计每一个小文件,最后合并结果即可。当然这个文件大家都已经理解了,这里主要是从C++的代码进行实现。

首先我们明确需要几个函数:

  1. 划分函数,将相同的字符串划分到相同小文件中, 这里我们需要有一个hash函数,将每个字符串映射成整数,再对小文件个数(50)取余。

  2. 统计每个小文件中的热词,这里需要实现一个统计函数,我们可以基于最小堆进行实现。

  3. 合并所有小文件的结果的函数,合并函数

  4. 由于我们没有数据,需要写一个随机函数,生出数据,生出数据的函数

  5. 随机产生数据的函数

/** 随机产生200W个长度为10的字符串,写入文件中,字符[0-5] + 'a'*/
void randCreateData(const char* outputfile, const int cnt = 1000000) 
    FILE *fp = NULL;
    fp = fopen(outputfile, "w+");
    if(fp == NULL) 
        printf("File error\\n");
        return;
    
    srand((unsigned)time(0));
    for(int i = 0; i < cnt; i++) 
        char tmp[12];
        int j;
        for(j = 0; j < 5; j++) 
            tmp[j] = char(rand()%5 + 'a');
        
        tmp[j] = '\\0';
        fprintf(fp, "%s\\n", tmp);
    
    fclose(fp);


  1. hash key函数
/** 得到hash(str) % MOD **/
int hash_key(const char* str) 
    int value = 0;
    while(*str != '\\0') 
        value = value * 31 + *str;
        value = value % MOD;
        str ++;
    
    return value;;

  1. 分割函数
/** 将200w * 5B的文件,根据key划分成50个文件,每个文件100B,大约1M **/
void partation(const char* inputfile, int filecnt = 50) 
    FILE * fps[filecnt];
    for(int i = 0; i < filecnt; i++) 
        char tmpfile[20] = "./data/tmpfile";
        char s[3];
        sprintf(s, "%d", i);
        char *file = strcat(tmpfile, s);
        // printf("%s\\n", file);
        fps[i] = fopen(file, "w+");
        assert(fps[i] != NULL);
    

    FILE *fp_read = fopen(inputfile, "r");
    assert(fp_read != NULL);
    int i = 0;
    char buffer[10];
    while(! feof(fp_read)) 
        // 这样读取包含换行符
        fgets(buffer, sizeof(buffer)-1 ,fp_read);
        // 去掉换行符号
        buffer[strlen(buffer)-1] = '\\0';
        // 得到字符串对应的key,写入对应的文件
        int key = hash_key(buffer) % filecnt;
        fprintf(fps[key], "%s\\n", buffer);
    
    // close 所有文件指针
    for(int i = 0; i < filecnt; i++) 
        fclose(fps[i]);
    
    fclose(fp_read);


  1. 处理一个文件的函数
/** 统计单个文件中的出现的频率 **/
int preocessedOneFile(const char *inputfile, const int topK=100) 
    map<string, int> s2cnt;
    FILE *fp_read = fopen(inputfile, "r");
    assert(fp_read != NULL);
    int i = 0;
    char buffer[10];
    while(! feof(fp_read)) 
        // 这样读取包含换行符
        fgets(buffer, sizeof(buffer)-1 ,fp_read);
        // 去掉换行符号
        buffer[strlen(buffer)-1] = '\\0';
        string s = buffer;
        s2cnt[s] += 1;
    
    // cout<<s2cnt.size()<<endl;
    for(auto it=s2cnt.begin(); it!=s2cnt.end(); it++) 
        q.push(make_pair(it->first, it->second));
        if(q.size() > topK)  // 保存最大的100个
            q.pop();
        
    

    /*
    int k = 0;
    while(q.size()) 
        pair<string, int> t = q.top();
        cout<<t.first<<" "<<t.second<<endl;
        q.pop();
        k ++;
        if(k > 10) break;
    
    */
   return s2cnt.size();



全部源码:github 源码

以上是关于编程之法之海量数据处理:寻找Top K的热词的主要内容,如果未能解决你的问题,请参考以下文章

构建之法之初遇

读构建之法之感

构建之法之阅读笔记02

初学构建之法之体会

读构建之法之感

#构建之法之初体验