比較BF、KMP和BM演算法的效能(純C語言實現,而且……VC6.0編譯的程式跑得比codeblocks 17.12編譯的快)
首先宣告:BF和KMP演算法是刁肥宅自己實現的,BM演算法源自此處,刁肥宅未曾妄加改動,只是作測試用。作業系統及硬體配置資訊如圖6所示,刁肥宅所用編譯環境為:Code::Blocks 17.12、VC6.0(完整綠色版)。所用原始碼與測試資料都已上傳到百度雲盤(提取密碼:dhu0)與CSDN“我的資源-下載”上,各位看官可以免費下載親測。
話不多說,先貼出三個演算法的C語言實現:
一、演算法實現
1.BF模式匹配演算法
標頭檔案BF.c:
/*Bf.h*/
#ifndef BF_H_INCLUDED
#define BF_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <malloc.h>
int Find( char *T, int T_Length,char *P, int P_Length );
#endif /* BF_H_INCLUDED*/
原始檔BF.c:
/*BF.c*/
#include "BF.h"
int Find( char *T, int T_Length,char *P, int P_Length )
{
/*在目標串T中從第0個字元開始尋找模式串P在T中匹配的位置。若在T中找不到與
串P匹配的子串, 則函式返回-1, 否則返回P在T中第一次匹配的位置。*/
int i, j, k; /*last為在T中最後可比對位置*/
/*printf( "In Find,T_Length = %d, P_Length = %d\n", T_Length, P_Length );*/
/*for ( i = 0; i <= strlen(T) - strlen(P); i++)*/
for ( i = 0; i <= T_Length - P_Length; i++ )/*逐趟比對*/
{
for ( k = i, j = 0; j < P_Length; k++, j++ ) /*從T.ch[i]開始與P.ch進行比對*/
if ( *( T + k ) != *( P + j ) )
break; /*比對不等跳出迴圈*/
if ( j == P_Length )
return i + 1; /*P已掃描完, 匹配成功*/
}
return -1; /*匹配失敗*/
}
原始檔test.c:
/*test.c*/
#include "BF.h"
int main()
{
/*freopen("x1.out","w",stdout);*/
srand( time(NULL) );
char *T = NULL,*P = NULL;
FILE *fp1 = fopen("x1.in","r");
if( fp1 == NULL )
{
printf("Opening Error.\n");
exit(EXIT_FAILURE);
}
int i,d,ChoosingKey = 0,Value = 0,T_Length,P_Length,RandomSize = 1000001430;
T = ( char * )malloc( sizeof(char) * RandomSize );
P = ( char * )malloc( sizeof(char) * 34 );
/*strcpy(P,"wOvT5jN9154gQ751sK9eH1I1l0Hgf9d");*/
strcpy(P,"fsf8QQCNlL80s1ouGx2TeANoH2jxx9SYQ");
for( i = 0;!feof(fp1);i ++ )
fscanf( fp1,"%c",(T + i) );
P_Length = ( strlen(P) );
T_Length = ( strlen(T) );
/*
for( i = 0;i < RandomSize;i ++ )
{
ChoosingKey = rand() % 3 + 1;
switch(ChoosingKey)
{
case 1:
Value = rand() % ( 90 - 65 + 1 ) + 65;
*( T + i ) = (char)Value;
break;
case 2:
Value = rand() % ( 122 - 97 + 1 ) + 97;
*( T + i ) = (char)Value;
break;
case 3:
Value = rand() % ( 57 - 48 + 1 ) + 48;
*( T + i ) = (char)Value;
break;
default:
*( T + i ) = 'Q';
}
}
i = 0;
while( *(T + i) != '\0' )
{
printf("%c",*(T + i));
i ++;
}
*/
d = Find ( T,T_Length,P,P_Length );
if( d == -1 )
printf("匹配位置失敗!\n");
else
printf("匹配位置為%d\n",d);
printf("Time used = %.2f seconds.\n",(double)clock() / CLOCKS_PER_SEC);
system("pause");
//return 0;
}
2.KMP模式匹配演算法
標頭檔案KMP.h:
/*KMP.h*/
#ifndef KMP_H_INCLUDED
#define KMP_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
#define DefaultSize 40
void GetNext ( char *P, int P_Length, int next[] );
int FastFind ( char *T, int T_Length,char *P, int P_Length,int next[ ] );
#endif /* KMP_H_INCLUDED*/
原始檔KMP.c:
/*KMP.c*/
#include "KMP.h"
/*
/*P和T的長度必須以引數的形式從主函式傳遞給被呼叫的 GEtNext 和 FastFind 函式,
否則當T長度較大時匹配結果必然錯誤!!!或者d的返回值為 -1 (預設匹配失敗時的值)
在這兩個函式中,直接用 strlen( P ) 、strlen( T ) 求P、T的長度,且似乎主要是因為 strlen( T )
最終匹配結果錯誤。為什麼?!
*/
/*void GetNext ( char *P, int next[] )*/
void GetNext ( char *P, int P_Length, int next[] )
{
/*對模式串P, 計算next失配函式*/
int j = 0, k = -1;
next[0] = -1; /*j已經在最左邊了,不可能再移動了,這時候要應該是i指標後移*/
/*while ( j < strlen( P ) )
直接用 strlen( P ) 求P的長度,最終匹配結果錯誤。為什麼?!
*/
while ( j < P_Length )/*計算next[j]*/
{
/*while ( k >= 0 && P->ch[j] != P->ch[k] )*/
while ( k >= 0 && *(P + j) != *(P + k) )
k = next[k];
j++;
k++;
if ( *(P + j) == *(P + k) )
next[j] = next[k];
else
next[j] = k;
}
/*printf( "In GetNext,strlen( P ) = %d\n",strlen( P ) );*/
}
/*int FastFind ( char *T, char *P, int next[ ] )*/
int FastFind ( char *T, int T_Length,char *P, int P_Length,int next[ ] )
{
/*在目標串T中尋找模式串P的匹配位置。若找到,則函式返回P在T中開始字元
下標,否則函式返回-1。陣列nex存放P的失配函式next[j]值。*/
int j = 0, i = 0; /*串P與串T的掃描指標*/
/*while ( j < strlen( P ) && i < strlen( T ) )
直接用 strlen( P ) 、strlen( T ) 求P、T的長度,最終匹配結果錯誤。為什麼?!
*/
while ( j < P_Length && i < T_Length ) /*對兩串掃描*/
/*while ( j < 31 && i < 1000050 )*/
{
/*printf ( "i=%d, j=%d\n", i, j );*/
if ( j == -1 || *(P + j) == *(T + i) )/*對應字元匹配,比對位置加一*/
{
j++;
i++;
}
else
j = next[j]; /*第j位失配,找下一對齊位置*/
}
/*printf( "In FastFind,strlen( T ) = %d,strlen( P ) = %d\n",strlen( T ),strlen( P ) );*/
/*if ( j < strlen( P ) )
直接用 strlen( P ) 求P的長度,最終匹配結果錯誤。為什麼?!
*/
if ( j < P_Length ) /*j未比完失配,匹配失敗*/
/*if ( j < 31 )*/
return -1;
else
/*return i - strlen( P ); */
return i - P_Length + 1; /*匹配成功*/
}
原始檔test.c:
/*test.c*/
#include "KMP.h"
int main()
{
/*freopen( "x1.out","w",stdout );*/
srand( time(NULL) );
char *T = NULL,*P = NULL;
FILE *fp1 = fopen("x1.in","r");
if( fp1 == NULL )
{
printf("Opening Error.\n");
exit(EXIT_FAILURE);
}
int i,T_Length = 0,P_Length = 0,d = 0,ChoosingKey = 0,Value = 0,RandomSize = 1000001430;
/*int Fail[10000120];*/
int *Fail = (int *)malloc( sizeof(int) * DefaultSize );
T = ( char * )malloc( sizeof(char) * RandomSize );
P = ( char * )malloc( sizeof(char) * 34 );
/*strcpy(P,"wOvT5jN9154gQ751sK9eH1I1l0Hgf9d");*/
strcpy(P,"fsf8QQCNlL80s1ouGx2TeANoH2jxx9SYQ");
P_Length = ( strlen(P) );
for( i = 0;!feof(fp1);i ++ )
fscanf( fp1,"%c",(T + i) );
T_Length = ( strlen(T) );
/*printf( "In main,T_Length = %d, P_Length = %d\n", T_Length, P_Length );
system("pause");*/
/*for( i = 0;i < 10000120;i ++ )
Fail[i] = 0;*/
memset( Fail,0,sizeof(Fail) );
/*GetNext ( P, Fail );*/
GetNext ( P, P_Length, Fail );
/*d = FastFind ( T, P, Fail );*/
d = FastFind ( T, T_Length, P, P_Length, Fail );
if( d == -1 )
printf("匹配位置失敗!\n");
else
printf("匹配位置為%d\n",d);/*-(d + 45) - 2*/
/*Sleep(1000 * 60);*/
printf("Time used = %.2f seconds.\n",(double)clock() / CLOCKS_PER_SEC);
system("pause");
/*return 0;*/
}
3.BM模式匹配演算法
標頭檔案BM.h:
/*BM.h*/
#ifndef BM_H_INCLUDED
#define BM_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <windows.h>
int *MakeSkip(char* ptrn,int pLen);
int *MakeShift(char *ptrn,int pLen);
int BMSearch(char *buf,int blen,char *ptrn,int plen,int *skip,int *shift);
#endif // BM_H_INCLUDED
原始檔BM.c:
/*BM.c*/
#include "BM.h"
/*
函式:int* MakeSkip(char*,int)
目的:根據壞字元規則做預處理,建立一張壞字元表
表的長度由字元的規模而定,
如果只有字母則長度只有26,
如果是字母加數字長度就是26+10
引數:
ptrn=>模式串P
pLen=>模式串P長度
返回:
int* - 壞字元表
*/
int *MakeSkip(char* ptrn,int pLen)
{
int i;
int len=pLen;
char *p=ptrn;
//為建立壞字元表,申請256個int的空間
int *skip=(int*)malloc(256*sizeof(int));
if(skip==NULL)
{
printf("malloc failed!");
return 0;
}
//初始化壞字元表,256個單元全部初始化為pLen
for(i=0;i<256;i++)
{
*(skip+i)=pLen;
}
//賦值,從左到右遍歷ptrn,這樣如果一個字元出現兩次,後面的覆蓋前面的,
//不在模式中出現的字元不用再賦值,它們使用預設值pLen。
while(pLen!=0)
{
*(skip+(int)*ptrn++)=--pLen;
}
return skip;
}
/*
函式:int *MakeShift(char*,int)
目的:根據好字尾原則做預處理,建立一張好字尾表
引數:
ptrn=>模式串P
pLen=>模式串P的長度
返回:
int* :好字尾表
*/
int *MakeShift(char *ptrn,int pLen)
{
//為好字尾表申請pLen個int的空間
//這樣,第一個位置放置長度為1的字尾
int *shift=(int *)malloc(pLen*sizeof(int));
int *sptr=shift+pLen-1;//方便為好字尾表進行賦值的指標
char *pptr=ptrn+pLen-1;//記錄好字尾表邊界位置的指標
char c;
//int i;
if(shift==NULL)
{
fprintf(stderr,"malloc failed!");
return 0;
}
c=*(ptrn+pLen-1);//儲存模式串中最後一個字元,因為要反覆用到它
*sptr=1;//以最後一個字元為邊界時,移動距離設為1(因為要與壞字元規則比較,所以這個是個假設,1也是最小的移動距離)
pptr--;//邊界移動到倒數第二個字元
while(--sptr>=shift)//該最外層迴圈完成給好字尾表中的每一個單元進行賦值的工作
{
char *p1=ptrn+pLen-2,*p2,*p3;
//該do...while迴圈完成以當前pptr所指向的字元為邊界時,要移動的距離
do
{
while(p1>=ptrn&&*p1--!=c);//該空迴圈,尋找與最後一個字元c匹配的字元所指向的位置
if(p1<ptrn)
{
*sptr=pLen;
break;
}
p2=ptrn+pLen-2;
p3=p1;
while(p3>=ptrn&&*p3--==*p2--&&p2>=pptr);//該空迴圈,判斷在邊界內字串匹配到什麼位置
if(p2<pptr)
{
if(*++p3==*++p2)
{
*sptr=p2-p3;
break;
}
if(*p2==*p3)
{
continue;
}
}
if(p3<ptrn)
{
*sptr=p2-p3;
break;
}
}while(p3>=ptrn);
pptr--;//邊界繼續向前移動
}
return shift;
}
/*
函式:int* BMSearch(char*,int ,char*,int,int*,int*)
目的:判斷文字串是否包含模式串P
引數:
buf->文字串T
blen->文字串T長度
ptrn->模式串P長度
plen->模式串P長度
skip->壞字元表
shift->好字尾表
返回:
int->1表示成功(文字串包含模式串),0表示失敗(文字串不包含模式串)
*/
int BMSearch(char *buf,int blen,char *ptrn,int plen,int *skip,int *shift)
{
int b_idx=plen;
if(plen==0)
{
return 1;
}
while(b_idx<=blen)//計算字串是否匹配到了盡頭
{
int p_idx=plen,skip_stride,shift_stride;
int i=0;
int temp=b_idx;//是為了不改動b_idx的值,b_idx將來用於計算移動的距離
while(buf[--temp]==ptrn[--p_idx])//開始匹配
{
i++;
if(p_idx==0)
{
fprintf(stderr,"match at %d!\n",b_idx - plen + 1);
return 1;
}
}
/*printf("i:%d\tbad:%c\n",i,buf[temp]);*/
skip_stride=skip[(unsigned char)buf[temp]]-i;//根據壞字元規則計算跳躍的距離
shift_stride=shift[p_idx];
/*printf("b_idx:%d 1:%d 2:%d\n",b_idx,skip_stride,shift_stride);*/
b_idx+=(skip_stride>shift_stride)?skip_stride:shift_stride;//取最大者
}
return 0;
}
原始檔test.c:
/*test.c*/
#include "BM.h"
int main()
{
FILE *fp1 = fopen( "E:/document/²âÊÔÊý¾Ý/100503.in","r" );
if( !fp1 )
{
printf( "Opening data failed.\n" );
Sleep( 1000 * 60 );
exit( EXIT_FAILURE );
}
int BLength = 100503,PLength = 60,i;
char *buf = ( char * )malloc( sizeof(char) * BLength );
char *ptrn = ( char * )malloc( sizeof(char) * PLength );
for( i = 0;!feof(fp1);i ++ )
fscanf( fp1,"%c",(buf + i) );
/*strcpy(ptrn,"6qc48TlVIvb482XM07Y4isP6X89a7WYi948579f1HR1Avsp2Qok5n2T0z9I");*/
strcpy(ptrn,"fsf8QQCNlL80s1ouGx2TeANoH2jxx9SYQ");
int *skip = NULL;
int *shift = NULL;
fprintf(stderr,"plen=%d!\n",strlen(ptrn));
skip=MakeSkip(ptrn,strlen(ptrn));
shift=MakeShift(ptrn,strlen(ptrn));
BMSearch(buf,strlen(buf),ptrn,strlen(ptrn),skip,shift);
/*printf( "%d\n",strlen("6qc48TlVIvb482XM07Y4isP6X89a7WYi948579f1HR1Avsp2Qok5n2T0z9I") );*/
/*return 0;*/
printf("Time used = %.2f seconds.\n",(double)clock() / CLOCKS_PER_SEC);
system( "pause" );
}
4.生成測試用隨機字串C程式碼
/*生成測試用隨機字串*/
#include <stdio.h>
#include <stdlib.h>
int main()
{
freopen("10000000050.out","w",stdout);
srand( time(NULL) );
char *T = NULL,*P = NULL;
int i,d,ChoosingKey = 0,Value = 0,RandomSize = 10000000050;
T = ( char * )malloc( sizeof(char) * RandomSize );
/*P = ( char * )malloc( sizeof(char) * 32 );
strcpy(P,"wOvT5jN9154gQ751sK9eH1I1l0Hgf9d");*/
for( i = 0;i < RandomSize;i ++ )
{
ChoosingKey = rand() % 3 + 1;
switch(ChoosingKey)
{
case 1:
Value = rand() % ( 90 - 65 + 1 ) + 65;
*( T + i ) = (char)Value;
break;
case 2:
Value = rand() % ( 122 - 97 + 1 ) + 97;
*( T + i ) = (char)Value;
break;
case 3:
Value = rand() % ( 57 - 48 + 1 ) + 48;
*( T + i ) = (char)Value;
break;
default:
*( T + i ) = 'Q';
break;
}
}
i = 0;
while( *(T + i) != '\0' )
{
printf("%c",*(T + i));
i ++;
}
/*printf("\n%s",P);*/
return 0;
}
二、測試結果
一言難盡,暫且放下結論,改天詳談,演算法執行效率:BM>BF>KMP。而且刁肥宅發現,VC6.0編譯的程式跑的結果耗時永遠比CodeBlocks 17.12編譯的短。
有圖有真相,各位可以去下載刁肥宅上傳的原始碼與測試資料,自行驗證。
相關文章
- CMM編譯器和C編譯器過程呼叫實現的比較編譯
- NEON彙編比純C程式碼快17倍!C程式
- C語言編譯工具C語言編譯
- 將你的 C 語言程式碼編譯成 .NET編譯
- C語言 - 條件編譯C語言編譯
- 現代編譯原理C語言描述pdf編譯原理C語言
- Linux下C語言編譯的問題LinuxC語言編譯
- c語言多檔案編譯C語言編譯
- C語言編譯全過程C語言編譯
- 57段讓編譯器崩潰的C語言程式碼編譯C語言
- [譯] 原生 iOS(Swift) 和 React-Native 的效能比較iOSSwiftReact
- 第一個C語言編譯器是怎樣編寫的?C語言編譯
- 第一個 C 語言編譯器是怎樣編寫的?編譯
- Rust編譯器比其他語言更能捕獲隱藏的錯誤 - kerkourRust編譯
- sift演算法的編譯與實現演算法編譯
- C語言的編譯連結執行過程C語言編譯
- 淺談,C語言編譯原理的個人見解C語言編譯原理
- 小C語言--詞法分析程式(編譯原理實驗一)C語言詞法分析編譯原理
- C語言的比較庫函式--qsortC語言函式
- 現代語言Go、Rust、Swift和Dart的比較GoRustSwiftDart
- 使用 Sublime Text 3 編譯 C 語言編譯
- C語言編譯器手機版C語言編譯
- 3- C語言編譯過程C語言編譯
- C語言編譯過程簡介C語言編譯
- 【開發語言】PHP、Java、C語言的編譯執行過程PHPJavaC語言編譯
- C語言編譯和連結過程簡介C語言編譯
- (嵌入式)Windows與Ubantu下的C語言程式的編譯執行WindowsC語言編譯
- C語言的條件編譯#if, #elif, #else, #endif、#ifdef, #ifndefC語言編譯
- 深入淺出編譯原理-1-C語言的文法編譯原理C語言
- 流行語言的比較 (轉)
- C語言_來了解一下GCC編譯器編譯C可執行指令碼的過程C語言GC編譯指令碼
- 這是一篇比較詳盡的核心編譯說明(轉)編譯
- C語言中編譯和連結C語言編譯
- 如何在Visual Studio和CodeBlocks中反編譯C++程式碼BloC編譯C++
- 如何分析和提高(C/C++)程式的編譯速度?C++編譯
- 編譯C++ 程式的過程編譯C++
- C語言-->(十四)結構體、巨集、編譯C語言結構體編譯
- java編譯、編碼、語言設定Java編譯