对对碰游戏概率计算

经常在直播中刷到对对碰游戏,好奇概率是怎么分布的,故用python做了个模拟实验。

实验假设:有一大箱含有type_num种颜色的球,现在要随机拿出initial_draws个,每当拿出的球中出现1对相同颜色的2个球就把这2个球放到一边并重新从箱子里拿出1个球(如果出现2对相同颜色的2个球则放一边并重新拿2个球,以此类推),若出现3个一样颜色的球则随机放2个球到一边并重新拿1个球,与此同时可以指定lucky_color_num个幸运颜色,当拿出指定的幸运颜色时,可以再拿1个球,最后所有拿出的球中没有重复的球时过程结束,进行100000次实验模拟最后总共拿出球的可能个数及其对应概率。


以下为实现代码:

可自行设定type_num、initial_draws、lucky_color_num参数得到拿球平均数、众数、中位数及可视化分布图;也可设定ball_cost、transport_cost、other_cost参数计算最终成本。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@File   : ball_experiment.py
@Author : ggb
@Date   : 2024/9/26 14:08 
@Desc   : 拿球模拟实验
"""

import random
from collections import Counter
import tqdm
import matplotlib.pyplot as plt


def calculate_median(data):
    """ 计算中位数 """
    data_sorted = sorted(data)  # 将数据按大小顺序排列
    n = len(data)
    if n % 2 == 1:  # 如果数据个数为奇数
        median = data_sorted[n // 2]
    else:  # 如果数据个数为偶数
        median = (data_sorted[n // 2 - 1] + data_sorted[n // 2]) / 2
    return median


def plot_results(result_dict, simulation_trials):
    """ 画出柱状图 """
    # 设置中文字
    plt.rcParams['font.sans-serif'] = ['SimHei']  # SimHei是黑体
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号'-'显示为方块的问题

    labels, counts = zip(*result_dict.items())

    plt.figure(figsize=(10, 6))
    plt.bar(labels, counts, width=0.8, color='skyblue')

    plt.xlabel('拿球次数')
    plt.ylabel('频率')
    plt.title('次数分布')
    plt.xticks(range(min(labels), max(labels) + 1, max(len(labels) // 10, 1)))  # 设置横坐标的显示间隔

    plt.show()


def with_lucky_color(type_num, initial_draws, simulation_trials, lucky_color_num):
    """ 有幸运颜色 """
    def process_balls(current_balls, colors):
        """ 碰球 """
        counts = Counter(current_balls)
        new_balls = []
        total_draws = 0

        for color, count in counts.items():
            if count == 1:  # 将不重复的球加入新球列表
                new_balls.append(color)
            elif count >= 2:  # 每找出一对相同颜色的球,加入一次新球
                pairs = count // 2
                new_balls.extend([color] * (count % 2))  # 保留多余的一个球,也可以用new_balls.append(color)
                total_draws += pairs
                for _ in range(pairs):  # 对于每对相同颜色的球,加入新的球
                    new_balls.append(random.choice(colors))

        return new_balls, total_draws

    def simulate_draws_with_lucky(colors, initial_draws, lucky_colors):
        total_draws = 0
        current_balls = random.choices(colors, k=initial_draws)
        total_draws += len(current_balls)

        # 继续直到所有球颜色不重复
        while len(set(current_balls)) != len(current_balls):
            lucky_count = sum(color in lucky_colors for color in current_balls)  # 检查当前球中有多少个幸运色
            if lucky_count > 0:
                for _ in range(lucky_count):  # 根据幸运色数量增加相应数量的新球
                    new_ball = random.choice(colors)
                    current_balls.append(new_ball)
                    total_draws += 1
            # 处理碰球逻辑
            current_balls, new_draws = process_balls(current_balls, colors)
            total_draws += new_draws

        return total_draws

    colors = list(f'col{i + 1}' for i in range(type_num))
    lucky_colors = random.sample(colors, lucky_color_num)  # 随机选择幸运颜色
    results = []

    for _ in tqdm.tqdm(range(simulation_trials)):
        results.append(simulate_draws_with_lucky(colors, initial_draws, lucky_colors))

    average_draws = sum(results) / simulation_trials
    print(f"幸运颜色: {lucky_colors}")
    print(f"平均拿球数量: {average_draws}")
    result_dict = Counter(results)
    for key in result_dict:  # 替换次数为比例
        result_dict[key] = round((result_dict[key] / simulation_trials) * 100, 2)
    print(f'拿球情况:{result_dict}')

    # 找出最大、最小拿球数
    max_key = max(result_dict.keys())
    min_key = min(result_dict.keys())
    max_value = result_dict[max_key]
    min_value = result_dict[min_key]
    print(f"最大拿球数是: {max_key}, 占比是: {max_value}")
    print(f"最小拿球数是: {min_key}, 占比是: {min_value}")

    # 计算众数和中位数
    mode = [k for k, v in result_dict.items() if v == max(result_dict.values())]
    median = calculate_median(results)
    print(f'众数: {mode}')
    print(f'中位数: {median}')

    # 画图
    # plot_results(result_dict, simulation_trials)

    return average_draws


if __name__ == "__main__":
    simulation_trials = 100000  # 模拟实验次数
    type_num = 8  # 颜色种类数
    initial_draws = 8  # 拿出数量
    lucky_color_num = 2  # 幸运色数量
    ball_cost = 3  # 每件成本
    transport_cost = 4  # 运输成本
    other_cost = 2  # 其他成本

    average_draws = with_lucky_color(type_num, initial_draws, simulation_trials, lucky_color_num)  # 有幸运色
    total_cost = average_draws * ball_cost + transport_cost + other_cost  # 全部成本
    print(f'全部成本为:{total_cost}')

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值