Part 1.什麼是“有限狀態機”
從百科詞條來看,有限狀態機(FSM/FSA)是爲研究有限內存的計算過程和某些語言類而抽象出來的一種計算機模型,它可以表示爲一個有向圖。有限狀態機多爲Moore機與Mealy機兩類,Moore機對每一個狀態都附加有輸出動作,而Mealy機對每一個轉移都附加有輸出動作。簡單理解,可以認爲Moore機的下一狀態只由當前狀態決定,而Mealy機的下一狀態不僅與當前狀態有關,還與當前輸入值有關。恰好今天磕的兩道題,一道屬於Moore機的,一道屬於Mealy機的。
Part 2.單鍵電風扇問題
單鍵電風扇只有一個鍵,比如對於風速只有高、中、低三檔的單鍵電扇而言,調節風速時,中速就是在高速與低速之間,它的下一檔與上一檔關係是固定的,可見,它的下一狀態只由當前狀態決定,因此它屬於Moore機。題目如圖:大致要求是,假設單鍵電風扇開始處於停止狀態,請模擬在各狀態下按下按鍵所發生的事件。
一看這道題,當然可以使用數組解決。把各種狀態寫進字符型數組,然後使用循環依次讀取數組,讀到底時,再從首位讀起;也可以使用int型數組,將每個狀態所對應的數字排列好,然後依次用switch語句printf輸出狀態;甚至用if else萬金油也行。不過,既然這幅圖那麼像鏈表,最終就簡單粗暴用單向循環鏈表寫了。程序如下:
#include<stdio.h>
struct FanState{
char *state=NULL;
struct FanState *next;
};
int main(){
FanState A,B,C,D,*head,*p;
char a[5]="STOP",b[4]="LOW",c[7]="MEDIUM",d[5]="HIGH";
A.state=a;
B.state=b;
C.state=c;
D.state=d;
head=&A;
A.next=&B;
B.next=&C;
C.next=&D;
D.next=&A;
p=head;
char s;
while((s=getchar())!=EOF){
if(s=='\n')printf("%s->%s\n",p->state,p->next->state);
else if(s=='q'||s=='Q')break;//按q或Q退出
p=p->next;
}
p=NULL;
return 0;
}
只需按回車健就能模擬按鍵的動作啦,很直觀,按q或Q退出循環。
但是遇到第二題,狀態與狀態轉移的關係就複雜多了,方法選擇至關重要。
Part 3.多媒體播放器問題
這個播放器可不是按個鍵就能確定下一個狀態的了,下一個狀態不僅跟現有狀態有關,還跟按鍵方式有關,這已經是個Mealy機了。
但乍一看,第一反應還是鏈表——沒錯,是雙向鏈表。但是,這雙向鏈表的好幾個結點next與prior指向貌似還不止一個,比如“暫停”,那就插入吧,哦,這還不夠,應該以那幾個爲原始結點呢?停止->播放->快速前進?停止->播放->快速後退?開始暈了,然後,萬一遇到某狀況,爲了達到某些結點,是不是還要刪掉一些結點呀,畢竟這個next與prior不能指向多處……
鏈表這種高級的手段本菜雞被弄暈了,還是考慮傳統點的方法吧。如果用數組表示狀態與執行操作的話,那麼有三種信息需要考慮:目前狀態、按鍵操作和下一個狀態。三維嗎?不,二維足矣。如果以目前狀態“暫停”爲數組第二行,”按一下“爲第2列,“按緊三秒”爲第5列,假設數組名爲Array,則Array[1][1]可表示“目前狀態+按一下”,Array[1][4]可表示“目前狀態+按緊三秒”,那麼這個數組位置所對應的元素是不是就可以表示下一個狀態了?當然可以,只要表示使用數字表示狀態,然後再switch結構中輸出對應的下一個狀態即可。
#include<stdio.h>
int main(){
int x=0,y,z=1;
int Array[5][5]={{6,2,6,6,6},
{6,2,6,6,0},
{6,1,3,4,0},
{6,2,6,6,6},
{6,2,6,6,6}};
printf("初始爲停止狀態:\n按一下,請輸入數字1;按兩下,請輸入數字2;\n");
printf("按三下,請輸入數字3;按緊三秒,請輸入數字4;\n退出請輸入數字5.\n");
printf("*******************************************************\n");
printf("多媒體播放狀態:\n停止\n");
while(1){scanf("%d",&y);
if(y==5)break;
switch(Array[x][y]){
case 0:printf("停止\n");break;
case 1:printf("暫停\n");break;
case 2:printf("播放\n");break;
case 3:printf("快速前進\n");break;
case 4:printf("快速後退\n");break;
default:z=0;printf("輸入錯誤,請重新輸入\n");break;
}
if(z)x=Array[x][y];
z=1;
}
return 0;
}
輸出結果如下:
個人感覺實現程序不一定要使用高大上的方法,如果條件允許,思路簡單清晰的方法纔是好方法。
感謝耐心觀看,如果對題目有更好的解決方法或者想法,歡迎分享~