隠れマルコフモデル(HMM)で遊ぶ

出力と状態が1対1に対応していなくて、状態を直接知ることができないようなものを隠れマルコフモデル(HMM:Hidden Markov Model)といいます。
で、その隠れマルコフモデルで遊ぶために、とりあえず状態遷移と出力文字候補を作ってみます。


この隠れマルコフモデルで適当に確率を設定して実行したときの結果は、こんな感じになりました。

状態遷移:31 32 33 31 32 33 0 31 32 33 0 21 22 0 21 22 0 21 22 31 32 33 31 32 33 0 31 32 33 31 
出力:serrar rer te rr rrsertah reht
public class HiddenMarkovModel {
    public static void main(String[] args){
        String[] Q = {//状態
            "0", "1", "21", "22", "31", "32", "33"};
        double[][] A = {//状態遷移頻度
            {0,  1,   2,    0,    2,    0,    0},
            {0,  0,   1,    0,    1,    0,    0},
            {0,  0,   0,    1,    0,    0,    0},
            {2,  2,   3,    0,    3,    0,    0},
            {0,  0,   0,    0,    0,    1,    0},
            {0,  0,   0,    0,    0,    0,    1},
            {3,  1,   3,    0,    2,    0,    0}};
        String[] Σ = {//出力文字
            " ", "a", "e", "s", "t", "h", "r"};
        double[][] E = {//文字出力頻度
            {1,   0,   0,   0,   0,   0,   0},
            {0,   2,   0,   3,   3,   0,   0},
            {0,   0,   0,   2,   2,   1,   3},
            {0,   1,   2,   0,   0,   1,   1},
            {0,   0,   0,   2,   2,   1,   2},
            {0,   2,   3,   0,   0,   0,   0},
            {0,   0,   0,   0,   0,   1,   2}};
        //確率の正規化
        for(double[] d : A){
            double sum = 0;
            for(double v : d) sum += v;
            for(int i = 0; i  < d.length; ++i){
                d[i] /= sum;
            }
        }
        for(double[] d : E){
            double sum = 0;
            for(double v : d) sum += v;
            for(int i = 0; i  < d.length; ++i){
                d[i] /= sum;
            }            
        }
        
        //
        int state = 0;
        String result = "";
        String status = "";
        for(int i = 0; i < 30; ++i){
            double r = Math.random();
            for(int j = 0; j < A[state].length; ++j){
                if(r < A[state][j]){
                    //次の状態がきまった
                    state = j;
                    status += Q[state] + " ";
                    double rs = Math.random();
                    //文字を決める
                    for(int n = 0; n < E[state].length; ++n){
                        if(rs < E[state][n]){
                            result += Σ[n];
                            break;
                        }
                        rs -= E[state][n];
                    }
                    break;
                }
                r -= A[state][j];
            }
        }
        System.out.println("状態遷移:" + status);
        System.out.println("出力:" + result);
    }
}