在这篇文章中你不会读到以下内容:
在洗牌中,卡牌的不同罕贵度是否会对上手率产生影响?(我也不知道,但是我没钱所以都用底罕)
线上洗牌是否存在特殊机制,流势等因素?废件是否更容易/更难上手?(有兴趣可以自己去调查)
如何给自己洗一手好牌/给对方洗一手烂牌?(你问裁判吧,裁判肯定懂)
如果你感兴趣的不是以上的内容,那让我们继续,我们将会聊到:
1. 洗牌的基本逻辑:什么是随机洗牌?
2. 洗牌与初始状态的关系
3. 切牌与分堆(所谓“大洗”)存在的问题,熵
4. 为什么我们需要插洗?
每个章节最后有结论部分,如果你觉得内容过于冗长,可以直接对照结论来验证自己是否理解我想表达什么。
1. 洗牌的基本逻辑:什么是随机洗牌?
(如果你对于随机,概率这些概念有基本的了解,建议直接跳过本段落至“结论”部分。)
如果你玩过任意一款包含抽卡或者随机掉落要素的游戏,相信你一定对于“随机”,“概率”,“期望”这些词语不会感到陌生。
如果我们掷一颗均匀的六面骰子,那么它向上的面就会有6种等可能的结果,每种结果出现的概率是1/6,而在掷骰子之前我们无论如何研究过去的骰子结果,都无法对下一次向上的数字做出猜中概率超过1/6的猜测。
那么随机的洗牌是什么样的呢?一个常见的误解是,我的40张牌的卡组里有16张手坑,那么随机洗牌是否应该洗成每连续的5张牌中有2张手坑呢?答案当然是否定的。
让我们先来看一下YGOMobile修正后的打乱机制,这里我用自然语言描述一下其基本流程:
Step 1. 取得需要洗的牌的序列。
这里我们假设共有40张,对应40个位置,给40个位置编号为0~39。
Step 2. 从第0到第39号位置:对于每个位置i,我们用均匀分布生成一个集合{i, i + 1 ... 39}中的整数r,随后将i号位置与r号位置的卡片交换(若r=i,则不进行任何操作)。
细心的读者或许注意到了,当我们对第i个位置进行了上述操作后,其后的操作只会影响第i+1到39号位置上的卡片,而第0号到第i号位置的卡片已经完全确定。
最终,我们“洗”好了一副牌,其卡顶可能是三个灰两个小白,也可能是霜精救龙墓指灰G,但问题随即而来:我们凭什么说这种洗牌是“随机”的呢?
为了简化问题,让我们考虑3张牌的情况。
如果将它们编号为0,1,2,那么当洗牌结束后,我们就可能有6种情况:(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)。
对于一个真正的随机洗牌过程,这6种情况出现的概率应该完全相同,那么,让我们用上述的方法来考虑出现(1, 2, 0)的概率,在洗牌前初始状态为(0, 1, 2):
i=0时:为了产生(1, 2, 0),我们需要在集合{0, 1, 2}上的均匀分布中取得1,因为有三个选项,这一步的概率是1/3。
交换结束后,当前顺序变为(1, 0, 2)。
i=1时:为了产生(1, 2, 0),我们需要在集合{1, 2}上的均匀分布中取得2,这样我们才能交换1号位置与2号位置,显然,这一步的概率是1/2,交换结束后,当前顺序变为(1, 2, 0)。
i=2时:我们产生随机数的集合是{2},那么我们只能取得2,而将2号位置的牌与2号位置互换不会带来任何变化。
因为每一步我们都必须成功,所以很显然,最终概率就是(1/3) * (1/2) = 1/6。
读者也可以验证其他组合出现的概率同样是1/6,那么我们就完成了一次完美的“随机”洗牌。
如果从另一个角度看这个过程,可以想像我们有一个大筐,其中装有40张卡,我们每次随机摸出一张卡放在之前的卡的上方,这个“随机摸出”的过程就是“产生均匀分布随机数”,而i步对应的就是将卡放在从下往上数第i+1张的位置。
为什么要提到“大筐”这个意象呢?因为如果我们仔细思考,就会发现“大筐”与“序列”的模型其实是不同的。
在大筐中,我们的卡片没有顺序,而在序列中,我们有一个初始顺序,每个步骤都是在调整这个顺序。
在这个优秀的洗牌流程中,我们发现无论是大筐还是序列都不影响我们理解,但是这二者确实有着不同。
在下一章节中,我们将会认识这种不同。
结论:随机洗牌的目标是通过一个过程生成n张牌的一个顺序,使得所有可能出现的顺序出现的概率相等。
在下面我会将这种分布称为“完美分布”。
需要注意的是,这种“完美分布”并不是一个确定的牌序,而是所有可能牌序集合上的均等概率分布。
2. 洗牌与初始状态的关系
我在实卡对局中,在对方洗切过我的卡组后并交还后,曾经收到过这样的要求:”请将你卡组上方的5张放到卡组最下方“。
这种情形并不罕见,我也欣然照做。
但如果仔细思考,会发现这个操作很有意思:
1. 如果我作弊,通过某种方法控制(在对方进行洗切后)卡顶的5张牌是自己想要的牌(例如:霜精,救龙,墓指,灰,G),那么进行对应操作之后对方可以保证我抽不到这5张牌。
2. 如果我作弊,并且控制使每连续5张都是一副完美的手牌,那么在这个操作后我依然可以拥有一副完美的手牌(第6~10张)。
3. 如果我进行了上述的“随机洗牌”,获得了完美分布,那么进行这个操作不会对牌序列的概率分布产生任何影响:对于一个卡顶5张按顺序是霜精,救龙,墓指,灰,G(有顺序)的序列A,必然就存在一个6~10张按顺序是这5张的序列B,使得将序列B的0~4号按顺序放到35~39号(后面的卡片顺次前移)后刚好成为序列A,而根据我们对随机洗牌的定义,序列A和序列B出现的概率是相等的,也就是说我抽到这5张牌的概率在进行了上述移动后完全没有发生变化。
这或许能给我们一些启发:尽管进行了同样的操作,在一些情况(例如情况1)中,这个操作极大改变了我的起手手牌,而在另一些情况(例如情况3)中,这个操作却没有对我起手手牌的概率分布产生任何影响。
同时,我们注意到这个操作没有任何随机性,所以对于一个想要作弊的选手,如果知道对方习惯让对手做这个动作,可以将想要的牌控制在第6~10张,同样可以达成目的。
对于这种选手,一个更复杂的方式或许可以在一定程度上遏制对手:在完成切洗后,我们掷一枚骰子,如果朝上的面是奇数,就要求对面进行该操作,否则就不要求对方做任何操作。
这种情况下,无论对面控制1~5还是6~10,都有50%的概率无法抽到自己想要的牌。
这个事例真正告诉我们的是:对于任意一个”确定“的洗牌操作,操作后的牌序分布都依赖于操作前的分布。
这里的“确定”不一定是“将5张牌放到卡组最下方”这种完全确定的步骤,也可以是“扔一枚骰子决定让对方是否将5张牌放到卡组最下方”这种包含随机过程的确定行为模式。
事实上,进行分堆,切牌,乃至插洗也是如此。
在实卡中,我们在洗牌开始前永远会有一个初始状态,也就是我们把牌从卡盒中拿出来,或者把卡收拢起来的状态。
一个常见的例子是,在进行完一场对局后,如果我们完成了一套展开combo,将场上,墓地或是除外区的卡片收拾在一起时,其中绝大多数会是combo组件,而剩余卡组中手坑的比例就会比原始卡组中高,那么如果我们将公开领域的这些卡片叠起来放在剩余卡组的上方,那么我们就会拥有一个并不随机的卡组顺序。
另一个更常见的情况是我们将额外卡组的卡片放入主卡组。
在线上,一个合理的洗牌过程类似“从(没有顺序的)筐中拿卡”,初始状态不会产生任何影响,但在线下,任何确定的洗牌模式产生的牌序分布都包含对初始状态的依赖。
那么我们应该如何解消这种初始状态带来的影响呢?毫无疑问,我们应该选择一种合理的洗牌方式,使得无论初始状态是什么,在我们的洗牌结束后,其都会变成“完美分布”。
还需要注意的一点是,如果我们拥有一个完美分布的卡组,那么只要对方不进行类似“看卡顶的牌然后放入对应位置”的操作,无论是进行分堆,切牌还是插洗,产生的都仍旧是完美分布。
也就是说,如果我们提前将自己的卡组序列的概率分布洗成完美分布,那么对面的合理操作和任何指示都不会改变这一分布,也就是说至少我们自身的抽卡能保证公平。
我没有玩过万智牌,但是在写这篇杂谈翻阅资料时,我查到了万智牌中关于“未充分洗牌”的一些规定,例如:
“如果之后有充分洗过套牌,任何先行的操作、编排或是叠放都是可接受的。”
如果读者理解了上面所说的内容,想必就能更好地理解这句话的含义了。
在这里,”充分洗过套牌“就是指我所提到的”合理的洗牌方式“,如果采用了这种洗牌方式,无论开始洗牌前对方如何精心编排卡组顺序,都能保证最终进行公平的抽卡。
结论:为了达成合理的随机洗牌,我们应该寻找一种方案,使得无论对于何种初始状态,在洗牌结束后其分布都是完美分布。
3. 切牌与分堆
(所谓“大洗”)存在的问题,熵。
在上面内容的基础上,我们就不难理解切牌(例如将上方20张与下方20张交换)与分堆(例如分成8堆,每堆5张)的局限性了。
对于切牌,无论你如何操作,一个作弊牌手都可以构造一个牌序,使得被切牌后仍然可以100%抽到理想的手牌。
例如,如果一个40卡卡组换入了8张side,作弊牌手将side卡放在5n+1(1, 6, 11...)位置,则连续的5张牌中都必然包含一张side卡。
这说明切牌完全无法摆脱对初始状态的依赖,并且对于特定的牌序完全不能起到防止作弊的作用。
我们不妨把这个顺序称作“掏狗顺序”。
对于分堆,我们同样考虑换side。
首先,如果将side卡片连续地放进主卡组,那side的卡片在主卡组中依然相邻。
而如果连续放入8张side卡并进行了一次5*8的分堆(分8堆,每堆5张),在分堆结束的时刻,我们无论从哪里开始抽5张牌,其中都必然包含一张side卡。
换句话说,如果对面知道我们会对对面的卡牌进行一次5*8的分堆之后切牌,那么对面只要将side连续放入,就可以保证在我们分堆后变成“掏狗顺序”。
但反过来说,如果对面利用了这种分堆来构造一个“掏狗顺序”,我们只需要反过来进行一次8*5的分堆,就会让8张side牌相连,变成最差的牌序。
从某种意义上讲,分堆甚至不如切牌:对于切牌,对方不知道你会切多少张,也就是说至少你在选择张数的问题上可能是随机的,而对于分堆,如果你有一个确定的分堆策略(例如分5堆),那么根据初始状态可以直接计算最终状态,也就是说你没有产生任何的随机性。
这种“随机性”其实是可以量化的,也就是所谓的“熵”。
我们常说熵可以用来衡量系统混乱的程度,但从另一个角度讲,熵也可以表示掌握某个混乱系统的全部或者部分信息所需要的信息量。
如果你有3枚均匀的硬币,编号为1,2,3,那么为了保存掷这些硬币的正反面状态,你就需要3个比特(0或者1)来进行存储。
但如果我们知道“有两枚硬币正面向上,一枚反面向上”,那么我们其实是获取了一部分信息:我们不知道哪个编号的硬币正面向上,所以比起3个比特存储的信息我们知道得更少,但是我们至少知道了结果是011,101和110中的一个,所以我们比起完全随机猜测又知道得多了一些。
而衡量我们知道的信息量的一个简单的标准就是“比特的数目”。
简单来说,如果掷n枚硬币(有顺序),共有2的n次方种情况,那么我们完全知道其正反面情况比起完全不知道,就多掌握了n个比特的信息,简单推广一下的话,对于n种概率相等的情况,我们知道具体处于某一种情况,比起完全不知道,我们多掌握的信息就是loG_2(n)比特。
回到洗牌的问题,之前提到过,对于n张牌的完美分布,其本质就是n!种概率相等的情形,也就是说为了掌握当前的卡组处于某一种情况,我们至少需要loG_2(n!)比特的存储空间来存储其对应的编码。
一个简单的方案就是将每个情形编号为1~n!之间的整数,那么我们存储一个n!以下的整数即可(常用的方法是康托编码)。
与之对应,如果我们拥有2种牌序,每种的概率都是50%,那么我们可以用一个比特来编码我们当前的牌序。
在此基础上,我们可以考虑某一个操作对熵的影响。
对于分堆:很明显,一次分堆后,任何一个旧的状态的概率都会被直接复制到一个新的状态上。
如果我们拥有2种牌序,概率各50%,在分堆后我们依然拥有2种牌序,概率仍然是各50%。
那么结论就呼之欲出了:一个确定策略(例如5*8)的分堆不会带来任何熵的增加。
对于切牌:一个简单的模型是你可以选择0~39中的一个数字x,将上方x张牌放到下方,那么如果你用均匀分布选取x,那么你每次操作其实是为掌握卡组顺序增加了loG_2(40)比特的难度。
为了方便,考虑卡组是32张的情况,在你随机选取0~31中的数字时,从二进制位来看本质上就是进行5次“0或者1”的选择,每次增加的“难度”正好为1比特。
那么从一个确定的分布变为一个“完美分布”需要多少比特呢?对于40张牌的卡组,需要的比特数也就是loG_2(40!),大约是159。
如果你每次增加的难度是loG_2(40)比特(约5.32),那么理论上你进行约30次操作后,其难度就与完美分布相当了。
当然事实并不是这样,因为我们可以注意到,无论切多少次牌,其最终仍然是40种情况中的1种,这是因为切牌只能让我们的状态在全状态集合上的一个子集上运动,而不具有遍历性。
另一个角度来看,无论你切了多少次,其本质都可以通过切1次实现,也就是说你能提供的最大难度只有loG_2(40)比特。
当然,这里提到的切牌指的是最基本的“拿起来再放下”,如果你分成几堆分别切牌,是可以达成遍历的,但是如你所见,为了达成完美分布保守估计需要切牌30次以上,这在比赛中是很难实现的。
之前我们提到,对于完美分布,任何一个不根据看牌面确定位置的策略执行后仍然是完美分布。
事实上,在所有的分布中,完美分布具有最大的熵,换句话说掌握其具体状态的难度是最大的,而这些策略对于完美分布并不能改变其熵的大小。
另一方面,如果你的对手看了你的牌顶是增殖的G,随后将其放到下方,然后看到下一张是机械箱所以放在上方,这种情况下,即使你原本是完美分布,也会变成一个并不完美的分布,所以从另一个角度来说,你的对手执行的是一个“熵减”行为,类似于整理房间。
结论:切牌和分堆都不能预防作弊。
从熵的角度来看,分堆不能带来任何熵的变化,而切牌虽然有较小的熵增,但是因为其缺乏遍历性,所以一次切牌后就会抵达上限。
4. 为什么我们需要插洗?
兜兜转转终于回到了标题。
这里提到的插洗,指的是我们将牌分成两摞,随后将一摞插进另一摞的空隙中。
根据上文熵的概念,我们可以建立简单的模型来计算一次这样的插洗造成的熵的增加。
第一个简单的模型是,假设我们有2n张牌,我们将n张牌拿出来插入另外n张中,可以考虑为我们拿起的n张和被插入的n张的内部的相对顺序不发生改变。
这种情况下,一次插洗总共可能有C(2n, n)种情况,也就是我们从2n个位置中选n个位置给拿出的n张牌,剩下的n个位置属于留下的n张,而其顺序是确定的。
这种情形下,单次插洗所带来的“难度”是loG_2(C(2n, n))比特, 对于40张的卡组,n=20,每次带来的“难度”是约37比特,我们只需要大约4.30次插洗就能抵达所需的难度。
我们也可以构造另一个模型:将2n张卡分为两堆A和B,随后做如下的操作:只要A和B中都有卡片,就掷一枚硬币,若为正面则从A中取最上方的一张卡片,否则从B中取最上方一张卡片,放入当前卡组(初始为空)的最上方。
如果仅有一个卡堆有卡片,则不用掷硬币直接拿起最上方的卡片放到当前卡组最上方,直到两个卡堆都为空。
这或许更类似实战中的切洗,而这种操作每次可以贡献大约35比特的“难度”(计算方法略),为了达到完美分布的难度大于需要4.55次。
这两个模型的遍历性都很好验证。
需要注意的是我们之前提到完美分布是“难度”最高的分布,所以多次洗切后的“难度”并不能超过完美分布。
并且,上述模型非常粗糙,与现实的切洗也有所不同(例如现实中的切洗其实会导致顶端和底端的牌不会离顶部和底部太远),但是因为我们需要取对数,所以只要数量级大致正确即可,无非是再多插洗几次。
至于顶部和底部的问题,最简单的解决方案便是结合插洗与切牌,以求达成最好的效果。
结论:插洗是非常有效率的洗牌方式,简单模型中大约5次就可以达成接近完美分布的熵,结合切牌并且多插洗几次可以达到相当好的打乱效果,基本可以保证游戏公平。
总结
所有牌手都想抽到一副梦中情牌,但是游戏的性质决定了我们抽牌终将是随机。
在这种情况下,确保自己卡组和对方卡组的随机分布并不会让游戏的天平向自己倾斜,却能最大限度地保证参与的游戏的公平性。
最后,如果对方牌手试图对自己或者我方的卡组进行熵减操作,请不要犹豫立即呼叫裁判。
感谢阅读。返回搜狐,查看更多