Project Euler 298
Project Euler 298
题目
Selective Amnesia
Larry and Robin play a memory game involving a sequence of random numbers between \(1\) and \(10\), inclusive, that are called out one at a time. Each player can remember up to \(5\) previous numbers. When the called number is in a player’s memory, that player is awarded a point. If it’s not, the player adds the called number to his memory, removing another number if his memory is full.
Both players start with empty memories. Both players always add new missed numbers to their memory but use a different strategy in deciding which number to remove:
Larry’s strategy is to remove the number that hasn’t been called in the longest time.
Robin’s strategy is to remove the number that’s been in the memory the longest time.
Example game:
| Turn | Called number | Larry’s memory | Larry’s score | Robin’s memory | Robin’s score |
|---|---|---|---|---|---|
| \(1\) | \(1\) | \(1\) | \(0\) | \(1\) | \(0\) |
| \(2\) | \(2\) | \(1,2\) | \(0\) | \(1,2\) | \(0\) |
| \(3\) | \(4\) | \(1,2,4\) | \(0\) | \(1,2,4\) | \(0\) |
| \(4\) | \(6\) | \(1,2,4,6\) | \(0\) | \(1,2,4,6\) | \(0\) |
| \(5\) | \(1\) | \(1,2,4,6\) | \(1\) | \(1,2,4,6\) | \(1\) |
| \(6\) | \(8\) | \(1,2,4,6,8\) | \(1\) | \(1,2,4,6,8\) | \(1\) |
| \(7\) | \(10\) | \(1,4,6,8,10\) | \(1\) | \(2,4,6,8,10\) | \(1\) |
| \(8\) | \(2\) | \(1,2,6,8,10\) | \(1\) | \(2,4,6,8,10\) | \(2\) |
| \(9\) | \(4\) | \(1,2,4,8,10\) | \(1\) | \(2,4,6,8,10\) | \(3\) |
| \(10\) | \(1\) | \(1,2,4,8,10\) | \(2\) | \(1,4,6,8,10\) | \(3\) |
Denoting Larry’s score by \(L\) and Robin’s score by \(R\), what is the expected value of \(|L-R|\) after \(50\) turns? Give your answer rounded to eight decimal places using the format x.xxxxxxxx .
解决方案
这题使用概率动态规划来解决。
本题的随机过程长度固定为 \(T=50\) 回合,每回合报出的数在 \(\{1,...,M\}(M=10)\) 间独立等概率。两位玩家的记忆容量均为 \(N=5\),但更新策略不同:
- Larry:移除“最久没有被报到”的数(LRU)。
- Robin:移除“在记忆里待得最久”的数(FIFO)。
我们记 Larry 的得分为 \(L\),Robin 的得分为 \(R\),目标是计算 \(T\) 回合后 \(\mathbb E[|L-R|].\)
由于策略依赖顺序信息,我们必须把每位玩家的记忆表示为有序序列。
对于 Larry 的记忆:令 \(A=(a_1,\dots,a_p)\) 表示 Larry 当前记忆\((0\le p\le N)\),并约定:
- \(a_1\) 是最久没被报到的数
- \(a_p\) 是最近被报到的数
- 拼接:\((a_1,\dots,a_p)\circ(x)=(a_1,\dots,a_p,x)\)
- 删除:若 \(x=a_k\),则\(A\ominus x=(a_1,\dots,a_{k-1},a_{k+1},\dots,a_p)\)
- 取尾: $ (A)= \[\begin{cases} (a_2,\dots,a_p),&p\ge 1\\ (),&p=0 \end{cases}\] $
那么 Larry(LRU)的更新可写为
\[ U_L(A,x)= \begin{cases} (A\ominus x)\circ(x), & x\in A\\ A\circ(x), & x\notin A\land|A|<N\\ \mathrm{tail}(A)\circ(x), & x\notin A\land|A|=N. \end{cases} \]
令 \(B=(b_1,\dots,b_q)\) 表示 Robin 当前记忆\((0\le q\le N)\),并约定:
- \(b_1\) 是最早进入记忆、因此“驻留时间最长”的数(将被 FIFO 淘汰)
- 命中时 Robin 不改变顺序
则 Robin(FIFO)的更新可写为
\[ U_R(B,x)= \begin{cases} B, & x\in B,\\ B\circ(x), & x\notin B \land |B|<N,\\ \mathrm{tail}(B)\circ(x), & x\notin B \land |B|=N. \end{cases} \]
令分差 \(D=L-R\)。当报到数字为 \(x\) 时,设\(\Delta(A,B,x)=\mathbf 1[x\in A]-\mathbf 1[x\in B]\in\{-1,0,1\}.\)那么每回合分差更新为$ DD+(A,B,x).$因此我们只需在动态规划过程中追踪记忆状态和当前分差。
如果直接把数字当作 \(\{1,...,10\}\),状态数会爆炸。但注意:数字本身没有任何区别,策略只依赖“相等/不等”和“是否在记忆里”的结构。
因此我们把当前出现过的数字做规范化重命名:
- 给当前状态中出现的所有数字依次编号为 \(0,1,...,m-1(m\le 10)\)
- 编号规则取一个确定性的方式即可,例如:从左到右先扫描 \(A\) 再扫描 \(B\),某数字第一次出现就赋予下一个新编号
记这个规范化映射为 \(\varphi\)。于是我们把真实状态映射到一个规范状态:\(A',B'=\varphi(A,B)\)。
这一步的意义在于:任意两个仅仅是把 \(\{1,...,M\}\) 重新置换的状态,在规范化后都会落到同一个 \(S\),从而极大减少状态数。
令 \(f_t(s,d)\) 表示进行了 \(t\) 回合后,处于规范状态 \(s\),且分差为\(d\)的概率。其中\(0\le t\le T,-d\le t\le d\)。
初始状态为两人记忆为空、分差为 0:\(f_0[S_0][0]=1, S_0=((),()).\)
设当前规范状态为 S=(A,B)\(。定义并集大小\)m=|{A}{B}|.$
那么下一次报到的数字分两类:
报到一个已出现过的编号 \(x\in\{0,1,\dots,m-1\}\) 每个具体数字概率都是 \(1/M\),因此这一类一共有 \(m\) 个分支、每个权重 \(1/M\)。
报到一个完全没出现过的数字(即不在 \(A\) 也不在 \(B\) 中) 这样的数字共有 \(M-m\) 个,总概率为 \((M-m)/10\)。并且它们对后续的影响完全等价,因此我们可以把它们聚合成一个“新编号” \(x=m\) 来处理。
于是对任意 \(x\)(包括 \(\{0,...,m-1\}\) 以及聚合的新编号 \(m\)),定义:
- 分差增量:\(\Delta=\Delta(A,B,x)\)
- 更新后的真实序列:\(A' = U_L(A,x), B'=U_R(B,x)\)
- 更新后的规范状态:\(S'=\varphi(A',B')\)
那么转移可以写成:\(f_{t+1}(S',d+\Delta)\leftarrow f_t(s,d)\cdot \Pr(x).\)
其中\(\Pr(x)=\left\{ \begin{aligned} &\dfrac{1}{M} &&\text{if}\quad 0\le x\le m-1\\ &\dfrac{M-m}{M} &&\text{else} \\ \end{aligned}\right.\)
最终,\(T\) 回合后分差分布为\(\displaystyle{P(D=d)=\sum_{S} f_{T}(s,d)}.\)
所以答案为
\[E[|D|]=\sum_{d=-T}^{T}|d| \cdot\sum_{S} f_{T}[S][d].\]
代码
1 | from collections import defaultdict |