詞頻統計


這是我們數據結構課程最后的一次作業,詞頻統計。

其主要要求是這樣的:

編寫程序統計一個英文文本文件中每個單詞的出現次數(詞頻統計),並將統計結果按單詞出現頻率由高至低輸出到指定文件中。

看到這個要求,在給出數據規模與下一步要求時,本來大家肯定會想,統計詞頻嘛。就是套用一下trie樹(字典樹),或者更簡單的,就是直接用數組或鏈表暴力查詢求解嘛。

數據規模也不是特別大,據我們后來的估算,大概不同的單詞數大概在1e5這個級別,總共的單詞數在1e6這個級別。不過坑人的地方在於這樣的一個評分方式:

在所有程序中運行正確且最快的將得滿分,其它程序的得分以最快的程序運行時間為基准,根據其運行時間計算得出。

可謂沒有對比就沒有傷害,當別人的代碼在0.1s跑出結果,而你的代碼通過幾秒甚至幾十秒求解出來答案時,此時的分數就很不樂觀了。

因此這也成為當時我們的各種絞盡心思的優化大作戰,從發現系統測試的bug(3次測試只要有一次正確就算正確了,因此有的人故意只算對一次其它兩次直接退出來換取時間)、到算法層次上的改進、到細枝末節(從數組的大小到每一句的優化編寫等等),到了后期大家都在為了節約一點時間而想出各種花招。

我當時為了提升程序的性能、減少運行時間,也琢磨出了一些小的tip。比如用fread一下讀入整個文件,比如手寫memcpy函數,比如究竟是內聯函數還是宏定義還是都不用,還有一些小的技巧了,不過這些都是非常細微的提升了。最主要的時間上差距還是在選擇了什么樣的算法、什么樣的數據結構上。沒有STL的C語言相對來說更多東西需要我們自己去實現。

在算法的選擇上,經過幾次試驗后,選擇的是字典樹+基數排序的做法,之前曾經試過快排啊、桶排還有鏈表等等,效果都不是特別好。

嘗試過的方法:

  • 內嵌匯編

    嘗試着把一些大量重復操作的語句用匯編代碼來替代,簡單試了幾次,不過就效果來說好像沒有啥用,可能使用方式不是很合理或者也可能是,C編譯器的優化已經足夠快了吧。
    

覺得可行但沒有嘗試過的方法:

  • 多線程

    應該是一定可以提升性能的方法,不過當時沒有實踐。
    
  • Map Reduce

    一種並行化的處理,不過之前都沒聽說過,因此當時也沒有用。
    

最終的代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define find(c) (c>=0) && (c<=25)
int p,tot,n,num[1500000],b[150000][41],first[80010],now[80010],next[80010],cnt[150010];
char x[150010][41],ans[41],s[8000010];
void dfs(int start,int k)
{
    int i,h,j;
    if (num[start]>0)
    {
        ans[k]='\0';
        n++;
        for (j = 0; j <= k; ++j)//strcpy(x[++n],ans);
            x[n][j] = ans[j];
        h = num[start];
        first[h] = (first[h]==0) ? n : first[h];
        next[now[h]] = n;
        now[h] = n;
    }
    for (i = 0; i < 26; ++i)
    if (b[start][i] != 0)
    {
        ans[k] = i+'a';
        dfs(b[start][i],k+1);
    }
}
int main(int argc, const char * argv[])
{
    FILE *in,*out;
    int i,l,j,z;
    in = fopen("article.txt","r");
    out = fopen("wordfreq.txt","w");
    
    l = fread(s,1,7000010,in);
    for (i = 0; i < l; ++i)
        s[i] = tolower(s[i])-'a';
    tot = i = 0;
    while (i < l)
    {
        p = 0 ;
        while (find(s[i]))
        {
                if (!b[p][s[i]])
                    b[p][s[i]] = (++tot);
                p = b[p][s[i]];
                i++;
        }
        num[p]++;
        i++;
    }
    
    n = 0;
    num[0] = 0;
    dfs(0,0);
    
    for (i = 60000; i >= 1; --i)
    for (j = first[i]; j != 0; j = next[j])
            fprintf(out,"%s %d\n",x[j],i);
    z = 0;
    for (i = 60000; i >= 1; --i)
    for (j = first[i]; j != 0 && z < 100; j = next[j])
    {
        printf("%s %d\n",x[j],i);
        z++;
    }
    return 0;
}


這個評測結果原來是實時的,一提交就能看到結果。不過后來這個平台被同學們找出各種減少時間的bug,老師不得不說最后會進行另一次科學合理的統一測試來確定最終成績。而在經過統一測試后,我僥幸獲得了5分的滿分,雖然還是比第一名的同學時間要慢上一些的,不過還是不錯的結果了。

結果截圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM