Blackjack 数学推导

去拉斯维加斯接触到这个东西, 查了下数学还挺有意思的 (没那么难也没那么简单那种), 一开始凭借直觉输了 70 USD, 于是回头推导了一下. 截图来自: 247blackjack.com.

1. 游戏规则

扑克 52 张不含大小王. 2-10 为对应数字点数. J,Q,K 为 10 点, A 可以是 1 可以是 11 点, 哪个有利选哪个. 手里有 A 可以按 11 算的叫软牌 (soft, 如 A+6 是软 17), 否则叫硬牌 (hard, 如 A+6+9 是硬 16, A 如果是 11 就超过 21 点).

首先, 你看到自己的两张牌以及庄家的一张牌, 然后你需要不断决定是否继续要牌, 你需要保证自己的点数之和尽可能接近 21 点但是不能超过 21 点, 否则爆牌 (bust). 你先爆先输, 即使庄家随后也爆牌.

你或者庄家可能在决策之前达到 Blackjack: 手上一张 A 和一张 10/J/Q/K. 这时立刻结算. 谁有 Blackjack 谁赢, 都有则平局. 接下来的决策双方将不会有 Blackjack.

然后轮到庄家亮出底牌然后行动. 庄家没有任何选择权, 如果点数之和小于 17 点必须要牌, 大于 17 点就必须停牌 (不管是软牌还是硬牌, 比如 A+8 就要停牌, 是软 19 点), 如果点数等于 17 点, 分成软 17 点 (至少有一个 A 可以是 11) 和硬 17 点 (如果有 A, A 必须是 1), S17 (soft 17) 规则在软 17 点直接停牌, H17 (hard 17) 在硬 17 点才停牌, 所以 H17 在 A+6 这种还需要要牌, 这时候如果来一张 4 就构成 21 点, 所以 H17 对庄家更加有利. 庄家如果超过 21 点也是爆牌.

当两方都结束的时候, 比较:

21 > 20 > 19 > 18 > … > 4 > 庄家爆牌 > 玩家爆牌

具体步骤:

步骤 图示
1. 下注 (Bet)
选筹码确认下注金额, 设为 xx.
image.png
2. 发牌 (Deal)
你得到两张明牌, 庄家一张明牌 (upcard) + 一张底牌. 若你头两张恰好是 A + 10 点牌, 即 Blackjack, 只有庄家同为 Blackjack 才平局, 否则你赢
image.png
3. (可能出现) 保险 (Insurance)
庄家明牌是 A 时会先询问保险: 至多押主注一半 12x\frac{1}{2}x, 赌庄家底牌是 10 点牌, 如果是的话, 你取回下注筹码并额外获得 xx 赌注 (1 赔 2, 总金额+x+x), 否则总金额 12x-\frac{1}{2}x (下注金额另算)
image.png
4. 你的回合: 要牌 / 停牌 (Hit / Stand)
每回合要么要一张牌, 要么停牌, 点数一旦超过 21 就爆牌立即输掉赌注, 停牌后开始庄家的回合.
image.png
5. (可能出现) 第一回合: 双倍 / 分牌 (Double / Split)
第一回合还可以出现:
1. Double: 只在头两张时可用, 赌注 x2, 只再发一张, 自动停牌.
2. Split: 头两张点数一样的时候 (10JQK 是一样的牌), 补一份等额注, 把牌拆成两份各自进行要牌或者停牌, 分别与庄家比. 分出的手一般仍可 Double, 分出的一对 A 每边只补一张.
image.png
6. 庄家回合与结算
你停牌后庄家翻开底牌, 机械执行固定规则: 不到 17 必须拿, 到 17 及以上必须停. 硬 17 规则到软 17 再拿一张.

按照这个规则比较: “Blackjack > 普通 21 点 (至少 3 张牌的 21 点) > 20 > 19 > 18 > … > 4 > 庄家爆牌 > 玩家爆牌”.

输了下注 x 全给庄家 (-x), 平了不亏不赚 (0), 赢了如果不是 Blackjack 获得 (+x) 赌注, 如果是 Blackjack 则获得 +1.5x (2 赔 3) 或者 +1.2x (5 赔 6) 的赌注.
image.png

2. 基本策略表

玩 Blackjack 可以通过查表.

先看是不是对子, 如果有, 看第下面表"你"的第一列, 然后看横排第一排庄家是哪张牌, “分” 代表 split, “要” 代表 hit, “停” 代表 stand, “双” 代表 double.

对子

你\庄家 2 3 4 5 6 7 8 9 10 A
A,A
2,2 / 3,3
4,4
5,5
6,6
7,7
8,8
9,9
10,10

在这之后, 或者如果不是对子, 看你是不是软牌. 如果存在一张 A 可以算 11, 也就是存在软牌, 则看下面这个表.

软牌 (A + 一张, A 算 11)

你\庄家 2 3 4 5 6 7 8 9 10 A
A+2 要/双 (1-8 副牌) 双/要 (\geq 3 张牌)
A+3 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌)
A+4 双/要 (S17 或 \geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌)
A+5 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌)
A+6 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌)
A+7 双/停 (S17 或 \geq 3 张牌) 双/停 (\geq 3 张牌) 双/停 (\geq 3 张牌) 双/停 (\geq 3 张牌) 双/停 (\geq 3 张牌)
A+8 双/停 (S17 或 \geq 3 张牌)
A+9

都不是就看硬牌表.

硬牌 (没有 A, 或 A 只能算 1)

你\庄家 2 3 4 5 6 7 8 9 10 A
<=8
9 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌)
10 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌)
11 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (\geq 3 张牌) 双/要 (S17 或 \geq 3 张牌)
12
13-15
16 要/停 (\geq 3 张牌)
>=17

注意:

  • 斜杠格是争议格. 斜杠前为本文主模型 (无限副牌, H17) 的最优.
  • A+2 对 5 在 1-8 副牌下选双, 在无限副牌选要, 16 对 10 在 16 由三张以上牌组成时选停
  • 11 对 A, A+4 对 4, A+7 对 2, A+8 对 6 在 S17 规则选斜杠后的动作.
  • 软牌里的"双"如果当时不允许双倍 (超过两张牌组成): A+7, A+8 改停, 其余改要. 硬牌如果不允许双倍, 则改为要牌.
  • 保险这笔独立赌注的期望恒为负, 永远不要选保险.

3. 表格的数学推导

3.1 两个约定

约定 1 (发牌概率): 13 种点数里, P(A)=P(2)==P(9)=1130.077P(A)=P(2)=\cdots=P(9)=\tfrac{1}{13}\approx 0.077; 10, J, Q, K 合并成"10 点牌", P(10)=4130.308P(10)=\tfrac{4}{13}\approx 0.308. 本文主模型用无限副牌近似: 每次抽牌概率不变, 彼此独立.

约定 2 (期望): 一个局面的期望 EE = 每种结果的盈亏 x 该结果的概率, 全部加起来, 单位是"赌注倍数". E=+0.332E=+0.332 的含义是: 这个局面重复无数次, 平均每次每押 $1 赚 33 美分, 代表平均盈利.

3.2 决策树

整张表的推导是对每一格 (自己的手牌和对面的手牌), 把可选动作 (停/要/双/分) 的期望各算一遍, 选择期望最大的为下一步, 可以看成一个决策树. 比如你的手牌为 11, 庄家有一张 6, 决策树如下:

%%{init: {"flowchart": {"nodeSpacing": 10, "rankSpacing": 40}} }%%
graph TD
    R{"11 对 6"}
    R -->|"停<br>-0.121"| S0["庄不爆必输"]
    R -->|"要 x1<br>+0.332"| H(("抽<br>加权和<br>= +0.332"))
    R -->|"双 x2<br>+0.665"| D["十支与'要'第一层相同, 但强制停牌<br>整体 x2: 2 x 0.332 = +0.665"]
    H -->|"1/13"| aA(("A 12<br>-0.121"))
    H -->|"1/13"| a2(("2 13<br>-0.121"))
    H -->|"1/13"| a3(("3 14<br>-0.121"))
    H -->|"1/13"| a4(("4 15<br>-0.121"))
    H -->|"1/13"| a5(("5 16<br>-0.121"))
    H -->|"1/13"| a6(("6 17<br>-0.006"))
    H -->|"1/13"| a7(("7 18<br>+0.223"))
    H -->|"1/13"| a8(("8 19<br>+0.453"))
    H -->|"1/13"| a9(("9 20<br>+0.678"))
    H -->|"4/13"| a10(("10 21<br>+0.894"))
    a5 --> R2{"16 对 6<br>只展开这一支"}
    R2 -->|"停<br>-0.121"| S2["取最大: 停<br>把 -0.121 回填进上面的圈"]
    R2 -->|"要<br>-0.443"| H2(("抽<br>加权和<br>= -0.443"))
    H2 --> bA(("A 17<br>-0.006")) & b2(("2 18<br>+0.223")) & b3(("3 19<br>+0.453")) & b4(("4 20<br>+0.678")) & b5(("5 21<br>+0.894")) & b6(("6 爆<br>-1")) & b7(("7 爆<br>-1")) & b8(("8 爆<br>-1")) & b9(("9 爆<br>-1")) & b10(("10 爆<br>-1"))
    class b6,b7,b8,b9,b10 bust
    classDef bust stroke:#c0392b,color:#c0392b;

3.3 庄家的最终分布

庄家没有选择权, 所以从任何点数出发, 之后的所有路径都可以穷举, 每条路径的概率是路上每张牌概率的乘积.

定义 f(t)f(t) = 庄家现在是硬 tt 点时, 最终停在 17, 18, 19, 20, 21 或爆牌的概率分布. 这个函数返回一个概率分布.

边界: tt 为 17 到 21 的硬牌时庄家已经停了, f(t)f(t) 就是"停在 tt 的概率是 1, 其余是 0".
递推: t<17t<17 时庄家必须再抽一张 cc:

f(t)=cP(c)f(t+c)(t+c>21 记为爆)f(t)=\sum_{c} P(c) f(t+c) \qquad (t+c>21 \text{ 记为爆})

手算 f(16)f(16): 抽 A, 2, 3, 4, 5 分别变 17-21, 各 113\tfrac{1}{13}. 抽 6, 7, 8, 9 (413\tfrac{4}{13}) 或 10 (413\tfrac{4}{13}) 都爆, 合计 813\tfrac{8}{13}. 所以 f(16)f(16) 是 17-21 各 0.077, 爆 0.615.

手算 f(15)f(15): 抽 2-6 分别变 17-21, 各 113\tfrac{1}{13}. 抽 7, 8, 9, 10 爆, 合计 713\tfrac{7}{13}. 抽 A 变 16, 接上面的 f(16)f(16) 继续.

P(停 17)=113+113×113=0.083P()=713+113×813=0.586P(\text{停 }17)=\tfrac{1}{13}+\tfrac{1}{13}\times \tfrac{1}{13}=0.083 \qquad P(\text{爆})=\tfrac{7}{13}+\tfrac{1}{13}\times \tfrac{8}{13}=0.586

所以 17-21 同为 0.083, 爆 0.586.

软牌多一条处理: 比如庄家 6 + A 是软 17, H17 规则必须要牌. 抽到 5 变 22, 把 A 改算 1 变硬 12, 接 f(12)f(12) 继续.

从明牌出发抽底牌, 一层层递推到底, 就得到下面的全表. 需要剔除庄家为 Blackjack 的情况, 因为当玩家或者庄家任何一方有 Blackjack 都自动结算, 不会出现决策.

庄家明牌 17 18 19 20 21
2 .130 .137 .131 .126 .120 .357
3 .126 .132 .127 .122 .116 .377
4 .122 .127 .123 .118 .113 .397
5 .118 .123 .118 .114 .109 .418
6 .115 .115 .115 .110 .106 .439
7 .369 .138 .079 .079 .074 .262
8 .129 .359 .129 .069 .069 .245
9 .120 .120 .351 .120 .061 .228
10 .121 .121 .121 .371 .037 .230
A .083 .207 .207 .207 .096 .201

(H17, 无限副牌, A 和 10 两行已剔除庄家 Blackjack.)

从这个表可以看出, 庄家明牌 2-6 时爆牌率 36%-44%, 明牌 7-A 时只有 20%-26%. 一个常见说法"庄家小牌就收手让它自己爆, 庄家大牌就必须搏"就来自这一列.

3.4 停牌的期望

你停在 TT 点后只剩四种结果, 每种的盈亏是确定的: 庄家爆 +1+1. 庄家没爆但比你小 +1+1. 同点 00. 庄家比你大 1-1. 按约定 2:

E(T)=P(庄爆)+P(<T)P(>T)E_{\text{停}}(T)=P(\text{庄爆})+P(\text{庄}<T)-P(\text{庄}>T)

平局那一项乘 0 消掉了. 代入庄家 6 的分布 (爆 0.439, 17, 18, 19, 20, 21 分别为 0.115, 0.115, 0.115, 0.110, 0.106):

你停在 计算 EE_{\text{停}}
12-16 庄家不爆最少 17, 必比你大: 0.4390.5610.439-0.561 0.121-0.121
17 0.439(0.115+0.115+0.110+0.106)0.439-(0.115+0.115+0.110+0.106) 0.006-0.006
18 0.439+0.1150.3310.439+0.115-0.331 +0.223+0.223
19 0.439+0.2300.2160.439+0.230-0.216 +0.453+0.453
20 0.439+0.3450.1060.439+0.345-0.106 +0.678+0.678
21 0.439+0.4550.439+0.455 +0.894+0.894

3.5 双倍的期望

双倍 = 赌注变 2 倍, 只拿一张, 自动停. 结果就是"停在 11+c11+c", 所以:

E(11)=2cP(c)E(11+c)E_{\text{双}}(11)=2\sum_c P(c) E_{\text{停}}(11+c)

抽到 变成 EE_{\text{停}} 概率 贡献
A-5 12-16 0.121-0.121 5/135/13 0.047-0.047
6 17 0.006-0.006 1/131/13 0.0005-0.0005
7 18 +0.223+0.223 1/131/13 +0.017+0.017
8 19 +0.453+0.453 1/131/13 +0.035+0.035
9 20 +0.678+0.678 1/131/13 +0.052+0.052
10 21 +0.894+0.894 4/134/13 +0.275+0.275

贡献之和为 +0.332+0.332 (单倍赌注的期望), 双倍即 2×0.332=+0.6652\times 0.332=+0.665.

3.6 要牌的期望

要牌和双倍的区别: 赌注不变, 而且拿完一张之后还能再决策. 所以:

E(T)=cP(c)max{E(T+c), E(T+c)}(T+c>21 记 1)E_{\text{要}}(T)=\sum_c P(c) \max\bigl\{E_{\text{停}}(T+c),\ E_{\text{要}}(T+c)\bigr\} \qquad (T+c>21 \text{ 记 } -1)

给这个 max 起个名字, 记 best(T)=max{E(T), E(T)}\text{best}(T)=\max\bigl\{E_{\text{停}}(T),\ E_{\text{要}}(T)\bigr\}, 含义是点数为 TT 时往后按最优打的期望. 上式就成了 E(T)=cP(c)best(T+c)E_{\text{要}}(T)=\sum_c P(c) \text{best}(T+c).

麻烦在于: 算 E(T)E_{\text{要}}(T) 要用到所有比 TT 大的点数的最优值. 解法是从 21 往下倒着算 (动态规划, 对于决策树就是从底往上算), 算到 TT 时比它大的都已知. 对庄家 6 手算头两行:

  • E(21)=1E_{\text{要}}(21)=-1, 再要必爆.
  • E(21)=+0.894E_{\text{停}}(21)=+0.894 (根据停牌期望), 所以 best(21)=+0.894\text{best}(21)=+0.894, 停.
  • E(20)E_{\text{要}}(20): 只有抽 A (113\tfrac{1}{13}) 不爆, =113(0.894)+1213(1)=0.854=\tfrac{1}{13}(0.894)+\tfrac{12}{13}(-1)=-0.854.
  • E(20)=+0.678E_{\text{停}}(20)=+0.678, 所以 best(20)=+0.678\text{best}(20)=+0.678, 停.

照这个节奏一路算到 11, 整列如下. 每一行的 EE_{\text{要}} 只用到它上面各行的 best\text{best}, 这就是"倒着算"的含义:

TT EE_{\text{停}} EE_{\text{要}} best\text{best} 动作
21 +0.894 -1.000 +0.894
20 +0.678 -0.854 +0.678
19 +0.453 -0.725 +0.453
18 +0.223 -0.613 +0.223
17 -0.006 -0.519 -0.006
16 -0.121 -0.443 -0.121
15 -0.121 -0.375 -0.121
14 -0.121 -0.308 -0.121
13 -0.121 -0.240 -0.121
12 -0.121 -0.172 -0.121
11 -0.121 +0.332 +0.332

这张表能读出来, 在初始是 11 对庄家 6 的时候, 要牌更好, 但是从 12-21 对庄家 6 全是停牌更好, 所以如果从 11 开始要牌, 就只能要一张牌就停了. 那如果只能要一张牌, 且期望收益为正, 还不如双倍. 所以最终 11 对 6 最终取 max(-0.121, +0.332, +0.665), 填双.

注意, 双倍没有出现在这张表里, 因为拿过一张牌之后就不允许双倍了, 它只在起手两张时作为第三个选项参与比较.

软牌是同一套递推, 只不过抽超了就把 A 改算 1 跳回硬牌.

3.7 分牌的期望

分成两手, 各补一份注, 每手从单张 cc 起步再抽第二张 c2c_2, 之后正常决策. 起手两张比上一小节的 best\text{best} 多一个双倍选项, 所以再定义 best2(两张起手)=max{E, E, E}\text{best}_2(\text{两张起手})=\max\bigl\{E_{\text{停}},\ E_{\text{要}},\ E_{\text{双}}\bigr\}. 分出的一对 A 每边只补一张, 不再决策, 直接取 EE_{\text{停}}. 于是

E(c)=2c2P(c2)best2(c+c2 起手)E_{\text{分}}(c)=2\sum_{c_2}P(c_2) \text{best}_2(c+c_2 \text{ 起手})

和"不分"的最优比较. 三个典型例子 (对应表上三种结论):

  • 8,8 对 10: 不分是 16 对 10, 停和要都是 0.540-0.540. 分开是两手"8 起步对 10", 合计 0.489-0.489. 分牌是为了少亏, 把一手死牌换成两手不太差的牌.
  • A,A 对 6: 不分是软 12, 最优约 +0.20+0.20. 分开每手从 A 起步 (下一张 413\tfrac{4}{13} 概率直接 21), 合计 +0.665+0.665. 分牌是为了扩大优势.
  • 10,10 对 6: 不分是 20, +0.678+0.678. 分开合计只有 +0.565+0.565. 20 已经够好, 拆掉是倒贴, 表上 10,10 永远不分.

3.8 有限副牌的计算

前面全部用了无限副牌近似. 真实的有限副牌怎么精确计算? 唯一的改动是把常数概率换成条件概率:

P(下一张=c已见的牌)=剩余的 c 还有几张剩余总张数P(\text{下一张}=c \mid \text{已见的牌})=\frac{\text{剩余的 } c \text{ 还有几张}}{\text{剩余总张数}}

状态从 (点数, 软硬) 扩成 (点数, 软硬, 剩余组成 (nA,,n10)(n_A,\dots,n_{10})): 每抽一张, 对应计数减一. 已见的三张牌 (你的两张 + 庄家明牌) 先从牌堆扣除. 期望的计算方式不变, 但是概率随着扩充的状态重新计算, 导致需要用计算机模拟 (代码在附录 A.1).

有限副牌会导致期望发生变化, 从而影响收益. 对于部分格子甚至会微弱地改变决策.

4. 关于保险

保险是一个独立的赌局. 它的规则是: 庄家明牌 A 时, 你可以额外下注最多主注一半, 赌底牌是 10 点牌, 赔 2:1. 这笔注和你手里的牌完全无关.

底牌是 10 点牌的概率 41330.8%\tfrac{4}{13}\approx 30.8\%. 每押 $1:

E保险=413(+2)+913(1)=1137.7%E_{\text{保险}}=\frac{4}{13}(+2)+\frac{9}{13}(-1)=-\frac{1}{13}\approx -7.7\%

盈亏平衡需要底牌是 10 的概率达到 13\tfrac{1}{3} (因为 p×2=(1p)p\times 2=(1-p) 解出 p=13p=\tfrac13).

唯一例外属于算牌者: 当已发出的小牌很多, 剩余牌里 10 点牌占比超过 13\tfrac13 时保险为正, 这恰好反过来证明, 对不记牌的人它永远为负.

5. 赌场优势和期望收益

5.1 定义

赌场优势 hh 定义为每押 $1 的平均亏损, h=Eh=-E:

总期望=h×总下注额\text{总期望}=-h\times\text{总下注额}

如果赌场优势为正, 期望收益永远为负, 除非一分钱也不下注 (不赚不亏). 下面推演对于 Blackjack, hh 可以是多少.

5.2 h=0h=0 的对称 Blackjack

你和庄家各拿牌, 双方都按同一条固定规则行动 (不到 17 拿, 到了 17 停), 同时亮牌比大小, 双方都爆算平局, Blackjack 不设额外赔付. 把任何一局中你和庄家的牌互换, 得到的是一局出现概率完全相同, 输赢恰好相反的牌局, 所以 P(你赢)=P(你输)P(\text{你赢})=P(\text{你输}), E=0E=0.

5.3 你先行动, hh 跳到 8%

如果玩家先行动, 爆牌立即输, 即使庄家随后也爆, 就算你仍机械照抄庄家的规则, 你也是不利的. 因为此时双方最终点数的分布完全相同, 但是"双爆"永远是你输, 于是

h=P(玩家和庄家都爆牌)=P()2h=P(\text{玩家和庄家都爆牌})=P(\text{爆})^2

按 H17 规则从起手两张一路抽到停, P()=0.285P(\text{爆})=0.285, h=0.28528.1%h=0.285^2\approx 8.1\% (把附录 dealer_dist 的起点换成任意起手两张即可复算). 这是整个赌场优势的地基.

5.4 把选择权卖回给玩家, hh 降到 0.5%

h=8%h=8\% 的游戏没人会坐下, 所以赌场把大部分优势"卖"了回来, 卖的东西是庄家没有的权利:

返还项 标价 累计 hh
起点: 先后手不对称 - 8.1%\approx 8.1\%
你的 Blackjack 多赔一半 (3:2), 庄家的不多收你 2.3%-2.3\% 5.8%\approx 5.8\%
自由选择停牌点 (12-16 对小牌收手等, 见 3.4 节) 3.2%-3.2\% 2.6%\approx 2.6\%
双倍: 只在占优局面把注加倍 (见 3.5 节) 1.6%-1.6\% 1.0%\approx 1.0\%
分牌: 止损或扩大优势 (见 3.7 节) 0.5%-0.5\% 0.5%\approx 0.5\%

注意, 每一项返还都以最优使用为前提, 普通人不按表而是凭直觉会让赌场有更大优势.

5.5 收紧规则, hh 回到 2%

等玩家群体普遍学会了基本策略, 赌场再增加一下规则收回更多的钱:

  • 软 17 从停改拿 (S17 -> H17): +0.2%+0.2\%.
  • Blackjack 从 3:2 改赔 6:5: +1.35%+1.35\%. 独立验算: 拿到 Blackjack 的概率 2×113×413=4.73%2\times\tfrac{1}{13}\times\tfrac{4}{13}=4.73\%, 剔除双方同为 Blackjack 的平局 (约 4.7%), 每次少赔 0.3 倍, 4.73%×0.953×0.31.35%4.73\%\times 0.953\times 0.3\approx 1.35\%.

5.6 精确值计算

一个开局由三张可见的牌决定: 你的两张 c1,c2c_1, c_2 和庄家明牌 uu. 无限副牌下它出现的概率是 P(c1)P(c2)P(u)P(c_1)P(c_2)P(u). 记 v(c1,c2,u)v(c_1,c_2,u) 为这个开局之后按最优打, 平均每押 $1 的盈亏. 于是

h=c1,c2,uP(c1)P(c2)P(u)v(c1,c2,u)h=-\sum_{c_1,c_2,u}P(c_1)P(c_2)P(u) v(c_1,c_2,u)

三重求和一共 10×10×10=100010\times 10\times 10=1000 项. 剩下的问题只是 vv 怎么取, 按 Blackjack 分三种情况. 先记 dd 为庄家是 Blackjack 的概率, 明牌是 A 时底牌是 10 的概率 d=413d=\tfrac{4}{13}, 明牌是 10 时底牌是 A 的概率 d=113d=\tfrac{1}{13}, 其他明牌 d=0d=0.

  • 你是 Blackjack (c1,c2c_1, c_2 恰为 A 和 10 点牌): 庄家也是就平局, 否则赢 1.5 (6:5 桌是 1.2). 所以 v=(1d)×1.5v=(1-d)\times 1.5.
  • 你不是, 庄家是: 直接输掉主注, 贡献 d×(1)d\times(-1).
  • 双方都不是: 进入第 3 节的决策, 价值就是那里算出的最优期望, 对子还要和分牌比一次, 贡献 (1d)×best(1-d)\times\text{best}. 注意第 3 节的所有期望都建立在"庄家不是 Blackjack"的条件分布上, 所以这里用 (1d)(1-d) 加权.

附录 A.2 的 house_edge 函数做的就是这个三重循环, 1000 项加完取负号, 得到本文模型 (无限副牌, H17, DAS, 不重分牌) 的 h3:20.79%h_{3:2}\approx 0.79\% (Blackjack 2 赔 3), h6:52.14%h_{6:5}\approx 2.14\% (Blackjack 6 赔 5).

6. 关于下注

我们已经知道了:

总期望=h×总下注额\text{总期望}= -h \times \text{总下注额}

改变下注方案无法控制期望收益, 只能控制亏损的波动.

6.1 打满 n 手的分布

按表打, 一手的结果不是简单的 ±1\pm 1. 把 5.6 那 1000 个开局各自的结果分布按概率加权, 就得到单手结果的精确分布 (6:5 桌, 代码在附录 A.3):

结果 (倍) xx 4-4 3-3 2-2 1-1 00 +1+1 +1.2+1.2 +2+2 +3+3 +4+4
概率 qq 0.02% 0.18% 4.5% 43.4% 8.8% 32.3% 4.5% 6.1% 0.23% 0.04%

(±2\pm 2 主要来自双倍和分牌, ±3\pm 3±4\pm 4 来自分牌后再双倍, +1.2+1.2 是 6:5 的 Blackjack.)

它的期望按定义就等于 h-h. 算出来 μ=0.0214\mu=-0.0214, 与 5.6 的 h6:5=2.14%h_{6:5}=2.14\% 一致. 3:2 桌把 +1.2+1.2 换成 +1.5+1.5, μ=0.0079=h3:2\mu=-0.0079=-h_{3:2}. 标准差 σ1.14\sigma\approx 1.14 倍赌注, 是均值绝对值的 53 倍, 这就是"一手的正常起伏幅度".

各手独立, 独立随机变量之和的方差等于方差之和, 所以 nn 手之后:

均值=nμ  (线性增长)标准差=σn  (平方根增长)\text{均值}=n\mu \ \ (\text{线性增长}) \qquad \text{标准差}=\sigma\sqrt{n}\ \ (\text{平方根增长})

n=100n=100: 均值 2.1-2.1 个赌注, 标准差 11.411.4 个赌注. 也就是说打完 100 手, 从亏 13 到赚 9 都属于一个标准差内的"正常".

均值什么时候才盖过噪声? 要求 nμ>σn|n\mu|>\sigma\sqrt{n}:

n>(σμ)2=(1.140.0214)22800 手n>\left(\frac{\sigma}{|\mu|}\right)^2=\left(\frac{1.14}{0.0214}\right)^2\approx 2800 \text{ 手}

3000 手以内, 你看到的输赢基本全是运气. 赌场每天数十万手, nn 大到噪声相对可忽略, 赚的是确定的平均值. 单个玩家则永远在 n\sqrt{n} 占主导的波动里.

6.2 破产风险与 1/20 规则

上面算的是"打满 nn 手之后"的分布, 但实际中你可能中途就把钱输光. 用 6.1 的分布蒙特卡洛模拟 20 万场 (代码在附录 A.3), "中途输光"的概率:

手里几个赌注 30 手 100 手 300 手
3 个 65% 82% 91%
10 个 11% 43% 69%
20 个 ~0% 10% 40%
50 个 ~0% ~0% 2%

20 个赌注恰好能压住约 100 手尺度的正常波动, 这就是流传的 “单注不超过资金 1/20” 经验的数学解释. 300 手尺度则需要约 50 注. 还有一个约束来自策略本身: 按表打, 一手最多要押到 2-4 份赌注 (双倍, 分牌, 分后再双倍), 本金低于这个余量时第 3 节的最优解不可行.

6.3 赢到多少收手

收手点改变不了期望符号, 它是在"到达目标的概率"和"目标大小"之间做权衡. 这个权衡可以精确计算.

假设你带着 aa 个赌注进场, 目标是赚到 +b+b 个赌注, 碰到目标或者输光就离场. 打完第 nn 手后的累计输赢记作 SnS_n, 开局 S0=0S_0=0, 到达目标就是 S=+bS=+b, 输光就是 S=aS=-a. 单手的输赢记作 XX, 它的均值 μ\mu 和标准差 σ\sigma 在 6.1 已经算出, 6:5 桌上 μ=0.0214\mu=-0.0214, σ1.14\sigma\approx 1.14. 我们要求的是到达目标的概率, 记作 pp.

公平游戏, μ=0\mu=0.

每手期望为 0 的意思是, 不管现在在哪个位置, 打一手之后位置的平均值不变:

E[Sn+1]=E[Sn+X]=E[Sn]+μ=E[Sn]E[S_{n+1}]=E[S_n+X]=E[S_n]+\mu=E[S_n]

离场位置只有两种. 概率 pp 停在 +b+b, 概率 1p1-p 停在 a-a. 平均一下并令其为 0:

pb+(1p)(a)=0  p=aa+bp\cdot b+(1-p)\cdot(-a)=0 \ \Longrightarrow\ p=\frac{a}{a+b}

如果本金和目标一样大, p=12p=\tfrac12. 如果本金 20 注只想赚 5 注, p=2025=80%p=\tfrac{20}{25}=80\%.

不公平游戏, μ<0\mu<0.

现在 E[Sn+1]=E[Sn]+μE[S_{n+1}]=E[S_n]+\mu, 期望每手下降 μ|\mu|, 没办法把 SS 套入公平游戏的方法, 但我们可以找到一个量 YnY_n , 满足两个条件, 就能套入公平游戏:

  1. YnY_n 由位置 SnS_n 决定.
  2. E[Yn+1]=E[Yn]E[Y_{n+1}]=E[Y_n] 对每手成立.

满足这两条的 YY 确实存在. 取

Yn=eθSnY_n=e^{\theta S_n}

θ\theta 是一个待定常数. 条件 1 显然满足. 验证条件 2, 关键一步是指数把和拆成积:

Yn+1=eθ(Sn+Xn+1)=eθSneθXn+1=YneθXn+1Y_{n+1}=e^{\theta(S_n+X_{n+1})}=e^{\theta S_n}\cdot e^{\theta X_{n+1}}=Y_n\cdot e^{\theta X_{n+1}}

Xn+1X_{n+1} 与前 nn 手独立, 所以与 YnY_n 独立, 独立随机变量乘积的期望等于期望的乘积:

E[Yn+1]=E[Yn]E[eθX]E[Y_{n+1}]=E[Y_n]\cdot E[e^{\theta X}]

对比第一步. 公平游戏里传递链的形式是 E[Sn+1]=E[Sn]+μE[S_{n+1}]=E[S_n]+\mu, 修正项是加性的 μ\mu. 这里修正项变成乘性的因子 E[eθX]E[e^{\theta X}], 所以条件 2 成立当且仅当

E[eθX]=1E[e^{\theta X}]=1

μ<0\mu<0 时这个方程有非零解. 记 φ(θ)=E[eθX]=kqkeθxk\varphi(\theta)=E[e^{\theta X}]=\sum_k q_k e^{\theta x_k}, 根据 6.1 分布:

  1. φ(0)=kqk=1\varphi(0)=\sum_k q_k=1.
  2. φ(0)=kqkxk=μ<0\varphi'(0)=\sum_k q_k x_k=\mu<0, 所以在 θ=0\theta=0 右侧, φ\varphi 先降到 1 以下.
  3. φ(θ)=kqkxk2eθxk>0\varphi''(\theta)=\sum_k q_k x_k^2 e^{\theta x_k}>0 对一切 θ\theta 成立, 所以 φ\varphi 严格凸.
  4. θ+\theta\to+\infty 时, 赢钱档的项发散, 比如 +1+1 档贡献 0.32eθ0.32\cdot e^{\theta}\to\infty, 所以 φ(θ)\varphi(\theta)\to\infty.

一条从 1 出发, 起初下降, 严格凸, 最终升向无穷的曲线, 必然在某个 θ>0\theta^*>0 处第二次穿过高度 1. 因此有非零解.

利用单手 θX\theta X 很小做泰勒近似, eθX1+θX+(θX)22e^{\theta X}\approx 1+\theta X+\tfrac{(\theta X)^2}{2}, 取期望并用 E[X]=μE[X]=\mu, E[X2]σ2E[X^2]\approx\sigma^2:

1+θμ+θ2σ22=1  θ=2μσ2=2×0.02141.290.0331+\theta\mu+\frac{\theta^2\sigma^2}{2}=1 \ \Longrightarrow\ \theta=\frac{-2\mu}{\sigma^2}=\frac{2\times 0.0214}{1.29}\approx 0.033

第三步, 列方程解 pp.

有了 Yn=eθSnY_n=e^{\theta S_n}E[Yn+1]=E[Yn]E[Y_{n+1}]=E[Y_n], 按照公平游戏的解法. 离场时 YY 的期望等于开局值 eθ0=1e^{\theta\cdot 0}=1. 概率 pp 停在 +b+bY=eθbY=e^{\theta b}, 概率 1p1-p 停在 a-aY=eθaY=e^{-\theta a}. 期望需要为 1:

peθb+(1p)eθa=1p\cdot e^{\theta b}+(1-p)\cdot e^{-\theta a}=1

解得:

p=eθa1eθ(a+b)1p=\frac{e^{\theta a}-1}{e^{\theta(a+b)}-1}

底金 20 个赌注时, 公式与模拟对照:

目标 到达概率 公平游戏对照 平均打几手 整场期望
+5+5 71% 80% 88 2.3-2.3
+10+10 52% 67% 165 4.3-4.3
+20+20 (翻倍) 33% 50% 296 6.9-6.9
+40+40 14% 33% 504 11.4-11.4

可以看出: 目标越小则到达概率越高, 期望亏损也越小, 因为平均手数少, 总下注少. 每一行的期望都是负的, 且随 bb 单调变差, 说明越想要赢更多钱, 期望亏损越大.

6.4 倍投法的谬误

有一种选择叫做倍投法: “只要本金够大, 前 nn 次输, 第 n+1n+1 次赢就全回来了, 而连输的概率极低”. 我们来分析一下.

设定. 每局赢的概率 pp, 输的概率 q=1pq=1-p, 赔率 1:1. 连输 nn 次后第 n+1n+1 局押 2n2^n 并赢, 净收益

2n(1+2++2n1)=12^n-(1+2+\cdots+2^{n-1})=1

恒等于 1. 所以"只要撑到赢那一局, 必赚一单位"是对的.

本金无限时. 设首次赢在第 τ\tau 局, P(τ=n)=qn1pP(\tau=n)=q^{n-1}p, 连输到底的概率 limnqn=0\lim_n q^n=0, 所以"最终赢 1"的概率确实是 1. 但完成一轮需要的本金是 2τ12^\tau-1, 它的期望

E[2τ]=n12nqn1p=2pn1(2q)n1E[2^\tau]=\sum_{n\ge 1}2^n q^{n-1}p=2p\sum_{n\ge 1}(2q)^{n-1}

q12q\ge\tfrac12 (公平或庄家占优) 时因 2q12q\ge 1发散. "足够大的本金"的期望值是无穷大.

本金有限时. 设本金 B=2N1B=2^N-1, 最多扛 NN 连输. 一轮只有两种结局: 赢 1 (概率 1qN1-q^N) 或输光 BB (概率 qNq^N):

E=(1qN)1qN(2N1)=1(2q)NE=(1-q^N)\cdot 1-q^N(2^N-1)=1-(2q)^N

公平赌局 q=12q=\tfrac12: E=0E=0, 一分不多不少, N=10N=10 时输的概率只有 11024\tfrac{1}{1024}, 但输的金额恰好 1023, 10231024×1\tfrac{1023}{1024}\times 1 与之精确抵消. 概率被压得多小, 单次损失就被放大多少倍.

回到 21 点本身, 赌场永远有优势, 所以输的概率一定大于赢的概率, 2q>12q>1. NN 很大的时候单轮爆仓 (连输) 概率极低, 但是长期玩总会出现爆仓.

结论: 倍投法没有逻辑错误的部分是"大概率赢一小笔". 错误发生在把它等同于"策略赚钱", 这混淆了高概率与正期望. 它的本质是用一个小概率的巨额亏损"购买"一个大概率的小盈利. 赌场设最低下注会让倍投玩家总下注额更大, 期望亏损越大.

附录: 复现代码

对应无限副牌和有限副牌 (1-8 副内) 的模拟. 五段代码按标注的依赖顺序运行.

A.1 策略表

无限副牌主模型, 独立运行. 打印硬牌表, 软牌把 best2 的初始状态换成软牌即可, 对子再和 ev_split 比较一次:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from functools import lru_cache

P = {c: 1/13 for c in range(1, 10)}; P[10] = 4/13 # 1 = A
H17 = True

def add(t, s, c): # (点数, 是否软) + 一张牌
t += c
if c == 1 and t + 10 <= 21: t += 10; s = True
if t > 21 and s: t -= 10; s = False
return t, s

def dealer_dist(u): # 3.3: 庄家最终分布, 剔除 Blackjack
dist = {17:0, 18:0, 19:0, 20:0, 21:0, 'bust':0}
hole = {c: p for c, p in P.items()
if not ((u==1 and c==10) or (u==10 and c==1))}
norm = sum(hole.values())
def rec(t, s, pr):
if t > 21: dist['bust'] += pr; return
if t > 17 or (t == 17 and not (s and H17)): dist[t] += pr; return
for c, p in P.items(): rec(*add(t, s, c), pr*p)
for c, p in hole.items(): rec(*add(*add(0, False, u), c), p/norm)
return dist

D = {u: dealer_dist(u) for u in range(1, 11)}

def ev_stand(t, u): # 3.4: 停牌的期望
if t > 21: return -1.0
d = D[u]
return d['bust'] + sum(d[f] for f in range(17, t)) - sum(d[f] for f in range(max(t+1,17), 22))

@lru_cache(None)
def ev_hit(t, s, u): # 3.6: 要牌的期望, 从 21 倒着算
return sum(p * (-1 if add(t,s,c)[0] > 21
else max(ev_stand(add(t,s,c)[0], u), ev_hit(*add(t,s,c), u)))
for c, p in P.items())

def ev_double(t, s, u): # 3.5: 双倍的期望
return sum(p * (-2 if add(t,s,c)[0] > 21 else 2*ev_stand(add(t,s,c)[0], u))
for c, p in P.items())

def best2(t, s, u): # 起手两张: 停/要/双 取最大
o = {'S': ev_stand(t,u), 'H': ev_hit(t,s,u), 'D': ev_double(t,s,u)}
return max(o, key=o.get), o

def ev_split(c, u): # 3.7: 分牌 (A 只补一张, 允许 DAS)
t0, s0 = add(0, False, c)
e = sum(p * (ev_stand(add(t0,s0,c2)[0], u) if c == 1
else max(best2(*add(t0,s0,c2), u)[1].values()))
for c2, p in P.items())
return 2*e

if __name__ == '__main__':
for t in range(5, 21): # 硬牌表
print(t, [best2(t, False, u)[0] for u in [2,3,4,5,6,7,8,9,10,1]])

有限副牌 (1-8 副) 的精确求解器, 独立运行, 对应 3.8. solve(手牌, 明牌, 副数) 返回 (停, 要, 双) 三个期望:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
H17 = True

def add(t, s, c):
t += c
if c == 1 and t + 10 <= 21: t += 10; s = True
if t > 21 and s: t -= 10; s = False
return t, s

def solve(hand, u, N):
deck = [4*N]*9 + [16*N]
for c in hand + [u]: deck[c-1] -= 1
deck = tuple(deck)
t, s = 0, False
for c in hand: t, s = add(t, s, c)
memoD, memoS, memoH = {}, {}, {}

def drec(dk, dt, ds): # 庄家从 (dt,ds) 抽到停
if dt > 21: return (0,0,0,0,0,1)
if dt > 17 or (dt == 17 and not (ds and H17)):
r = [0]*6; r[dt-17] = 1; return tuple(r)
key = (dk, dt, ds)
if key in memoD: return memoD[key]
tot = sum(dk); acc = [0.0]*6
for i in range(10):
if dk[i]:
nd = dk[:i] + (dk[i]-1,) + dk[i+1:]
sub = drec(nd, *add(dt, ds, i+1)); w = dk[i]/tot
for j in range(6): acc[j] += w*sub[j]
memoD[key] = tuple(acc); return memoD[key]

def dealer(dk): # 底牌分布, 剔除 Blackjack
if dk in memoS: return memoS[dk]
t0, s0 = add(0, False, u)
acc = [0.0]*6
norm = sum(dk[i] for i in range(10)
if not ((u==1 and i+1==10) or (u==10 and i+1==1)))
for i in range(10):
c = i+1
if dk[i]==0 or (u==1 and c==10) or (u==10 and c==1): continue
nd = dk[:i] + (dk[i]-1,) + dk[i+1:]
sub = drec(nd, *add(t0, s0, c)); w = dk[i]/norm
for j in range(6): acc[j] += w*sub[j]
memoS[dk] = acc; return acc

def ev_stand(dk, pt):
if pt > 21: return -1.0
d = dealer(dk); ev = d[5]
for j, f in enumerate(range(17, 22)):
if f < pt: ev += d[j]
elif f > pt: ev -= d[j]
return ev

def ev_hit(dk, pt, ps):
key = (dk, pt, ps)
if key in memoH: return memoH[key]
tot = sum(dk); ev = 0.0
for i in range(10):
if dk[i]:
nd = dk[:i] + (dk[i]-1,) + dk[i+1:]
nt, ns = add(pt, ps, i+1)
ev += dk[i]/tot * (-1 if nt > 21 else max(ev_stand(nd, nt), ev_hit(nd, nt, ns)))
memoH[key] = ev; return ev

def ev_dbl(dk, pt, ps):
tot = sum(dk); ev = 0.0
for i in range(10):
if dk[i]:
nd = dk[:i] + (dk[i]-1,) + dk[i+1:]
nt, _ = add(pt, ps, i+1)
ev += dk[i]/tot * (-2 if nt > 21 else 2*ev_stand(nd, nt))
return ev

return ev_stand(deck, t), ev_hit(deck, t, s), ev_dbl(deck, t, s)

if __name__ == '__main__':
for N in (1, 2, 6, 8): # A+2 对 5 随副数, 策略表的争议格
S, H, Dd = solve([1, 2], 5, N)
print(N, '副: A+2 对 5, 双-要 =', round(Dd - H, 4))
S, H, _ = solve([10, 4, 2], 10, 6) # 三张组成的 16 对 10
print('16 = 10+4+2 对 10, 6 副: 停', round(S, 4), ' 要', round(H, 4))
S, H, _ = solve([7, 7], 10, 1) # 单副 7,7 对 10
print('7,7 对 10, 1 副: 停', round(S, 4), ' 要', round(H, 4))

A.2 期望收益

接在 A.1 的无限副牌代码之后运行, 对应 5.6 的三重求和:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def house_edge(bj_pay=1.5):            # 6:5 传 1.2
ev = 0
for c1, p1 in P.items():
for c2, p2 in P.items():
for u, pu in P.items():
d = {1: 4/13, 10: 1/13}.get(u, 0) # 庄家 Blackjack 概率
if {c1, c2} == {1, 10}: v = (1-d)*bj_pay
else:
t, s = add(*add(0, False, c1), c2)
e = max(best2(t, s, u)[1].values())
if c1 == c2: e = max(e, ev_split(c1, u))
v = -d + (1-d)*e
ev += p1*p2*pu*v
return -ev

if __name__ == '__main__':
print('h(3:2) =', round(house_edge(1.5), 4), ' h(6:5) =', round(house_edge(1.2), 4))

A.3 n 手分布

接在 A.1 与 A.2 之后运行, 输出 6.1 的单手分布. 末行同时打印均值与 h-h, 验证两者精确相等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
from collections import defaultdict

memoH2 = {}
def hit_dist(t, s, u): # 已拿过牌后按最优打, 最终点数分布
key = (t, s, u)
if key in memoH2: return memoH2[key]
if ev_stand(t, u) >= ev_hit(t, s, u):
memoH2[key] = {t: 1.0}; return memoH2[key]
out = defaultdict(float)
for c, p in P.items():
nt, ns = add(t, s, c)
if nt > 21: out['bust'] += p
else:
for k, q in hit_dist(nt, ns, u).items(): out[k] += p*q
memoH2[key] = dict(out); return memoH2[key]

def hand_dist(t, s, u): # 起手两张按最优打, (最终点数, 乘数) 分布
a, _ = best2(t, s, u)
if a == 'S': return {(t, 1): 1.0}
out = defaultdict(float)
for c, p in P.items():
nt, ns = add(t, s, c)
if a == 'D': out[(nt if nt <= 21 else 'bust', 2)] += p
elif nt > 21: out[('bust', 1)] += p
else:
for k, q in hit_dist(nt, ns, u).items(): out[(k, 1)] += p*q
return dict(out)

def res(fin, m, f): # 对上庄家最终 f 的盈亏
if fin == 'bust': return -m
if f == 'bust' or fin > f: return m
return 0 if fin == f else -m

FIN = [17, 18, 19, 20, 21, 'bust']
def outcome_nonsplit(t, s, u):
hd = hand_dist(t, s, u); out = defaultdict(float)
for f in FIN:
for (fin, m), q in hd.items(): out[res(fin, m, f)] += D[u][f]*q
return out

def outcome_split(c, u): # 两手对同一个庄家结果, 给定 f 条件独立
side = defaultdict(float)
t0, s0 = add(0, False, c)
for c2, p in P.items():
t, s = add(t0, s0, c2)
for k, q in ({(t, 1): 1.0} if c == 1 else hand_dist(t, s, u)).items():
side[k] += p*q
out = defaultdict(float)
for f in FIN:
cond = defaultdict(float)
for (fin, m), q in side.items(): cond[res(fin, m, f)] += q
for r1, q1 in cond.items():
for r2, q2 in cond.items(): out[r1+r2] += D[u][f]*q1*q2
return out

def total_dist(bj_pay=1.2): # 6.1: 1000 个开局按概率加权
tot = defaultdict(float)
for c1, p1 in P.items():
for c2, p2 in P.items():
for u, pu in P.items():
w = p1*p2*pu
d = {1: 4/13, 10: 1/13}.get(u, 0)
if {c1, c2} == {1, 10}:
tot[bj_pay] += w*(1-d); tot[0.0] += w*d; continue
tot[-1.0] += w*d
t, s = add(*add(0, False, c1), c2)
base = max(best2(t, s, u)[1].values())
dist = outcome_split(c1, u) if (c1 == c2 and ev_split(c1, u) > base) else outcome_nonsplit(t, s, u)
for k, q in dist.items(): tot[round(float(k), 4)] += w*(1-d)*q
return dict(tot)

if __name__ == '__main__':
td = total_dist(1.2)
mu = sum(k*q for k, q in td.items())
sd = (sum(k*k*q for k, q in td.items()) - mu*mu)**0.5
for k in sorted(td): print(f'{k:+.1f}: {td[k]*100:.2f}%')
print('mean =', round(mu, 5), ' -house_edge(1.2) =', round(-house_edge(1.2), 5), ' sd =', round(sd, 4))

A.4 破产风险

独立运行, 单手分布取 6.1 的数值, 对应 6.2 的表. 每格 20 万场, 全表约需几分钟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import random, bisect
random.seed(3)
vals = [-4, -3, -2, -1, 0, 1, 1.2, 2, 3, 4] # 6.1 的精确分布
prob = [.0002, .0018, .0446, .4337, .0878, .3231, .0451, .0610, .0023, .0004]
cum = []
t = 0
for p in prob: t += p; cum.append(t)
def draw(): return vals[bisect.bisect(cum, random.random())]

def ruin(bank, hands, trials=200000): # 6.2: n 手内中途输光的概率
b = 0
for _ in range(trials):
x = bank
for _ in range(hands):
if x < 1: b += 1; break
x += draw()
return b / trials

if __name__ == '__main__':
for bank in (3, 10, 20, 50):
print(bank, '注:', [f'{ruin(bank, h):.0%}' for h in (30, 100, 300)])

A.5 收手时机

接在 A.4 之后运行, 对应 6.3 的表. 每行输出到达概率, 平均手数与整场期望:

1
2
3
4
5
6
7
8
9
10
11
12
13
def reach(bank, target, trials=50000):     # 6.3: 输光之前先到 +target 的概率
hit = 0; steps = 0
for _ in range(trials):
x = bank; n = 0
while 1 <= x < bank + target:
x += draw(); n += 1
hit += x >= bank + target; steps += n
return hit / trials, steps / trials

if __name__ == '__main__':
for tgt in (5, 10, 20, 40):
p, n = reach(20, tgt)
print(f'目标 +{tgt}: 到达 {p:.0%}, 平均 {n:.0f} 手, 整场期望 {p*tgt-(1-p)*20:+.1f} 注')

Blackjack 数学推导
http://example.com/2026/08/29/2026-08-29-blackjack-math/
Author
Wind_like
Posted on
August 29, 2026
Licensed under