📌  相关文章
📜  将给定的球分成具有相同数量不同颜色的两半的概率(1)

📅  最后修改于: 2023-12-03 15:25:21.981000             🧑  作者: Mango

将给定的球分成具有相同数量不同颜色的两半的概率

这个问题可以转化为从给定的球集合中随机选择若干个球,并将它们放入两个集合中,要求这两个集合中有相同数量的不同颜色的球。那么我们可以采用以下算法:

  1. 统计每种颜色球的数量,记为 $n_i$,并计算总共的球数量 $n$;
  2. 如果 $n$ 为奇数,则无法将球集合分成两半,返回概率为 0;
  3. 令 $k=\frac{n}{2}$,即要求每个集合中有 $k$ 个球;
  4. 对于每种颜色,计算出其可以分配到集合 1 中的方案数 $c_i$,然后使用 容斥原理 计算出至少有一种颜色无法满足条件的方案数 $N$;
  5. 返回概率为 $\frac{\binom{k}{n_1} \binom{k}{n_2} \cdots \binom{k}{n_m} - N}{\binom{n}{k} \times 2}$。

其中 $\binom{n}{k}=\frac{n!}{k!(n-k)!}$ 表示从 $n$ 个不同元素中选出 $k$ 个元素的组合数。

参考实现如下(Python 3):

from typing import List

def split_balls(ball_colors: List[str], ball_counts: List[int]) -> float:
    n = sum(ball_counts)
    if n % 2 == 1:
        return 0.0
    k = n // 2
    c = [0] * len(ball_counts)
    for i in range(len(ball_counts)):
        c[i] = sum(1 for j in range(ball_counts[i]+1) if j <= k and k-j <= ball_counts[i])
    N = 0
    for i in range(1 << len(ball_counts)):
        s1, s2 = 0, 0
        for j in range(len(ball_counts)):
            if i & (1 << j):
                s1 += c[j]
                s2 += ball_counts[j] - c[j]
        if s1 != k or s2 != k:
            N += 1
    return (2**(len(ball_counts)-1) * choose(k, *ball_counts) - N) / (2 * choose(n, k))

def choose(n: int, *a: int) -> int:
    res = 1
    for x in a:
        res *= math.comb(n, x)
        n -= x
    return res

其中 ball_colors 是球的颜色列表,ball_counts 是对应颜色球的数量,返回的是将这些球分成具有相同数量不同颜色的两半的概率。

如果要求每种颜色的球数量一样,那么可以直接计算 $\frac{1}{2} \times \frac{n!}{(\frac{n}{2})! (\frac{n}{2})!}$。