文本比较算法再实现-3

周末在家把思路理了一边,先是用python实现了一下,但性能不太理想(100k/s),考虑到可能是由于动态语言的效率本身比较慢的原因,于是 将算法改成c语言实现,最终的结果是:1.8M/s(硬件环境:Intel Core Duo 1.73G, 内存2G)。对于这个结果来说,我还是不太满意,比较现在动辄都是上G的数据。这样的效率太慢了,下面放上代码,各位讨论下是否还有优化的余地或者这个算 法本身比较慢,或者这个方案是不可行的?

以下代码在Ubuntu9.04下编译并运行通过,测试数据是从je上随便搞了几篇文章。 gcc版本:4.3.3

 

 

C代码
  1. #include <stdio.h>   
  2.   
  3. #include <string.h>   
  4.   
  5. #include <sys/types.h>      
  6.   
  7. #include <dirent.h>      
  8.   
  9. #include <sys/stat.h>    
  10. #include <time.h>    
  11. #include <stdlib.h>    
  12.   
  13. #define STEP 10   
  14.   
  15. int  count = 0; //文档个数   
  16. char * str = NULL; //一个大的字符串,存储所有文档的内容   
  17. int * ends; //文档的结束点集合   
  18. int  ends_len = 0, ends_mem_len = 10; //文档结束点的内存参数(当前长度,内存长度)   
  19. int  str_len = 0, str_mem_len = 10, str_unicode_len=0; //字符串的内存参数(字符串长度,字符串内存长度, 字符串unicode长度:即一个汉字占一个长度时的长度)   
  20. struct  id_map{ //一个文档在内存中的映射位置   
  21.     int  id; //文档id   
  22.     int  start; //字符串中的开始位置   
  23.     int  end; //字符串中的结束位置   
  24. };  
  25. struct  id_map * idmaps=NULL; //文档在内存中的映射地址   
  26. int  idmaps_len = 0, idmaps_mem_len=0; //文档映射参数   
  27. //添加一个文档映射参数   
  28. void  addIdMap( struct  id_map map){  
  29.     if (idmaps==NULL){ //如果数组还没有建立,就建立一个数组来进行存储   
  30.         idmaps = (struct  id_map *)malloc( sizeof ( struct  id_map)*10);  
  31.     }  
  32.     //如果当前的文档数已经到达了上一次建立的内存长度,则扩展内存,步长为10   
  33.     if (idmaps_len==idmaps_mem_len){  
  34.         idmaps_mem_len += STEP;  
  35.         idmaps = (struct  id_map *)realloc(idmaps,  sizeof ( struct  id_map)*idmaps_mem_len);  
  36.         if (idmaps==NULL){  
  37.             printf("内存不足" );  
  38.             return ;  
  39.         }  
  40.     }  
  41.     *(idmaps+idmaps_len) = map;  
  42.     idmaps_len++;  
  43. }  
  44.   
  45. //读取一个文本文件   
  46. char * readTextFile( char * path){  
  47.     char  ch; //当前的字符   
  48.     FILE  *fp; //文件指针   
  49.     int  result;  
  50.     fp = fopen(path, "rb" );  
  51.     if (fp!=NULL){ //如果文档读取成功   
  52.         if (str==NULL){  
  53.             //初始化str,ends的内存。这两个的增长步长均为10   
  54.             ends = (int  *)malloc(  sizeof ( int ) * 10);  
  55.             str = (char  *)malloc(10);  
  56.         }  
  57.         if (!str){  
  58.             printf("内存不足" );  
  59.             fclose(fp);  
  60.             return  NULL;  
  61.         }  
  62.         int  unicode_ = 0;  
  63.         while ((ch=fgetc(fp))!=EOF){ //读取文件,一直读到最后,将内容放到str中。   
  64.             if (str_len == str_mem_len){  
  65.                 str_mem_len += STEP;  
  66.                 str = (char  *)realloc(str, str_mem_len);  
  67.                 if (str == NULL){  
  68.                     printf("内存不足" );  
  69.                     fclose(fp);  
  70.                     return  NULL;  
  71.                 }  
  72.             }  
  73.             if (unicode_ == 0){ //如果上一个字符不是Unicode字符,则判断如果当前字符为unicode字符,则进入unicode计数。   
  74.                 if (ch>=0 && ch<127){  
  75.                     str_unicode_len++;  
  76.                 }else {  
  77.                     unicode_ = 1;  
  78.                 }  
  79.             }else   if (unicode_ == 1){  
  80.                 unicode_ =2;  
  81.             }else   if (unicode_ == 2){ //按照utf-8编码进行计算,每个汉字占三个字符。   
  82.                 unicode_ = 0;  
  83.                 str_unicode_len++;  
  84.             }  
  85.             *(str+str_len)=ch;  
  86.             str_len++;  
  87.         }  
  88.         //记录结束点   
  89.         if (ends_len == ends_mem_len){  
  90.             ends_mem_len += STEP;  
  91.             ends = (int  *)realloc(ends,   sizeof ( int ) * ends_mem_len);  
  92.             if (ends == NULL){  
  93.                 printf("内存不足" );  
  94.                 fclose(fp);  
  95.                 return  NULL;  
  96.             }  
  97.         }  
  98.         //printf("---%d,%d,%d/n", ends_len,ends_mem_len,str_unicode_len);          
  99.         //*(ends+ends_len) = str_unicode_len;   
  100.         *(ends+ends_len) = str_unicode_len;  
  101.         ends_len++;  
  102.         str = (char  *)realloc(str, str_len);  
  103.         //*(str+len)='/0';   
  104.         fclose(fp);  
  105.         return  str;  
  106.     }  
  107.     return  NULL;  
  108. }  
  109.   
  110. //读入一个文件夹内的所有文件   
  111. int  init_search_dir( char  *path)  
  112.   
  113. {  
  114.   
  115.     DIR *dir;     
  116.   
  117.         struct  dirent *s_dir;     
  118.   
  119.         struct   stat file_stat;   
  120.   
  121.     char  currfile[1024]={0};     
  122.   
  123.     int  len = strlen(path);  
  124.   
  125.     printf("%s/n" ,path);  
  126.   
  127.     if ( (dir=opendir(path)) == NULL)  
  128.   
  129.     {     
  130.   
  131.         printf("opendir(path) error./n" );     
  132.   
  133.         return  -1;     
  134.   
  135.     }  
  136.   
  137.         while ((s_dir=readdir(dir))!=NULL)     
  138.   
  139.     {     
  140.   
  141.             if ((strcmp(s_dir->d_name, "." )==0)||(strcmp(s_dir->d_name, ".." )==0))     
  142.   
  143.             continue ;  
  144.   
  145.             sprintf(currfile,"%s%s" ,path,s_dir->d_name);     
  146.   
  147.             stat(currfile,&file_stat);     
  148.   
  149.             if (S_ISDIR(file_stat.st_mode)){ //如果是文件夹,则递归读取   
  150.   
  151.                     init_search_dir(currfile);     
  152.   
  153.             }else {  
  154.   
  155.                     printf("%-32s/tOK" ,currfile);  
  156.             //设置一个文档与 str的映射,并读取文档的内容   
  157.             struct  id_map map;  
  158.             map.id=atoi(s_dir->d_name);  
  159.             map.start = str_unicode_len;  
  160.             readTextFile(currfile);  
  161.             map.end = str_unicode_len;  
  162.             addIdMap(map);  
  163.             printf("/t%d/n" , str_unicode_len);  
  164.         }  
  165.   
  166.         count++;  
  167.   
  168.         }     
  169.   
  170.         closedir(dir);  
  171.     ends = (int  *)realloc(ends,  sizeof ( int ) * ends_len);  
  172.   
  173.     return  0;  
  174.   
  175. }  
  176.   
  177. //计算一个utf-8字符串的长度(汉字占一个长度)   
  178. int  utf8_str_len( char * utf8_str){  
  179.     int  length = 0, unicode_ = 0, i=0;  
  180.     for (;i<strlen(utf8_str);i++){  
  181.         if (unicode_ == 0){  
  182.             if (utf8_str[i]>=0 && utf8_str[i]<127){  
  183.                 length++;  
  184.             }else {  
  185.                 unicode_ = 1;  
  186.             }  
  187.         }else   if (unicode_ == 1){  
  188.             unicode_ =2;  
  189.         }else   if (unicode_ == 2){  
  190.             unicode_ = 0;  
  191.             length++;  
  192.         }  
  193.     }  
  194.     return  length;  
  195. }  
  196.   
  197. //查找该结束点是否存在(2分查找)   
  198. int  find_ends( int  num){  
  199.     if (num>ends[ends_len-1]||num<ends[0]){  
  200.         return  -1;  
  201.     }  
  202.     int  end = ends_len;  
  203.     int  start = 0;  
  204.     int  index=ends_len / 2;  
  205.     while (1){  
  206.         if (ends[index]==num){  
  207.             return  index;  
  208.         }  
  209.         if (start == end || index == start || index == end){  
  210.             return  -1;  
  211.         }  
  212.         if (ends[index] > num){  
  213.             end  = index;  
  214.         }else {  
  215.             start = index;  
  216.         }  
  217.         index = start + ((end-start) / 2);  
  218.     }  
  219. }  
  220.   
  221. //主要函数。搜索所有文档中所有存在于该字符串相似的文档,算法出处及JAVA实现参见:http://www.blogjava.net/phyeas/archive/2009/02/15/254743.html   
  222. void  search( char * key){  
  223.     int  key_len = utf8_str_len(key); //计算key的长度   
  224.     int  i=0, j=0, j_ = 0, i_ = 0;  
  225.     //char barr[key_len][str_unicode_len];   
  226.     char * barr[key_len]; //   
  227.     //char narr[key_len][str_unicode_len];   
  228.     char * narr[key_len];  
  229.     //char darr[key_len][str_unicode_len];   
  230.     char * darr[key_len];  
  231.     //一个按照最大匹配度排序的文档序列。最大匹配度不可能大于key的长度+1,所以声明一个key_len+1长度的数组进行保存即可。数据格式类似:[[],[2,3],[5],[]]   
  232.     int * max_id_maps[key_len + 1]; //该数组的第n个下标表示最大匹配度为n的文档有哪些   
  233.     int  max_id_maps_lens[key_len + 1], max_id_maps_mem_lens[key_len + 1];  
  234.     int  key_ascii_len = strlen(key);  
  235.       
  236.     struct  timeval tpstart,tpend;  
  237.     float   timeuse;   
  238.     gettimeofday(&tpstart,NULL);  
  239.     //初始化三个数组。i_,j_表示当前的坐标,i,j表示当前左右的字符串中的字符位置   
  240.     for (i_=key_len-1, i=key_ascii_len-1;i>=0 && i_>=0;i--,i_--){  
  241.         barr[i_] = (char *) malloc(str_unicode_len); //动态申请内存是为了解决c语言函数内声明数组的长度有限制   
  242.         narr[i_] = (char *) malloc(str_unicode_len);  
  243.         darr[i_] = (char *) malloc(str_unicode_len);  
  244.         int  is_left_ascii = key[i]<0 || key[i] >= 127 ? 0 : 1;  
  245.         for (j=str_len-1, j_=str_unicode_len-1;j>=0&&j_>=0;j--,j_--){  
  246.             int  is_right_ascii = str[j] < 0 || str[j] >= 127 ? 0 : 1;  
  247.             barr[i_][j_] = 0;  
  248.             if (!is_left_ascii || !is_right_ascii){  
  249.                 if (!is_left_ascii && !is_right_ascii){  
  250.                     int  k = 2, eq=1;  
  251.                     for (;k>=0;k--){  
  252.                         if (i-k >= 0 && j-k>=0 && key[i-k] != str[j-k]){  
  253.                             eq = 0;  
  254.                             break ;  
  255.                         }  
  256.                     }  
  257.                     barr[i_][j_] = eq;  
  258.                 }else {  
  259.                     barr[i_][j_] = 0;  
  260.                 }  
  261.             }else {  
  262.                 barr[i_][j_] = str[j] == key[i] || tolower(str[j]) == tolower(key[i]) ? 1 : 0;  
  263.             }  
  264.   
  265.             darr[i_][j_] = 0;  
  266.             narr[i_][j_] = 0;  
  267.             int  indexOfEnds = find_ends(j_);  
  268.             int  n_right = 0, n_down = 0, n_rightdown = 0, d_right = 0, d_down = 0, d_rightdown = 0;  
  269.             if (indexOfEnds == -1 && j_!=str_unicode_len - 1){  
  270.                 n_right = narr[i_][j_ + 1];  
  271.                 d_right = darr[i_][j_ + 1];  
  272.             }  
  273.             if (i_!=key_len -1){  
  274.                 n_down = narr[i_ + 1][j_];  
  275.                 d_down = darr[i_ + 1][j_];  
  276.             }  
  277.             if (indexOfEnds == -1 && j_!=str_unicode_len - 1 && i_!=key_len -1){  
  278.                 n_rightdown = narr[i_ + 1][j_ + 1];  
  279.                 d_rightdown = darr[i_ + 1][j_ + 1];  
  280.             }  
  281.             n_rightdown += barr[i_][j_];  
  282.             narr[i_][j_] = n_right > n_down ? (n_right > n_rightdown ?  n_right : n_rightdown) : (n_down > n_rightdown ? n_down : n_rightdown);  
  283.             if (barr[i_][j_]){  
  284.                 darr[i_][j_] = d_rightdown + 1;  
  285.             }else   if (n_right >= n_down){  
  286.                 darr[i_][j_] = d_right;  
  287.             }else {  
  288.                 darr[i_][j_] = d_down + 1;  
  289.             }  
  290.   
  291.               
  292.             if (!is_right_ascii){  
  293.                 j-=2;  
  294.             }  
  295.             //printf("%d/t", narr[i_][j_]);   
  296.         }  
  297.         //printf("/n");   
  298.         //max_id_maps[i] = (int *)malloc(sizeof(int)*10);   
  299.         max_id_maps_mem_lens[i_] = 0;  
  300.         max_id_maps_lens[i_] = 0;  
  301.           
  302.         if (!is_left_ascii){  
  303.             i-=2;  
  304.         }  
  305.     }  
  306.   
  307.     //max_id_maps[key_len] = (int *)malloc(sizeof(int)*10);   
  308.     max_id_maps_mem_lens[key_len] = 0;  
  309.     max_id_maps_lens[key_len] = 0;  
  310.     int  k=0;  
  311.     //计算最大匹配度和最优匹配路径长度。并将其放到如到max_id_maps中   
  312.     for (k=0;k<idmaps_len;k++){  
  313.         int  end=idmaps[k].end, j=idmaps[k].start, end_i = key_len, max_ = 0, min_ = -1;  
  314.         while (j<end){  
  315.             int  temp_end_i = -1;  
  316.             for (i=0;i<end_i;i++){  
  317.                 if (barr[i][j]){  
  318.                     if (temp_end_i==-1){  
  319.                         temp_end_i = i;  
  320.                     }  
  321.                     if (narr[i][j] > max_){  
  322.                         max_ = narr[i][j];  
  323.                     }  
  324.                     if (min_ == -1 || darr[i][j] < min_){  
  325.                         min_ = darr[i][j];  
  326.                     }  
  327.                 }  
  328.             }  
  329.             if (temp_end_i != -1){  
  330.                 end_i = temp_end_i;  
  331.             }  
  332.             j++;  
  333.         }  
  334.         if (max_ != 0){  
  335.             if (max_id_maps_mem_lens[max_] == 0){  
  336.                 max_id_maps[max_] = (int  *)malloc( sizeof ( int )*10);  
  337.                 max_id_maps_mem_lens[max_] = 10;  
  338.             }else   if (max_id_maps_mem_lens[max_] == max_id_maps_lens[max_]){  
  339.                 max_id_maps_mem_lens[max_] += STEP;  
  340.                 max_id_maps[max_] = (int  *)realloc(max_id_maps[max_],  sizeof ( int )*max_id_maps_mem_lens[max_]);  
  341.             }  
  342.             *(max_id_maps[max_] + max_id_maps_lens[max_]) = idmaps[k].id;  
  343.             max_id_maps_lens[max_]++;  
  344.         }  
  345.     }  
  346.     //-----------------计时,计算性能   
  347.     gettimeofday(&tpend,NULL);   
  348.     timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;   
  349.     timeuse/=1000000;   
  350.     printf("Used Time:%f/n" ,timeuse);   
  351.     for (i=0;i<=key_len;i++){  
  352.         printf("%d -- " ,i);  
  353.         for (j=0;j<max_id_maps_lens[i];j++){  
  354.             printf("%d/t" , max_id_maps[i][j]);  
  355.         }  
  356.         printf("/n" );  
  357.     }  
  358.     //--------------计时结束   
  359.     //释放在这个函数中申请的动态内存。   
  360.     for (i=0;i<=key_len;i++){  
  361.         if (max_id_maps_mem_lens[i]>0){  
  362.             //printf("%d,",max_id_maps_mem_lens[i]);   
  363.             free(max_id_maps[i]);  
  364.         }  
  365.         if (i!=key_len){  
  366.             free(barr[i]);  
  367.             free(narr[i]);  
  368.             free(darr[i]);  
  369.         }  
  370.     }  
  371.     //testPrint(&narr, key_len, str_unicode_len);   
  372. }  
  373. //释放程序中申请的动态内存   
  374. void  freeMemory(){  
  375.     free(ends);  
  376.     free(idmaps);  
  377.     free(str);  
  378. }  
  379.   
  380.   
  381. int  main(){   
  382.     init_search_dir("/home/phyeas/test/" );  
  383.     search("Java云计算" );  
  384.     //search("BCXCADFESBABCACA");   
  385.     //init_search_dir("/home/phyeas/test/test2/");   
  386.     //int i=0;   
  387.     freeMemory();  
  388.     return  0;  
  389. }  
 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值