量化面试绿皮书:1. 海盗分金博弈
文中内容仅限技术学习与代码实践参考,市场存在不确定性,技术分析需谨慎验证,不构成任何投资建议。
1. 海盗分金博弈
五个海盗抢走了一个装满 100 枚金币的箱子。作为一群民主的海盗,他们同意以下分配战利品的方法:最资深的海盗将提议分配硬币。所有的海盗,包括最资深的海盗,都会进行投票。如果至少50%的海盗(在本例中为3名海盗)接受提议,则黄金按提议进行分配。如果没有,则将最高级的海盗喂给鲨鱼,然后从下一个最高级的海盗开始这个过程.…重复这个过程,直到一个计划被批准。你可以假设所有的海盗都是完全理性的:他们首先想活下去,其次是获得尽可能多的金子。最后,作为嗜血的海盗,如果在其他方面均等的结果之间做出选择,他们希望船上的海盗更少。
Q: 金币最终将如何分配?
A: 根据海盗的分配规则和理性行为(优先生存、其次最大化金币、最后在金币相同情况下希望船上海盗更少),我们从海盗数量最少的情况开始逆向推理,逐步推导出五个海盗时的分配方案。
一、推理过程
-
一个海盗(只剩 P5):
- P5 提出方案,拿走所有 100 枚金币。
- 投票:仅 P5 一人,100% 支持,方案通过。
- 分配:P5 得到 100。
-
两个海盗(P4 和 P5):
- P4 提出方案。需要至少 50% 支持(2 的 50% 为 1,即至少一票支持)。
- P4 会投票支持自己的方案,因此即使 P5 反对,方案也能通过(P4 一票支持即满足条件)。
- P4 可以提出:自己拿 100,P5 拿 0。
- 分配:P4 得到 100,P5 得到 0。
-
三个海盗(P3、P4、P5):
- P3 提出方案。需要至少 50% 支持(3 的 50% 为 1.5,向上取整需至少 2 票支持)。
- P3 会投票支持自己,因此还需要一票支持。
- 如果方案被拒绝,P3 被淘汰,进入两个海盗情况(P4 得 100,P5 得 0)。
- P5 在拒绝情况下得 0,因此 P3 可以贿赂 P5:给 P5 1 枚金币,P5 会支持(1 > 0)。
- P4 在拒绝情况下得 100,但 P3 无需贿赂 P4(成本高)。
- 方案:P3 得 99,P4 得 0,P5 得 1。
- 投票:P3 支持、P5 支持(两票支持),方案通过。
-
四个海盗(P2、P3、P4、P5):
- P2 提出方案。需要至少 50% 支持(4 的 50% 为 2,需至少 2 票支持)。
- P2 会投票支持自己,因此还需要一票支持。
- 如果方案被拒绝,P2 被淘汰,进入三个海盗情况(P3 得 99,P4 得 0,P5 得 1)。
- P4 在拒绝情况下得 0,因此 P2 可以贿赂 P4:给 P4 1 枚金币,P4 会支持(1 > 0)。
- P3 在拒绝情况下得 99,贿赂成本高;P5 在拒绝情况下得 1,如果给 1 枚,金币相同但海盗更少时 P5 可能反对(嗜血偏好),因此贿赂 P4 更划算。
- 方案:P2 得 99,P3 得 0,P4 得 1,P5 得 0。
- 投票:P2 支持、P4 支持(两票支持),方案通过。
-
五个海盗(P1、P2、P3、P4、P5):
- P1 提出方案。需要至少 50% 支持(5 的 50% 为 2.5,向上取整需至少 3 票支持)。
- P1 会投票支持自己,因此还需要两票支持。
- 如果方案被拒绝,P1 被淘汰,进入四个海盗情况(P2 得 99,P3 得 0,P4 得 1,P5 得 0)。
- P3 在拒绝情况下得 0,因此 P1 可以贿赂 P3:给 P3 1 枚金币,P3 会支持(1 > 0)。
- P5 在拒绝情况下得 0,因此 P1 可以贿赂 P5:给 P5 1 枚金币,P5 会支持(1 > 0)。
- P2 在拒绝情况下得 99,贿赂成本高;P4 在拒绝情况下得 1,如果给 1 枚,金币相同但海盗更少时 P4 可能反对,因此贿赂 P3 和 P5 更划算。
- 方案:P1 得 98,P2 得 0,P3 得 1,P4 得 0,P5 得 1。
- 投票:P1 支持、P3 支持、P5 支持(三票支持),方案通过。
二、最终分配
在五个海盗的情况下,金币分配如下:
- 最资深海盗(P1):98 枚金币
- 第二资深海盗(P2):0 枚金币
- 第三资深海盗(P3):1 枚金币
- 第四资深海盗(P4):0 枚金币
- 最年轻海盗(P5):1 枚金币
总金币:98 + 0 + 1 + 0 + 1 = 100 枚。
三、代码实现
要解决海盗分金问题,我们可以使用逆向归纳法(从最少海盗数开始逐步推导到5个海盗),并通过Python代码实现这一逻辑。以下是完整的代码实现:
def pirate_allocation(total_pirates):# 存储不同海盗数量的分配方案allocations = {}# 基础情况:1个海盗allocations[1] = {1: 100}# 从2个海盗开始逐步计算到指定数量的海盗for n in range(2, total_pirates + 1):# 获取n-1个海盗时的分配方案prev_alloc = allocations[n-1]# 计算每个非提议者海盗在下一轮(n-1海盗)中的收益next_round_gains = []for i in range(2, n + 1): # 当前海盗编号i(从2到n)# 在下一轮中,当前海盗的编号变为i-1gain_in_next = prev_alloc.get(i-1, 0)next_round_gains.append((i, gain_in_next))# 按下一轮收益升序排序,收益相同则按海盗编号升序next_round_gains.sort(key=lambda x: (x[1], x[0]))# 计算通过方案所需总票数(向上取整)total_votes_needed = (n + 1) // 2votes_to_buy = total_votes_needed - 1 # 需要收买的海盗数# 初始化当前分配方案(所有海盗0金币)curr_alloc = {pirate: 0 for pirate in range(1, n + 1)}total_cost = 0# 收买代价最小的votes_to_buy个海盗for idx in range(votes_to_buy):pirate_id, next_gain = next_round_gains[idx]bribe_amount = next_gain + 1 # 必须比下一轮收益多至少1金币curr_alloc[pirate_id] = bribe_amounttotal_cost += bribe_amount# 剩余金币归提议者(1号海盗)curr_alloc[1] = 100 - total_cost# 存储当前海盗数的分配方案allocations[n] = curr_allocreturn allocations[total_pirates]# 计算5个海盗的分配方案
result = pirate_allocation(5)
print("金币分配方案(海盗编号从最资深到最年轻海盗):")
for pirate, coins in sorted(result.items()):print(f"海盗{pirate}: {coins}枚金币")
输出结果:
金币分配方案(海盗编号从最资深到最年轻海盗):
海盗1: 98枚金币
海盗2: 0枚金币
海盗3: 1枚金币
海盗4: 0枚金币
海盗5: 1枚金币
代码逻辑详解
- 基础情况处理:当只有1个海盗时,他独占全部100枚金币。
- 逆向归纳:
- 从2个海盗开始逐步计算到5个海盗。
- 对于每个海盗数量
n
:- 获取
n-1
个海盗时的分配方案(作为下一轮基准)。 - 计算每个非提议者海盗(编号2~n)在下一轮中的预期收益。
- 将这些海盗按下一轮收益排序(收益低者优先,收益相同则按编号升序)。
- 计算通过方案所需票数:
ceil(n/2)
(通过整数除法(n+1)//2
实现)。 - 提议者(1号海盗)需要收买
ceil(n/2)-1
个海盗,选择代价最小的海盗(给下一轮收益+1
金币)。 - 剩余金币归提议者所有。
- 获取
- 输出结果:返回5个海盗时的分配方案。
关键点说明
- 理性行为:海盗优先考虑生存,其次最大化金币,最后在同等收益下希望减少海盗。
- 投票逻辑:提议者只需确保获得刚好足够的赞成票(包括自己的一票)。
- 收买策略:提议者会收买在下一轮收益最低的海盗,用最小代价(
下一轮收益+1
金币)换取支持。 - 嗜血特性:通过严格大于下一轮收益的出价规避嗜血影响(避免海盗因嗜血而投反对票)。
风险提示与免责声明
本文内容基于公开信息研究整理,不构成任何形式的投资建议。历史表现不应作为未来收益保证,市场存在不可预见的波动风险。投资者需结合自身财务状况及风险承受能力独立决策,并自行承担交易结果。作者及发布方不对任何依据本文操作导致的损失承担法律责任。市场有风险,投资须谨慎。