https://www.zhihu.com/question/307814660/answer/566825886
小明把钥匙丢了,只知道钥匙肯定丢在家里或者学校里。如果丢在了家里,在家里找一次能够找到的概率为a;如果丢在了学校里,在学校里找一次能够找到的概率为b。最初小明认为钥匙丢在家里的概率为p。问:小明应该采取什么策略来找钥匙才能够最快找到钥匙(寻找次数尽量少)?期望寻找次数是多少?
假如第一次在家里找,结果没有找到,钥匙依旧在家的概率为$p_1=frac{p imes(1-a)}{1-p imes a}
$
如果第一次在学校找,结果没找到,钥匙依旧在家的概率为$p_1=frac{p}{1-(1-p) imes b}
$
$$f(p)=min{cost_{home},cost_{school}}
$$
$$cost_{home}=canfind imes1+(1-canfind) imes [1+f(p_1)]
$$
$$cost_{school}=canfind imes1+(1-canfind) imes [1+f(p_1)]
$$
其中canfind表示能够找到的概率, $p_1
$ 表示经过一次寻找之后钥匙在家的概率。如果在家里找到了,那么只需要一步就能找到钥匙;如果没有找到,那么需要 $1+f(p_1)
$步才能找到钥匙。
贪心法应该是对的
根据 $p imes a>(1-p) imes b
$来决定去家里找还是学校找。
假如寻找次数为16步,那么所有的寻找序列构成一个深度为16的二叉树。从根节点到叶子节点的路径就表示一个决策。每个结点都有一个数字f(node)表示该结点的找到钥匙的期望寻找次数。当前节点的期望寻找次数依赖于它的两个儿子(分别对应去家里找和去学校找的期望寻找次数)中的期望寻找次数较小者。按照这个方法,暴力枚举整个满二叉树上的决策,发现最终得到的决策路径和贪心法找大的决策路径是一致的。
期望寻找次数为:3.1857095316916766
f(p) 函数表示钥匙在家时,期望寻找次数。这个函数表达式为
$$f(p)=min{1+(1-pa)f(frac{p-pa} { 1 - pa}),1+(1-b+pb)f(frac{p}{1-b+pb})}
$$
根据贪心法(若不是此问题的具体情境,很难看出上式可以转化为条件函数)可以化简此式:
贪心条件为 $pacirc(1-p)b
$,也就是 $pcirc frac{b}{a+b}
$
这个公式等价于
$$f(p)= egin{cases} 1+(1-pa)f(frac{p-pa} { 1 - pa}) when pgtfrac{b}{a+b} \ 1+(1-b+pb)f(frac{p}{1-b+pb}) when plefrac{b}{a+b}\ end{cases}
$$
其中,a、b为常量,p取值范围(0,1)
这个解析式是否能够进一步化简呢?
a = 0.8 # 在家找到的概率
b = 0.4 # 在学校找到的概率
def solve(p, c):
# 钥匙在家的概率为p,返回最优决策、期望找到钥匙所花费的步数
if c == 0:
return max(1 / a, 1 / b) # 如果最后没有找到
if p > b / (a + b):
step = solve(p * (1 - a) / (1 - p * a), c - 1)
ifhome = 1 + step * (1 - p * a) # 如果去家里找,期望找到的步数
return ifhome
else:
step = solve(p / (1 - (1 - p) * b), c - 1)
ifschool = 1 + step * (1 - (1 - p) * b)
# 选取两个儿子中期望步数较小者
return ifschool
import pylab as plt
import numpy as np
print(solve(0.3, 200))
xs = np.linspace(0, 1, 1000)
ps = [solve(i, 200) for i in xs]
plt.plot(xs, ps)
plt.xlabel("p")
plt.ylabel("step")
print(b/(a+b))
plt.plot([b/(a+b)] * 100, np.linspace(0, max(ps), 100), c='r')
plt.show()
使用tensorflow求解迭代形式的函数方程
import tensorflow as tf
import pylab as plt
import numpy as np
import os
a = 0.8
b = 0.4
c = 1000
loss = tf.constant(0.0)
xs = np.linspace(0, 1, c)
fp = tf.Variable(tf.random_uniform((c,)), dtype=tf.float32)
if not os.path.exists("img"):
os.mkdir('img')
for i in range(c):
p = xs[i]
if p > b / (a + b):
next_p = (p - p * a) / (1 - p * a)
next_ind = int(round(next_p * c))
next_ind = min(c - 1, next_ind)
y = 1 + (1 - p * a) * fp[next_ind]
else:
next_p = p / (1 - b + p * a)
next_ind = int(round(next_p * c))
next_ind = min(c - 1, next_ind)
y = 1 + (1 - b + p * b) * fp[next_ind]
loss = loss + tf.abs(y - fp[i])
train_op = tf.train.AdamOptimizer(0.01).minimize(loss)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(100000):
_, lo = sess.run([train_op, loss])
if i % 1000 == 0:
if i % 5000 == 0:
plt.close()
plt.figure()
print(i, lo)
plt.plot(xs, sess.run(fp))
plt.savefig("img/%s.jpg" % i)