目录
6. 一次游戏通关带来的收入
问题描述
我们考虑一个在游戏相关岗位面试常见的问题: 一次游戏通关带来的收入
一个游戏有四关,通过概率依次为0.9, 0.7, 0.6, 0.5。
第一关不收费,第二到四关每次收费分别为1块, 2块, 3块。
用户每玩一次都会无限打下去直至通关,通关后用户可以提现 10 块钱作为奖励。
问: 公司可以在每次用户游戏中平均挣多少钱。
思路与算法
在前一小节中我们通过【有向图+期望DP+高斯消元】这个方法论解决了装备问题,题目描述和解法可以参考上一篇(Leetcode概率题面试突击系列1~5)第5题。
下面我们在装备升级问题上做一些变化,我们保持图结构不变,但是我们把期望 DP 转移方程中的边权改为节点的权。
我们首先考虑【公司可以在每次用户游戏中收费多少钱】,然后减去 10 块钱的奖励就是挣的钱。上图中,每个节点上的数值表示在该节点上没停留一次需要缴纳的费用。
考虑各节点的期望,可以得到状态转移方程组如下:
求解以上方程组可以得到:,公司可以在每次用户游戏中平均挣6块钱。
7. 祝你好运
问题描述
“祝你好运”一种赌博游戏,经常在嘉年华和赌场玩。
玩家可以在 1, 2, 3, 4, 5, 6 中的某个数上下注。然后投掷 3 个骰子。
如果玩家下注的数字在这三次投掷中出现了 x 次,则玩家获得 x 倍原始投注资金,并且原始投注资金也会返还,也就是总共拿回 (x + 1) 倍原始投注资金。如果下注的数字没有出现,则原始投注资金不会返还。
问:每单位原始投注资金,玩家期望会输多少?
思路与算法
玩家期望与玩家押注哪个数字无关。
每次掷骰子投中所压注的数字的概率为,每一轮(指一次押注三次投掷)玩家投掷结果有4种情况:
- 中0次,概率为
,拿回0倍
- 中1次,概率为
,乘以3是因为可能是第1次中,可能是第2次中,可能是第3次中。拿回2倍
- 中2次,概率为
,拿回3倍
- 中3次,概率为
,拿回4倍
以上4个概率之和为1,这个可以作为一个基准,用于确认概率计算是否正确的一个必要条件。
因此,总的期望是,所以玩家期望每单位原始投注资金会输
.
蒙特卡洛仿真
以下代码,直接CV自下面的链接。
import numpy as np
def game():
x = 0
for _ in range(3):
if np.random.randint(1, 7) == 1:
# 以等于1为判决条件并无必然性,可以是1~6中的任何一个数字,得到的结果一样
x += 1
if x == 0:
return 0
return x + 1
# 每次测试中玩家共押注1e6次
def test():
T = int(1e6)
total_income = 0
for t in range(T):
total_income += game()
print("expected loss: {:.6f}".format((T - total_income) / T))
# 10次测试
for i in range(10):
test()
8. 仓促的决斗
问题描述
在某一天的决斗场,有两个人会在 5 点到 6 点之间随机的某一分钟到来,然后 5 分钟后离开。
如果某一时刻这两个人同时在场,会发生决斗。
问:这两个人发生决斗的概率是多少?
思路与算法
如上图所示,横坐标表示A到达时间,纵坐标表示B到达的时间。因此,只有在两者到达的时间落在中间涂斜线条纹带时才满足决斗发生条件。
因此,决斗发生的概率为中间涂斜线条纹带的面积于整个正方形面积之比,即:
蒙特卡洛仿真
import numpy as np
from multiprocessing import Pool
def test(T):
array_arrival_times = 60 * np.random.random(size=(T, 2))
array_diff = np.abs(np.diff(array_arrival_times))
return np.mean(array_diff <= 5)
T = int(1e7)
args = [T] * 8
pool = Pool(8)
ts = pool.map(test, args)
print("发生决斗的概率: {:.6f}".format(sum(ts) / len(ts)))
作者:FennelDumplings
链接:https://leetcode.cn/leetbook/read/probability-problems/neb04t/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
顺便学习以下python pool包的用法(我自己写这个代码是决计写不出来,因为我根本不知道有pool这么一个包)。
Pool类可以提供指定数量的进程供用户调用,当有新的请求提交到Pool中时,如果池还没有满,就会创建一个新的进程来执行请求。如果池满,请求就会告知先等待,直到池中有进程结束,才会创建新的进程来执行这些请求。
以上代码就相当于开了8个进程同时进行,然后调用map函数将8个子进程映射为8次test的调用。
9. 帮助赌徒戒赌
问题描述
布朗沉迷于轮盘赌,并且强迫症地总是在 13 这个数字上押注 1 美元。
轮盘赌有 38 个等概率的数字,如果玩家押注的数字出现,则拿到 35 倍投注金额,加上原始投注金额,共 36 倍,如果押注的数字没出现,则损失原始投注金额。
好朋友为了帮助布朗戒赌,设计了一个赌注,与布朗等额投注 20 美元(好朋友和布朗均需要出 20 美元),赌以下事情:布朗在第 36 次游戏完成之后会亏钱(如果果真亏钱了,则朋友得到双方的筹码共 40 美元,否则布朗得到这 40 美元)。
问:这个戒赌方案是否有效(如果布朗能挣钱,说明无效,如果布朗会亏钱则说明有效)。
思路与算法
单纯地就轮盘赌而言,每次投注布朗押中的概率为(1/38),获利35美元;押不中的概率为(37/38),损失1美元。因此,布朗的轮盘赌的预期收益为:。从概率上来说,布朗肯定是要输钱的。
但是具体到本戒赌方案,布朗连续押注36次,亏钱的概率是多大呢?注意,这个戒赌方案中,除了投注以及押中所得,还有与朋友的对赌。布朗36次中:
- 如果押中一次,可以赢20美元(轮盘赌本身平局,赚朋友的20美元);
- 押中一次以上布朗肯定也是赢钱;
- 如果一次都没有押中则布朗亏钱(总计亏56美元)
一次都没有押中的概率为
押中一次的概率为
布朗亏钱的概率只有0.3829。但是仅根据这一概率还不足以说明,朋友设计的这一戒赌方案是否无效。要计算在这一戒赌方案中布朗的总体盈亏期望。
在这个戒赌方案中,布朗的收获来自于两部分。
第一部分来自于轮盘赌本身,投注押中的概率为,每次投注的盈亏期望是
,36次投注的总的盈亏期望是
。
第二部分来自于朋友。如果布朗一次都没有押中,则亏20元,至少押中一次(时)则赢20元。根据以上计算得到的概率可知,这一部分的盈亏期望是:
因此总的盈亏期望为:
所以看总体盈亏期望,布朗可以赚2.7893美元,本戒赌方案无效。
蒙特卡洛仿真
from multiprocessing import Pool
import numpy as np
p = 1/38
def game1():
# 1 次游戏,返回游戏结束拿回的钱
if np.random.rand() < p:
return 36
return 0
def game36():
# 36 次游戏,返回36次游戏结束拿回的钱
w = 0
for _ in range(36):
w += game1()
if w >= 36:
return w + 40
return w
def test(T):
np.random.seed()
total = 0
for _ in range(T):
total += game36()
return total / T
T = int(1e6)
args = [T] * 8
pool = Pool(8)
incomes = pool.map(test, args)
for x in incomes:
print("Average Income: {:.6f}".format(x))
作者:FennelDumplings
链接:https://leetcode.cn/leetbook/read/probability-problems/nejvsr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
10. 较短的一节木棍
问题描述
一根棍子,随机选个分割点,将棍子分为两根。
a. 较小的那一根的长度平均是多少?
b. 较小的那一根与较长的那一根的比例平均是多少?
思路与算法
记棍子全长为1。随机分割点的位置距左端距离为。
服从均一分布
。
记较短的那一截长度为y,叫长的那一截的长度为z
当,
当,
以上的计算是凭直觉的粗糙运算,远远不够严谨。严谨的论述如下(摘自官解供参考学习):
蒙特卡洛仿真
import numpy as np
def pyfunc(x):
"""
0 < x < 1
"""
if x > 0.5:
x = 1 - x
return x
def pyfunc2(x):
"""
0 < x < 0.5
"""
return x / (1 - x)
func = np.frompyfunc(pyfunc, 1, 1)
func2 = np.frompyfunc(pyfunc2, 1, 1)
def test(T):
X = np.random.uniform(0, 1, T)
X = func(X)
Y = func2(X)
return (np.mean(X), np.mean(Y))
T = int(1e7)
m1, m2 = test(T)
print("较短的木棍的期望长度: {:.6f}".format(m1))
print("较短的木棍与较长的木根长度比值的期望: {:.6f}".format(m2))
作者:FennelDumplings
链接:https://leetcode.cn/leetbook/read/probability-problems/nefags/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
回到力扣题解总目录:Leetcode每日一题总目录(动态更新。。。)
参考:https://leetcode-cn.com/leetbook/read/probability-problems