用HMM(隱馬)圖解三國殺的于吉「質疑」

·背景javascript

    最近乘閒暇之餘初探了HMM(隱馬爾科夫模型),以爲還有點意思,可是網上的教程都超級枯草,可讀性不好,抄來抄去的,一堆公式仍在你面前,誰能搞的懂(但園內的兩篇寫的還算不錯。真才實學)。在熬製3天后,把這篇心得反饋給各位碼友,爲了更加生動的說明模型,特舉例三國殺的"于吉"以便加深各位印象。java

 

·于吉 ios

武將技:【蠱惑】——你能夠說出任何一種基本牌或非延時類錦囊牌,並正面朝下使用或打出一張手牌。若無人質疑,則該牌按你所述之牌結算。如有人質疑則亮出驗明:若爲真,質疑者各失去1點體力;若爲假,質疑者各摸1張牌。不管真假,棄置被質疑的牌。僅當被質疑的牌爲紅桃花色且爲真時,該牌仍然能夠進行結算。最大的意義在於猜想真假,也就是HMM中的隱藏隊列。算法

                                                        

·HMM 五對象 ide

·觀察隊列:也就是對手打出的聲明牌序,例如【殺、殺、桃、殺、南蠻】。這3張牌我的感受算是于吉回合內聲明頻率最高的3張牌。函數

·隱藏隊列:也就是對手打出該張牌時是真?是假?,例如【真、真、假、假、真】,這個是HMM以後要計算的對象之一。學習

·初始狀態Matrix P/Pie:聲明第一張牌時,是真是假的機率。spa

·狀態轉移Matrix A:從聲明第二章牌開始,由真變假,或假變真,或真變真,或假變假的機率。3d

·混淆矩陣Matrix B:每次聲明時的真假心態下,該將什麼牌聲明成什麼牌。對象

·HMM 兩大問題(還有一個學習就不寫了)

·評估問題:該輪的聲明牌序,其中存在假牌的可能性有多高?

·解碼問題:該輪的聲明牌序,其中哪幾張牌可能爲假牌?

·HMM 評估算法過程

 

·HMM 解碼算法過程

 

 ·HMM測算結果

 

Complie Done


act:0   Q[0][0]:0.16            Q[0][1]:0.1        
act:0   Q[1][0]:0.0332          Q[1][1]:0.047      
act:1   Q[2][0]:0.021128        Q[2][1]:0.005476   
act:0   Q[3][0]:0.003302        Q[3][1]:0.005047   
act:2   Q[4][0]:0.00220564      Q[4][1]:0.00085047 

Last Sum Prob=0.00305611

act:0   Q[0][0]:0.16            Q[0][1]:0.1        
act:0   com[0]0.096       comp[1]0.07           Q[1][0]:0.0192          com[0]0.064       comp[1]0.03           Q[1][1]:0.032      
act:1   com[0]0.01152     comp[1]0.0224         Q[2][0]:0.00896         com[0]0.00768     comp[1]0.0096         Q[2][1]:0.00192    
act:0   com[0]0.005376    comp[1]0.001344       Q[3][0]:0.0010752       com[0]0.003584    comp[1]0.000576       Q[3][1]:0.001792   
act:2   com[0]0.00064512  comp[1]0.0012544      Q[4][0]:0.00050176      com[0]0.00043008  comp[1]0.0005376      Q[4][1]:0.00016128 

Last Max Prob:0.00050176

Path[0][0]=-1   Path[0][1]=-1
Path[1][0]=0    Path[1][1]=0
Path[2][0]=1    Path[2][1]=1
Path[3][0]=0    Path[3][1]=0
Path[4][0]=1    Path[4][1]=1

real    0m0.001s
user    0m0.000s
sys     0m0.000s

  Last Sum Prob=0.00305611,我的理解更貼近於本次序列不做弊的可能性。

  Path[i][j]來源t-1時刻兩種隱藏狀態的機率對比,前面<後面 爲1,前面>後面 爲0。從1和0的區別看,0的出現意味着該張牌聲明時做假可能性更高。

 ·不足之處

  1. HMM主要依賴於t-1,而在真實世界中,于吉的聲明會顧忌整個牌局,也就是t-N以前的狀態。
  2. 大多數HMM關注兩個互斥類屬性(Yes or No),而在牌局中,于吉的聲明真、假後,若是再出現質疑,會出現超越聲明真假自己的真假效果(好拗口),這使得對手判斷難度增長。
  3. HMM具備強關聯,也就是在數據樣本大到必定階段後,會發現某種觀察狀態與隱藏屬性會無限接近1:1的關係。而在牌局中,即使是基本牌的花色又是一個X因素,尤爲是紅桃殺的牌數(2張)小於紅桃"桃"(7張),而南蠻入侵都是"黑桃"和"草花"。這使得觀察序列與隱藏序列受到了必定干擾,或容易被人臆斷。
  4. HMM的干擾因素。干擾因素一,在於HMM 3個矩陣模型的參數設定,這個卻是還能控制一下。若是是像于吉在牌局中的質疑,還會受到對手人數和血量的干擾,若是周泰把把質疑,必然會於吉第N+1張的聲明膽量,這些都是HMM所不能控制。
  5. P/A/B的參數主觀因素更高,對結果影響較大

 ·源碼

#include <iostream>
#include <vector>
#include <map>
#include <iomanip>
#include <algorithm>


using namespace std;

vector<string> v_ob;
vector<string> v_hide_real;
map<string,int> m_ob;
map<string,int> m_p;
double P[2]={0.8,0.2};                                                  //初始狀態矩陣
double A[2][2]={{0.6,0.4},{0.7,0.3}};                   //狀態轉移矩陣
double B[2][3]={{0.2,0.4,0.4},{0.5,0.2,0.3}};   //混淆矩陣

void Para_init();                                                               //初始化觀察隊列
void Forward();                                                                 //算前向
void Viterbi();

int main()
{
  Para_init();
  Forward();
  Viterbi();

}


//vector 做爲函數入參數 void show_vector(vector <int> &vecTest)
void Viterbi()
{
        int LEN=v_ob.size();
        int M=2;
        double Q[LEN][M];
        double Path[LEN][M];
        for(int i=0;i<LEN;i++) 
        {
                int act=m_ob[v_ob[i]];                     //當天活動
                cout<<"act:"<<act<<"\t";
                for(int j=0;j<M;j++)
                {    
                        if(i==0)
                        {
                            Q[i][j]=P[j]*B[j][act];
                                Path[i][j]=-1;
                        }
                        else
                        {
                            double compare[2]; 
                            for(int z=0;z<M;z++)
                            {
                                compare[z]=Q[i-1][z]*A[z][j];
                            }
                                if(compare[0]<compare[1])
                                { Path[i][j]=1;}
                                else
                                { Path[i][j]=0;}
                            Q[i][j]=max(compare[0],compare[1])*B[j][act];
                                cout<<"com[0]"<<left<<setw(11)<<compare[0]<<" "<<"comp[1]"<<setw(11)<<compare[1]<<"\t";
                    }
                cout<<"Q["<<i<<"]["<<j<<"]:"<<left<<setw(11)<<Q[i][j]<<"\t";
           }
       cout<<endl;
        }
    cout<<endl;
        cout<<"Last Max Prob:"<<max(Q[LEN-1][0],Q[LEN-1][0])<<endl;
        cout<<endl;

        for(int i=0;i<LEN;i++)
        {
                for(int j=0;j<M;j++)
                {cout<<"Path["<<i<<"]["<<j<<"]="<<Path[i][j]<<"\t";}
            cout<<endl;
        }


}

void Forward()
{
   int LEN=v_ob.size();
   int M=2;
   double Q[LEN][M];
   for(int i=0;i<LEN;i++) 
   {
           int act=m_ob[v_ob[i]];                     //當天活動
           cout<<"act:"<<act<<"\t";
           for(int j=0;j<M;j++)
           {
                    //首行判斷
                        if(i==0)
                        {
                                Q[i][j]=P[j]*B[j][act];
                                //cout<<"j="<<j<<"act="<<act<<endl;
                        }
                        else
                        {
                double sum=0;
                                for(int z=0;z<M;z++)
                                {
                                //      cout<<"tmp="<<Q[i-1][z]*A[z][j]<<" ";
                                        sum+=Q[i-1][z]*A[z][j];
                          }
                                Q[i][j]=sum*B[j][act];
                        }
                        cout<<"Q["<<i<<"]["<<j<<"]:"<<left<<setw(11)<<Q[i][j]<<"\t";
           }
           cout<<endl;
   }

   double sum=0;
   for(int j=0;j<M;j++)
   {
           sum+=Q[LEN-1][j];
   }
   cout<<endl;
   cout<<"Last Sum Prob="<<sum<<endl;
   cout<<endl;
}




void Para_init()
{
//add oberser_list
   v_ob.push_back("kill");
   v_ob.push_back("kill");
   v_ob.push_back("tao");
   v_ob.push_back("kill");
   v_ob.push_back("man");
// dict
   m_ob.insert(make_pair("kill",0));
   m_ob.insert(make_pair("tao",1));
   m_ob.insert(make_pair("man",2));

   m_p.insert(make_pair("true",0));
   m_p.insert(make_pair("false",1));
}

  

本人才疏學淺,各位麻友輕拍磚~~~ ^_^

相關文章
相關標籤/搜索