0%

抽牌算二十四点有解的概率问题

闲着没事的阿玮提出了这么个问题:在一副扑克牌中任抽4张,算24点,有解的概率是多少。

(注:不允许使用乘方、开根、求导、阶乘等计算。)

于是开始研究:

  1. 计算任意一组数一个的二十四点表达式的算法解决:

上bing搜索,copilot太热情了,直接给出了算法。这个算法的原理就是枚举四个数之间所有可能的数学四则运算表达式,然后一个个算。测试代码发现可用。既然可以使用,那么具体原理不深究了。代码如下(cal24.py):

from itertools import permutations, product

def calculate(a, b, op):
    if op == '+':
        return a + b
    elif op == '-':
        return a - b
    elif op == '*':
        return a * b
    elif op == '/':
        if b != 0:
            return a / b
        else:
            return None

def solve_24(nums):
    operators = ['+', '-', '*', '/']
    for num_perm in permutations(nums):
        for ops in product(operators, repeat=3):
            # Try different parenthesis combinations
            expressions = [
                f"(({num_perm[0]} {ops[0]} {num_perm[1]}) {ops[1]} {num_perm[2]}) {ops[2]} {num_perm[3]}",
                f"({num_perm[0]} {ops[0]} ({num_perm[1]} {ops[1]} {num_perm[2]})) {ops[2]} {num_perm[3]}",
                f"{num_perm[0]} {ops[0]} (({num_perm[1]} {ops[1]} {num_perm[2]}) {ops[2]} {num_perm[3]})",
                f"{num_perm[0]} {ops[0]} ({num_perm[1]} {ops[1]} ({num_perm[2]} {ops[2]} {num_perm[3]}))",
                f"(({num_perm[0]} {ops[0]} {num_perm[1]}) {ops[1]} ({num_perm[2]} {ops[2]} {num_perm[3]}))"
            ]
            for expr in expressions:
                try:
                    if abs(eval(expr) - 24) < 1e-6:
                        return expr
                except ZeroDivisionError:
                    continue
    return 0

# 示例使用
nums = [8, 12, 6, 6]
solution = solve_24(nums)
print(solution)
  1. 一副52张的扑克里面抽牌,抽到一个特定组合的概率是多少:

分析这个问题可以发现,所有的组合可以分类如下:

AAAA型, AAAB型, AABB型, AABC型, ABCD型。

没有其他可能的分布。

计算这5种组合中,特定一种组合出现的概率。

总抽法是comb(52, 4)。(注:comb是qalculate写法,表示组合数; perm是qalculate写法, 表示permutation, 排列数)

AAAA型:1 / comb(52, 4)

AAAB型:考虑花色。A牌是在四个花色中选了3个,B牌是在四个花色里面选了一个。全部考虑顺序。则概率为4/52 * 3/51 * 2/50 * 4/49 * perm(4, 4)。

AABB型:同理。4/52 * 3/51 * 4/50 * 3/49 * perm(4, 4)

AABC型:同理。4/52 * 3/51 * 4/50 * 4/49 * perm(4, 4)

ABCD型:同理。4/52 * 4/51 * 4/50 * 4/49 * perm(4, 4)

具体的数据见代码。

把所有情况列出,所有概率加和,发现略小于1。正常现象,因为python浮点数存在误差。

代码如下(stat.py):

import cal24
pa = 0
for f in range(1, 14):
    for i in range(1, f+1):
        for j in range(1, i+1):
            for k in range(1, j+1):
                if f != i and i != j and j != k:
                    # case1: abcd
                    # p = 2816/4165
                    p = 24*32/812175
                elif f == i and i != j and j != k or f != i and i == j and j != k or f != i and i != j and j == k:
                    # case2: aabc
                    # p = 2112/20825
                    p = 12*8/270725
                elif f == i and i == j and j != k or f != i and i == j and j == k or f == i and i != j and j == k:
                    # case3: aaab
                    # p = 96/20825
                    p = 4*4/270725
                elif f == i and i != j and j == k:
                    # case4: aabb
                    # p = 216/20825
                    p = 6*6/270725
                elif f == i and i == j and j == k:
                    # case5: aaaa
                    # p = 1/20825
                    p = 1/270725
                expr = cal24.solve_24([f, i, j, k])
                print(f, i, j, k, expr)
                if expr:
                    pa += p


print(pa)

执行python stat.py > 24.txt可以得到:

1 1 1 1 0
2 1 1 1 0
2 2 1 1 0
2 2 2 1 0
2 2 2 2 0
3 1 1 1 0
3 2 1 1 0
3 2 2 1 0
3 2 2 2 (3 * (2 + 2)) * 2
3 3 1 1 0
3 3 2 1 (3 * (3 + 1)) * 2
3 3 2 2 ((3 + 3) * (2 + 2))
3 3 3 1 ((3 + 3) * (3 + 1))
3 3 3 2 (3 + (3 * 3)) * 2
3 3 3 3 ((3 * 3) * 3) - 3
4 1 1 1 0
4 2 1 1 0
4 2 2 1 4 * (2 * (2 + 1))
4 2 2 2 ((4 + 2) * (2 + 2))
4 3 1 1 4 * (3 * (1 + 1))
4 3 2 1 4 * ((3 + 2) + 1)
4 3 2 2 ((4 + 2) + 2) * 3
4 3 3 1 (4 * (3 + 3)) * 1
4 3 3 2 0
4 3 3 3 ((4 + 3) * 3) + 3
4 4 1 1 4 * ((4 + 1) + 1)
4 4 2 1 ((4 + 4) * (2 + 1))
4 4 2 2 (4 + (4 * 2)) * 2

……

13 13 11 4 0
13 13 11 5 0
13 13 11 6 0
13 13 11 7 0
13 13 11 8 0
13 13 11 9 ((13 + 13) - 11) + 9
13 13 11 10 0
13 13 11 11 0
13 13 12 1 ((13 / 13) + 1) * 12
13 13 12 2 ((13 - 13) + 12) * 2
13 13 12 3 12 * (3 - (13 / 13))
13 13 12 4 0
13 13 12 5 0
13 13 12 6 13 + (13 - (12 / 6))
13 13 12 7 0
13 13 12 8 0
13 13 12 9 0
13 13 12 10 ((13 + 13) - 12) + 10
13 13 12 11 13 + ((13 - 12) * 11)
13 13 12 12 ((13 - 13) + 12) + 12
13 13 13 1 0
13 13 13 2 (13 - (13 / 13)) * 2
13 13 13 3 0
13 13 13 4 0
13 13 13 5 0
13 13 13 6 0
13 13 13 7 0
13 13 13 8 0
13 13 13 9 0
13 13 13 10 (13 + (13 / 13)) + 10
13 13 13 11 ((13 + 13) - 13) + 11
13 13 13 12 ((13 + 13) / 13) * 12
13 13 13 13 0
0.8012448056145496

可以看到,有解的概率大概是80%多点。

后来发现,可以参考知乎这篇回答
他计算的结果和我相近。