📅  最后修改于: 2023-12-03 15:28:12.120000             🧑  作者: Mango
该谜题源于计算机科学家 Donald E. Knuth 在 1974 年创作的《计算机编程艺术》第一卷第一章的习题 70,也被称为“汤姆和杰瑞”(Tom and Jerry)问题。
该问题可以用图论的方式进行建模,并在计算机算法领域中具有重要的应用。
问题描述如下:
在一个二分图中,分别有 $m$ 个左顶点和 $n$ 个右顶点,其中每个左顶点有 $p$ 条边与右顶点相连。
现在,假设一只老鼠从二分图的左侧出发,每次可以随机地选择一个相邻的顶点移动,直到到达二分图的右侧。
请问,老鼠达到右侧的期望步数是多少?
首先将二分图生成一下。
可以用 Python3 中的 NetworkX 库来实现:
import networkx as nx
def complete_bipartite_graph(m, n):
"""
生成一个 m x n 的二分图,其中左侧有 m 个顶点,右侧有 n 个顶点,每个左侧顶点有 n 个右侧顶点相邻。
"""
G = nx.Graph()
G.add_nodes_from(["L{}".format(i) for i in range(1, m + 1)], bipartite=0)
G.add_nodes_from(["R{}".format(i) for i in range(1, n + 1)], bipartite=1)
G.add_edges_from([("L{}".format(i), "R{}".format(j)) for i in range(1, m + 1) for j in range(1, n + 1)])
return G
使用方式:
>>> G = complete_bipartite_graph(2, 3)
>>> G.edges()
[('L1', 'R1'), ('L1', 'R2'), ('L1', 'R3'), ('L2', 'R1'), ('L2', 'R2'), ('L2', 'R3')]
>>> nx.draw(G, with_labels=True)
假设 $E_{i,j}$ 表示在第 $i$ 步时到达左侧第 $j$ 个顶点时达到右侧的期望步数。
$E_{i,j}$ 可以通过 $E_{i-1}$ 计算得出。
由于每个左侧顶点有 $p$ 条边与右侧相邻,因此第 $i$ 步到达左侧第 $j$ 个顶点时,有 $1/p$ 的概率直接到达右侧。
否则,到达左侧第 $j$ 个顶点后,需要从它相邻的右侧顶点中随机选择一个顶点进入下一步,因此有 $1/(pm)$ 的概率移动到右侧相邻的某个顶点,有 $p-1$ 的概率留在原地等待下一步移动。
因此,
$$ E_{i,j}=1+\frac{1}{p}\sum_{k=1}^{n}E_{i-1,k}+\frac{p-1}{p}\cdot E_{i,j} $$
化简得:
$$ E_{i,j}=\frac{1}{p}\sum_{k=1}^{n}E_{i-1,k}+\frac{p}{p-1} $$
边界条件:$E_{0,j}=0$ (二分图的左侧没有起点)
下面是 Python3 中的示例代码:
def tom_and_jerry(G):
"""
给定一个二分图 G,计算老鼠从左侧到达右侧的期望步数
"""
m = len([n for n, p in G.nodes(data="bipartite") if p == 0])
n = len([n for n, p in G.nodes(data="bipartite") if p == 1])
p = len(list(G.neighbors("L1")))
E = [0] * (n+1) # E[0] 不作为左侧的起点
for i in range(1, m+1):
E_next = [0] * (n+1)
for j in range(1, n+1):
E_next[j] = sum([E[k] for k in range(1, n+1) if k != j]) / (p*m) + p / (p-1)
E = E_next
return E[n]