"""
从目标集合 S 中有放回地抽取 n 次, 计算子集 s 中的每个元素至少抽中 1 次的概率.
目标集合 S 为 [A, A, B, C...] 这种有重复的集合.
集合 s 为 S 的子集, 且 s 中不包含重复元素.
"""
import time
from typing import List
from random import Random
from functools import reduce
def frac(n):
"""
n!
"""
if n <= 1:
return 1
r = 1
while n > 1:
r *= n
n -= 1
return r
def calculate_probability(choices: List[str], targets: List[str], num_draw: int) -> float:
"""
计算此概率:从目标集合 choices 中有放回地抽取 num_draw 次, targets 集合中每个元素至少抽中一个.
targets 必须是 choices 的子集
"""
num_choice = len(choices)
num_target = len(targets)
left_draw = num_draw - num_target
targets = {t: choices.count(t) for t in targets}
if left_draw < 0:
print(f"0.0%")
return 0.0
# 目标集合每个选项出现的次数相乘, 为目标集合的可能数
count = reduce(lambda x, y: x * y, targets.values())
# 如果抽取次数大于目标集合个数, 则还可以再抽取若干次任意元素
count *= (num_choice ** left_draw)
# 以上获得的可能数是固定顺序下的, 所以总的可能数需要再乘以 num_draw 的阶乘
count *= frac(num_draw)
# 每次抽取有 num_choice 次可能, 总共抽取 num_draw 次
total = num_choice ** num_draw
p = round(count / total * 100, 2)
print(f"{p}%")
return p
def observe_probability(choices, targets, num_draw) -> float:
"""
观察此概率:从目标集合 choices 中有放回地抽取 num_draw 次, targets 集合中每个元素至少抽中一个.
targets 必须是 choice 的子集.
"""
rand = Random()
rand.seed(time.time() * 1000)
count = 0
total = 1000000
# 模拟 total 次
for num_test in range(total):
targets = {t: 0 for t in targets}
# 每次模拟抽取 num_draw 次
for draw in range(num_draw):
choice = rand.choice(choices)
if choice in targets:
targets[choice] += 1
values = list(targets.values())
match = all(map(lambda x: x >= 1, values))
if match: # 所有目标都抽到, 计数加 1
count += 1
p = round(count / total * 100, 2)
print(f"{p}%")
return p
def test():
choices = [
"A", "A", "A", "A",
"B", "B", "B", "B",
"C", "C", "C", "C",
"D",
"E",
]
targets = ["A", "B", "C", "D"]
num_draw = 5
observe_probability(choices, targets, num_draw)
calculate_probability(choices, targets, num_draw)
if __name__ == "__main__":
test()
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.