読者です 読者をやめる 読者になる 読者になる

CodeIQ Blog

自分の実力を知りたいITエンジニア向けの、実務スキル評価サービス「CodeIQ(コードアイキュー)」の公式ブログです。

「一次マルコフモデル」の練習

問題解説

こんにちは。

CodeIQの三木です。

 

この度は沢山の解答ありがとうございました。

確率計算をする面白さを感じて貰えれば幸いです。

 

問題、解答例(力技、R)と、私が面白いと思った解答を紹介します。

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

●問題

 

秀樹君の会社の社食には6種類のメニューがあります。

生姜焼き

ハンバーグ

トンカツ

ラーメン

オムライス

カレー

 

秀樹君は毎日社食でご飯を食べます。

メニュー選びは以下の確率で行われます。

(1)昨日食べたメニューと違うものを選ぶ確率は全て同じ

(2)昨日食べたメニューと同じものを選ぶ確率は、(1)の半分

 

つまり、月曜日に生姜焼きを食べたとすると、

火曜日に生姜焼き以外のものを選ぶ確率は全て同じ。

 「ハンバーグを選ぶ確率」=「トンカツを選ぶ確率」=・・・=「カレーを選ぶ確率」

生姜焼きを選ぶ確率は、ハンバーグなどを選ぶ確率の半分。

 「生姜焼きを選ぶ確率」=「ハンバーグを選ぶ確率」÷2

 

秀樹くんは月曜日に生姜焼きを食べました。

木曜日に生姜焼きを食べる確率は何%でしょう?

小数第一位まで(第二位を四捨五入)お答え下さい

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

●解答例

 

■例1:力技で解く

 

木曜日に生姜焼きに至る組み合わせと確率は以下のとおりです。

 f:id:codeiq:20121119155332j:plain

 ケース4の生姜焼き以外を2連続で頼む場合、2回めに生姜焼き以外を頼む

確率は1回めと異なる点の考慮が必要です。

 

■例2:Rで解く

 

n <- 6 

m <- (n-1)*2+1

ps <- 1/m

pd <- 2/m

 

mat <- sapply(1:n, function(i)ifelse(i==1:n,ps,pd))

v <- ifelse(1==1:n, 1, 0)

for(i in c(""tue"", ""wed"", ""thu"")){

  v <- mat %*% v

 

print(v[1])

 

# [1] 0.1660406

 

※hotokuさんとほとんど同じでした!びっくりしました。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

●その他なるほどと思った解答の紹介。

 

いろんな解き方がありました。御参考までに

なるほどと思った解答を紹介します。

 

■U_Taylorさんの解答:JSで解答

 

mon = 0;

thu = 0;

sum = 0;

 

for(tue=0;tue<6;++tue)

for(wed=0;wed<6;++wed)

sum += (mon == tue ? 1 : 2) * (tue == wed ? 1 : 2) * (wed == thu ? 1 : 2);

 

WScript.Echo(sum / (11 * 11 * 11));

 

■nekochanさんの解答:Rで解答(私とは異なったアプローチです。数学的にはこっちのほうがカッコイイです。)

 

sz <- 6 # 選べる品目数

# 推移確率行列の作成、rowSums(mat)で割るのは正規化のため(行方向の和を1にしたい)

mat <- matrix(rep(2,sz*sz),ncol=sz) - diag(1,sz,sz)

mat <- mat / rowSums(mat)

# 初期値(生姜焼きが確率1で、後は0という月曜日の状態)の作成

# この行列は上から順に、生姜焼き、ハンバーグ、トンカツ、ラーメン、

# オムライス、カレーを選ぶ確率を意味する6*1の行列である

init.mat <- matrix(c(1,rep(0,5)),ncol=1)

# 左から遷移行列を1回かけたら、翌日の食事選択確率を表す6*1の行列が得られる。

# 3日後の食事を選ぶ確率が知りたいので、3回行列を左からかければよい。

thu.prob <- mat %*% mat %*% mat %*% init.mat

print(thu.prob[1]*100) # 1行目の要素が生姜焼きの選択確率、16.60...と表示される

 

■hirokazuさん:C++のiostreamを使用。(こんな使い方ができるんですね~

勉強になりました。)

 

#include<iostream>

 

using namespace std;

 

double dp[4][6];

int main(){

 dp[0][0]=1.0;

 for(int i=1;i<4;i++){

  for(int j=0;j<6;j++){

   for(int k=0;k<6;k++){

    if(j==k){

     dp[i][k] += dp[i-1][j]*1/11;

    }

    else{

     dp[i][k] += dp[i-1][j]*2/11;

    }

   }

  }

 }

 int ans = (int)(dp[3][0]*1000+0.5);

 //anser:16.6%

 cout<<""anser:""<<ans/10<<'.'<<ans%10<<'%'<<endl;

 return 0;

}

 

以上です。