此马尔可夫链模型中鼠标的预期寿命
Expected lifetime of the mouse in this Markov chain model
我正在阅读 the cat and mouse Markov model on wikipedia,并决定编写一些 Julia 代码以凭经验确认分析结果:
P = [
0 0 0.5 0 0.5 ;
0 0 1 0 0 ;
0.25 0.25 0 0.25 0.25;
0 0 0.5 0 0.5 ;
0 0 0 0 1
]
prob_states = transpose([0.0, 1, 0, 0, 0])
prob_end = [0.0]
for i in 1:2000
prob_states = prob_states * P
prob_end_new = (1 - sum(prob_end)) * prob_states[end]
push!(prob_end, prob_end_new)
println("Ending probability: ", prob_end_new)
println("Cumulative: ", sum(prob_end))
end
println("Expected lifetime: ", sum(prob_end .* Array(1:2001)))
这里P
是转移矩阵,prob_states
是每次迭代状态的概率分布,prob_end
是每一步终止概率的数组(例如prob_end[3]
是在第 3 步终止的概率)。
根据该脚本的结果,鼠标的预期寿命约为4.3,而分析结果为4.5。该脚本对我来说很有意义,所以我真的不知道它哪里出了问题。有人能帮忙吗?
P.S。将迭代次数增加一个数量级几乎没有任何改变。
老鼠存活的概率很快接近于零。这不仅对老鼠来说是不幸的,对我们来说也是不幸的,因为我们不能使用 64 位浮点数(Julia 默认使用它)来准确地近似这些微小的生存时间值。
事实上,大多数值 prob_end
在相对较少的迭代次数后完全相同为零,但分析评估这些值应该是 not-quite 零。 Float64
类型根本无法表示这么小的正数。
这就是为什么对数组进行乘法和求和永远不会达到 4.5 的原因;应该使总和更接近该值的步骤失败不能做出贡献,因为它们等于零。相反,我们看到收敛到较低的值。
使用 可以 表示任意微小正值的不同类型,也许是一种可能。有一些建议 但您可能会发现它们非常慢并且 memory-heavy 在执行此马尔可夫链模型的超过几百次迭代时。
另一种解决方案可能是将代码转换为与 log probabilities 一起工作(通常用于克服浮点数的这种限制)。
如果只是想凭经验确认结果,可以直接模拟模型:
const first_index = 1
const last_index = 5
const cat_start = 2
const mouse_start = 4
function move(i)
if i == first_index
return first_index + 1
elseif i == last_index
return last_index - 1
else
return i + rand([-1,1])
end
end
function step(cat, mouse)
return move(cat), move(mouse)
end
function game(cat, mouse)
i = 1
while cat != mouse
cat, mouse = step(cat, mouse)
i += 1
end
return i
end
function run()
res = [game(cat_start, mouse_start) for i=1:10_000_000]
return mean(res), std(res)/sqrt(length(res))
end
μ,σ = run()
println("Mean lifetime: $μ ± $σ")
示例输出:
Mean lifetime: 4.5004993 ± 0.0009083568998918751
我正在阅读 the cat and mouse Markov model on wikipedia,并决定编写一些 Julia 代码以凭经验确认分析结果:
P = [
0 0 0.5 0 0.5 ;
0 0 1 0 0 ;
0.25 0.25 0 0.25 0.25;
0 0 0.5 0 0.5 ;
0 0 0 0 1
]
prob_states = transpose([0.0, 1, 0, 0, 0])
prob_end = [0.0]
for i in 1:2000
prob_states = prob_states * P
prob_end_new = (1 - sum(prob_end)) * prob_states[end]
push!(prob_end, prob_end_new)
println("Ending probability: ", prob_end_new)
println("Cumulative: ", sum(prob_end))
end
println("Expected lifetime: ", sum(prob_end .* Array(1:2001)))
这里P
是转移矩阵,prob_states
是每次迭代状态的概率分布,prob_end
是每一步终止概率的数组(例如prob_end[3]
是在第 3 步终止的概率)。
根据该脚本的结果,鼠标的预期寿命约为4.3,而分析结果为4.5。该脚本对我来说很有意义,所以我真的不知道它哪里出了问题。有人能帮忙吗?
P.S。将迭代次数增加一个数量级几乎没有任何改变。
老鼠存活的概率很快接近于零。这不仅对老鼠来说是不幸的,对我们来说也是不幸的,因为我们不能使用 64 位浮点数(Julia 默认使用它)来准确地近似这些微小的生存时间值。
事实上,大多数值 prob_end
在相对较少的迭代次数后完全相同为零,但分析评估这些值应该是 not-quite 零。 Float64
类型根本无法表示这么小的正数。
这就是为什么对数组进行乘法和求和永远不会达到 4.5 的原因;应该使总和更接近该值的步骤失败不能做出贡献,因为它们等于零。相反,我们看到收敛到较低的值。
使用 可以 表示任意微小正值的不同类型,也许是一种可能。有一些建议
另一种解决方案可能是将代码转换为与 log probabilities 一起工作(通常用于克服浮点数的这种限制)。
如果只是想凭经验确认结果,可以直接模拟模型:
const first_index = 1
const last_index = 5
const cat_start = 2
const mouse_start = 4
function move(i)
if i == first_index
return first_index + 1
elseif i == last_index
return last_index - 1
else
return i + rand([-1,1])
end
end
function step(cat, mouse)
return move(cat), move(mouse)
end
function game(cat, mouse)
i = 1
while cat != mouse
cat, mouse = step(cat, mouse)
i += 1
end
return i
end
function run()
res = [game(cat_start, mouse_start) for i=1:10_000_000]
return mean(res), std(res)/sqrt(length(res))
end
μ,σ = run()
println("Mean lifetime: $μ ± $σ")
示例输出:
Mean lifetime: 4.5004993 ± 0.0009083568998918751