本文旨在通过简单的数学和计算机模拟,解释为何在赌博中倍投毫无意义。
(假如你觉得在某处看过此文章,没必要惊讶,那很可能也是我写的,只不过被我转移到了这边)
倍投法(马丁格尔策略,Martingale Strategy)是一种赌博策略,该策略的核心思想是在每次输钱之后增加下一次赌注,以期望在赢得一局后能够弥补之前的损失,从而实现盈利。它基于一种假设,即输钱的次数越多,最终赢回来的可能性就越大。
倍投只存在于理论中,只有在赌资、赌注、时间无限的情况下有意义,三个要素只要有一个不满足就是无意义的,显然,现实世界中这三个条件中任意一个都不能被满足。赌博是反直觉的,倍投也是反直觉的,倍投其实并不会让你赚更多,但是同样的,也不会让你亏更多。你的赢钱期望一开始就决定了,所以不需要说什么倍投必死,试试反倍投,任何稀奇古怪的策略都是无意义的。
倍投法存在两个心理骗局,让你觉得这个方法好像可行。
概率很大,但是期望仍然是负的
单纯的概率是没有意义的,赌博是为了赢钱,必须加上收益期望才有意义。
拿抛硬币为例,胜率1/2,玩6轮,从1开始投注。学过小学数学的人会知道,连输6场的概率是1/64,也就是1.5625%。在倍投法中,只要赢一次就能获得收益,因此获得收益的概率为1-1/64,约为98.4375%。看上去是稳赚的,但是这没有意义,因为期望仍然是非正数。
在上面这个简单例子中,假如你赢了一次就跑,你有1/64的几率输掉63元,然后有63/64的几率赚1元。计算期望为1x63/64-63x1/64,结果为0元。
如果你赢了不停,继续下注1元,结果相同。这个也是反直觉的,并不会像赌客所想的那样赢更多,也不会像普通人那样见好不收会亏更多。公式证明有点麻烦,出于篇幅问题,借用一张图片,把规模缩小到3轮,这是轮盘赌红蓝球用倍投法的排列组合。把最右边的期望加起来,其实还是0。
期望为0是50%胜率的公平赌局,现实中赌场的概率都是小于50%的,期望必然为负数。倍投相比均注,会让你从等概率的不赚不赔,变成大概率小赚,小概率大赔。其实还是一样的。除了节省时间以外没有什么意义。
但是在完全理想的环境下,倍投法确实是必赢的,因为你的赌资、时间和下注上限都是正无穷,赢的概率就是必然100%。但是只要条件不是无限,直接把上面的例子等比放大一亿倍也是同样的期望。
黑天鹅其实很常见
倍投基于一个假设,连输的概率很低,但是只要赢一次就能翻本。因此在交替输赢的局面下,倍投可以有较稳定的收益,直到遇到连输才会爆仓离场。但是连输的概率在多轮赌局后,就会非常大。这个很反直觉,但是确实是这样的。比如在上面的抛硬币赌局里,6场里连输6场概率是1.5625%,这个很容易计算出来。但是如果继续玩下去,玩10场、100场、200场呢?通过程序计算排列组合以及随机模拟,可以得出以下结果。代码在最后边。
为了让结果更有趣味性,我还加了记录模拟过程中出现过的最大连输数,这个最大连输次数主要和模拟次数有关。由于计算排列组合的时间复杂度是按指数增加的,超过20局以上就会耗时非常长,因此后续没有列出。
胜率为0.50,10000000次数模拟中,
每6次赌局中平均至少出现一次连输6次发生概率:1.56%,
出现过的最多次连输:6次
胜率为0.5,通过排列组合,
每6次赌局中平均至少出现一次连输6次发生概率:1/64,1.56%
胜率为0.50,10000000次数模拟中,
每10次赌局中平均至少出现一次连输6次发生概率:4.68%,
出现过的最多次连输:10次
胜率为0.5,通过排列组合,
每10次赌局中平均至少出现一次连输6次发生概率:48/1024,4.69%
胜率为0.50,10000000次数模拟中,
每50次赌局中平均至少出现一次连输6次发生概率:31.41%,
出现过的最多次连输:28次
胜率为0.50,10000000次数模拟中,
每100次赌局中平均至少出现一次连输6次发生概率:54.47%,
出现过的最多次连输:29次
胜率为0.50,10000000次数模拟中,
每200次赌局中平均至少出现一次连输6次发生概率:79.93%,
出现过的最多次连输:31次
胜率为0.50,10000000次数模拟中,
每1000次赌局中平均至少出现一次连输6次发生概率:99.97%,
出现过的最多次连输:32次
其实这个也是赌徒谬误的一种,“怎么可能连输10把呢?“,上得山多终遇虎,实际上这很容易,只要玩1000盘,连输10把的概率就已经是38%了,玩2000盘就是62%。只要你玩得够多,那什么都能碰上。而且赌局本质上是毫无联系完全随机的,一年每个月玩100把和一天玩1200把并没有本质区别。
总结
显然,赌博中使用倍投的期望只取决于赌局胜率,也就是说倍投并不能改变任何期望。
不过有些问题仍然萦绕不去。在较短期的赌局中,拥有较大的赌资和较小的赌注,确实可以使得胜率非常高,即使此时期望仍然为非正数。比如上面的例子,有98.4375%的几率赢1元,仅有1.5625%的几率输掉63元。总体胜率如此之高,这很让人迷惑,如何说服我不使用倍投?
在某种程度上,这确实呈现出一种悖论,其中期望为非正数是事实,而总体胜率却异常高。这种情况令人容易受到短期内高胜率的吸引。因此,很多网站提到的“在短期内,使用倍投法以小博大是有意义的”也有其合理之处。不过根据上面的计算我们知道,这个悖论不会持续太久,只要时间久一点,黑天鹅就很容易遇见。
代码
以下是模拟的python代码,你可以通过修改开头那几个为大写的变量来改变模拟目的。其中排列组合计算耗时极长,无法在正常时间内算出20局以上的结果,慎用,可以通过注释最后两行来取消排列组合计算。
# -*-coding:utf-8-*-
import random
from typing import Final, Tuple
# 修改下面的变量可以调整模拟的目的
SIM_ROUNDS: Final[int] = 100000 # 模拟次数
LOSING_STREAK_NUM: Final[int] = 6 # 连输次数
BET_ROUNDS: Final[int] = 10 # 赌局次数
P: Final[float] = 0.5 # 赌局胜率
###
def permuting_betting(bet_rounds: int, losing_streak_num: int) -> Tuple[int, int]:
"""
通过排列组合计算一系列赌局中出现至少连输特定次数的几率,赌局胜率固定为50%,耗时极长
Args:
bet_rounds (int): 赌局次数。
losing_streak_num (int): 连输次数。
Returns:
Tuple[int, int]: 至少特定次数连输的排列数;总排列组合数。这两个值的比值为几率。
"""
reach_losing_streak_times: int = 0
for curr_bet in range(0, 1 << bet_rounds):
losing_streak_count: int = 1
prev_lowbit: int = 0
while curr_bet != 0:
curr_lowbit: int = curr_bet & -curr_bet
losing_streak_count = losing_streak_count + \
1 if curr_lowbit == prev_lowbit << 1 else 1
if losing_streak_count >= losing_streak_num:
reach_losing_streak_times += 1
break
prev_lowbit = curr_lowbit
curr_bet &= curr_bet-1
return reach_losing_streak_times, 2**bet_rounds
def simulate_betting(sim_rounds: int, bet_rounds: int, p: float, losing_streak_num: int) -> Tuple[float, int]:
"""
通过模拟计算一系列赌局中出现至少连输特定次数的几率,赌局胜率可调,耗时正常
Args:
sim_rounds (int): 模拟次数,影响精度、耗时、以及最大连输次数。
bet_rounds (int): 赌局次数。
p (float): 赌局胜率。
losing_streak_num (int): 连输次数。
Returns:
Tuple[float, int]: 至少连输特定次数的几率;最大连输次数。
"""
def simulate_betting_helper(bet_rounds: int, p: float, losing_streak_num: int) -> Tuple[bool, int]:
losing_streak_count: int = 0
max_losing_streak: int = 0
reach_losing_streak_num: bool = False
for _ in range(bet_rounds):
win: bool = random.random() < p
losing_streak_count = losing_streak_count+1 if not win else 0
max_losing_streak = max(max_losing_streak, losing_streak_count)
reach_losing_streak_num = losing_streak_count >= losing_streak_num or reach_losing_streak_num
return reach_losing_streak_num, max_losing_streak
avg_freq_reach_losing_streak: float = 0.0
max_losing_streak: int = 0
reached_times: int = 0
for _ in range(sim_rounds):
reached, curr_max_losing_streak = simulate_betting_helper(
bet_rounds, p, losing_streak_num)
reached_times += 1 if reached else 0
max_losing_streak = max(curr_max_losing_streak, max_losing_streak)
avg_freq_reach_losing_streak = reached_times/SIM_ROUNDS
return avg_freq_reach_losing_streak, max_losing_streak
if __name__ == '__main__':
avg_freq_reach_losing_streak, max_losing_streak = simulate_betting( SIM_ROUNDS, BET_ROUNDS, P, LOSING_STREAK_NUM)
print(f"胜率为{P},{SIM_ROUNDS}次数模拟中, \n每{BET_ROUNDS}次赌局中平均至少出现一次连输{LOSING_STREAK_NUM}次发生概率:{avg_freq_reach_losing_streak*100:.2f}% \n出现过的最多次连输:{max_losing_streak}次")
# 计算排列组合,耗时极长
reached_times, all_permutations = permuting_betting( BET_ROUNDS, LOSING_STREAK_NUM)
print( f"胜率为0.5,通过排列组合, \n每{BET_ROUNDS}次赌局中平均至少出现一次连输{LOSING_STREAK_NUM}次发生概率:{reached_times}/{all_permutations},{reached_times/all_permutations*100:.2f}%")
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。