crypt.8. 网上玩牌

来源:互联网 发布:python sorted函数 编辑:程序博客网 时间:2024/04/29 09:26

from:2017 CCF计算机课程改革导教班. 陈道蓄

18

本章将探讨并非坐在一起的玩者如何玩扑克之类的纸牌游戏。牌经由电子邮件分发。与网上的商业扑克游戏系统不同,由玩者洗牌发牌。并不需要一个所有玩者必须信任的控牌人。显然其中有些难处:玩者必须通过电子邮件发送信息以实现洗牌与发牌,但却不能因此获得有关自己发出的牌的任何信息。而且任何牌不能被发两次,这就要求发牌人知道哪些牌已经发过了。最后一点,如果其中有人使诈,其他人应该能发现。

通过蜗牛邮政发牌

为了便于理解基本想法,我们暂且假设我们通过蜗牛邮政系统交换邮件。首先看看两个玩者,AliceBob。他们在不同的地方,无法碰面。每个玩者想欺骗对方似乎很容易,只需在手边另放一副同样的纸牌。他/她可以私下从自己那副牌中选一手对自己有利的牌,例如同花大顺。为避免这种事发生,每张牌必须有个封签,表示它是正常使用的,而非有人从另一副牌取来插入的。

如何洗牌和发牌

我们用的牌每副有52张,草花A,草花2方块K等等。洗牌后每个玩者拿5张牌。怎么能让一个玩者洗了牌,发了牌,却不知道对方拿到了什么牌呢?我们用信封放每张牌,52张牌被分别放入不同的黄色信封。接下来,确认谁往哪个信封中放了牌是至关重要的。Bob将所有信封封口并加自己的封签。

随后Bob像洗牌一样上下搬弄了几下后就将所有信封寄给AliceAlice从信封上完全看不出里面装的是什么牌,因此她不可能给自己选好牌,把差牌分给BobAlice只能再将整叠信封上下搬弄几下,便从中随机选出5个信封给自己,另外再选5个给BobAlice随即把为Bob选的5个信封寄回Bob处。这时两个玩者可以打开自己的信封拿到牌了。

玩者能使诈吗?例如Alice再打开别的信封试图遇到更大的牌点,这样可以选更好的牌。显然无法避免Alice这样做。但由于信封上的封签,Alice并不能将一张牌放回原来的信封,也不能将其放入另一个信封。如果她打开了其它信封,以后若两人相遇她无法将这些信封还给Bob而不被发现做过的手脚。Bob将牌装入信封时也能使诈。例如,他可能自己留一张牌,让一个信封空着。在游戏过程中一旦拿到空信封,他就可以使用私下留的那张牌了。但如果他没拿到空信封对他就不利了,因为事后双方相遇时核对一下信封就会发现他使的诈。

如何下注

下一步该下注了。你打算如何下注当然可以写在信中。我们不需要什么新方法。

如何换牌

叫牌后每个玩者可以从剩下的牌中任意选择一张或多张替代手上的牌。首先Alice可以选n张牌,这里n15之间的某个整数。这里有点儿问题:Alice必须先决定丢弃的n张牌,然后再选新牌。否则就像前面提到Alice可能使诈的情况,她可以先选n张新牌,然后在5+n张牌中选最好的5张。这就要求Bob必须介入Alice换牌的过程。

另外,Alice也不能将剩下的42个信封寄还给Bob,否则她就没法证明自己确实是先弃牌然后才选新牌的。甚至Bob也有可能在黄色信封上做了什么隐秘的标记区分信封中是好牌还是差牌。例如他可以在封签的字体中略做变化,可能做得比下图所示还不显眼,图中小写字母b的写法有点差异。

不过利用信封就可以解决这个问题。Alice将剩下的42个黄色信封放入略大些的红色信封中,签上自己的封签,按随机方式叠放后寄给Bob

Alice还将自己丢弃的n张牌放入特别的信封也寄给BobBob不能打开这些信封,他不应知道Alice丢掉哪些牌。以后两人碰头时可以验证此信封依然封口,并且丢弃的确实是n张牌。

Bob并不能区分红信封中的内容,他只能从中随机选择n个寄回给Alice。这样Alice就拿到了新换的牌。

Bob换牌的过程是一样的,他得将红色信封装入另一层信封内。

摊牌

玩者将自己的牌展示给对方,游戏就此结束。每个玩者给对方发封信报上自己手中的牌。没人保留手上的牌作为事后的证明。赢者即可确定。

如何确认没有人作弊

双方均有多种可能违背规定的规则欺骗对方。例如每个玩者可能打开超出规定数量的信封以便选到更好的牌或者以此推断对方手上的牌。不过只要游戏结束后双方有机会碰面这样的作弊很容易被发现。双方可以将牌展示给对方并一起打开尚未开启的信封,以此证明自己没有作弊。

讨论

上述玩牌的方式有几个明显的缺点:

l 将牌放入信封只能手工完成,代价较大,而且信封不能反复使用。

l 在双方下次相遇之前无法判定游戏过程中是否有人作弊。

l 蜗牛邮政过慢,等待会让人兴趣索然,而且费用显然高于电子邮件。

l 一旦信件丢失,游戏就无法继续。当一个玩者觉得自己输定了,便可毁弃邮件。想发现谁拦截了邮件是不可能的。

问题在于是否能有一种“电子”信封,可以像纸质信封一样使用,但性能更好。特别是这样的信封能由计算机生成,并通过电子邮件传送。这可以省去手工放入纸牌的负担,而且发送者可以保存电子邮件,万一丢失可以重发。

用电子邮件处理纸牌

电子信封

如何实现电子信封呢?第一个想法是对纸牌编码。 Bob创建这些编码,经混排后发送给AliceAlice不知道牌点与编码之间的对应关系,她是在不知牌面的条件下选牌。在介绍如何使用编码洗牌发牌之前,我们先考虑只要Bob需要拿牌的特例。假定每张牌有个固定的数值表示,游戏双方均知道这个数值。假设0表示草花A1表示草花22表示草花312表示草花K13表示黑桃A,余此类推,黑桃后面是红心,最后是51表示方块K

如何洗牌并将牌发给Bob

开始时Bob随机生成纸牌编码,也就是下面这样的表:

牌面 编码 牌面  编码

0(草花A)   ®    1 5(草花6)    ®    0

1(草花2)   ®    42 6(草花7)    ®    43

2(草花3)   ®    22  

3(草花4)   ®    25

4(草花5)   ®    51 51(方块K)   ®    13

左列是所有牌面,右列随机创建的每种牌面对应编码,码值是051之间的整数,每个数恰好用一次。Alice不知道表的内容。

要给Bob5张牌,Alice051之间随机选5个不同的整数发送给BobBob用这张表找出自己的牌。例如,Alice选了01134251Bob拿到的牌就是草花6、草花A、方块K、草花2和草花5Alice并不知道表的内容,因此她无法影响Bob会拿到什么牌。不过选的编码Alice是知道的,所以不会有重复。

这就像使用真正的信封。如果要将草花A放入一个黄色信封,Bob的做法是分配给这张牌一个数字,上面的例子中分配的是1信封中装了什么Alice看不到。这里一样,Alice也不知道1究竟是那张牌。两种方式下,Bob拿到什么牌都不会受到Alice的影响。

但游戏结束时,Bob有可能诡称他创建的表示另一个样子,这样他可以为自己选好牌。因此必须让Bob建的编码表固定下来,以后不能被他修改。我们下面来会介绍如何利用单向函数实现这一点。

单向函数

先回顾一下第14章中介绍的单向函数。我们称f是单向函数意味着f的函数值很容易计算,但其反函数f-1的值很难计算。第14章中我们举了电话号码簿的例子。计算单向函数f的值相等于根据姓名查电话号码,这显然很容易。而反函数的计算就相当于根据电话号码查该号码的使用者,这在通常的号码簿中显然不容易。

我们该如何利用单向函数避免Bob设定纸牌的编码表后又悄悄地改动它呢?类似于电话号码簿,假设AliceBob都有一本编码表的号码簿,其中包含大量的编码表所对应的编号。

Bob发牌的过程如下:Bob先从号码簿中随机选一个编码表(例如选第569页中最下面那个)Bob把这个编码表的号码发给Alice。这里的号码是039784如果Alice想找到这个表,她得扫描整个索引书。如果这需要太多时间,其实她就无法根据Bob告诉她的号码找到相应的编码表。那她能做的就是随机的地在051之间选5个数字发给Bob。而Bob则能根据相应编码表得到自己的牌。0号牌(草花A)编码为11号牌(草花2)编号为42,等等。游戏结束后,Bob将使用的编码表在索引书中的位置发给AliceAlice知道了表的内容就能验证这是否确实是Bob在游戏开始时使用的编码表。而起她还可以确认Bob拿到的牌是否真是她指定的那些牌。

如何换牌

换牌很方便。在前面的例子中如果Bob想将手上的草花2替换掉,只要告诉Alice他打算换掉编码为42的牌。由于Alice并不知道草花2所对应的编码,她就不会知道Bob想丢弃的究竟是那张牌。随后Alice选一个新数码作为新牌发给Bob,她知道已经发过的所有牌的编码,所以不会将一张牌发两次。这样虽然Alice并不知道Bob拿了哪些牌,也不会重复。

数学描述

现在我们用比较数学化的形式来解释上面的过程。编码表的号码簿就相当于一个单向函数f。这个函数将书中某个编码表的位置映射到一个数字。在上述例子中,Bob随机从书中选一个位置为x的编码表,并将f(x)发送给Alice。由于f是单向函数,Alice很难从f(x)算出x。换句话说Alice要想知道x就得搜索整本书。游戏结束后Bobx告诉AliceAlice就很容易算出f(x),因此可以这确实是游戏开始时Bob告诉她的号码。这样Bob也不可能悄悄地改用其它编码表来欺骗Alice

另一方面,对计算机而言保存或搜索整本书都是很容易的。我们并不采用号码簿,而是直接利用单向函数从编码表算出一个数值,这个数确定相应的编码表。这里不再讨论单向函数的细节。

每个编码表也被看作一个函数。上面我们已经提到每张纸牌有个代码。编码表可看作函数b,将每张纸牌的代码(051之间的整数)映射到各自的编码上(也是051之间的整数)。其实编码表也可以认为描述了f的反函数b-1反函数将每个纸牌的编码映射到各自的代码。要计算f-1(z)我们搜索编码表的右列,在左列种读出结果。对于只有52行的表当然很简单。在右列种每个数只出现一次,由此可知对所有的xb-1(b(x))=x

给两个玩者发牌

要给两个玩者,AliceBob,发牌,我们使用两个分别创建的不同编码表,各人的编码表不让对手知晓。假设a,b分别为对应于AliceBob各自编码表的函数。函数a,b满足对于051之间的任意数,a(b(x))=b(a(x))数学家称之为可交换函数。两个可交换函数用在自变量x上,结果不受先后次序的影响。

下面是可交换函数的例子:

这个例子中没有列出完整的编码表,而是定义了由编码表左列的项的计算右列对应项的函数。很容易验证a(b(x))b(a(x))结果一致:要计算a(b(x))先在x上加37,在加25,若结果大于51我们就减去52。计算b(a(x))无非是颠倒两次加法的顺序。不仅对3725,不管加什么数,顺序变化不影响结果。

这么简单的可交换函数显然不适合编码表应用。若Bob得知函数a对自变量x的值a(x),立刻可知Alice用于加法的数25。这样Bob也就知道了函数a,当然能破译Alice的编码。

确保在用的编码表始终一致

我们仍然用a,b分别表示AliceBob选定的编码表。前面说过, 我们用一个单向函数fAlice计算f(a)并发给Bob;类似地Bob计算f(b)发给Alice。因为f是单向函数,两人均不能获知有关对方的编码表的信息。但两人都按照选定的编码表a,b发送信息。游戏结束后,Alice将自己的编码表a发给Bob,这样Bob便可计算f(a)并核查a是否真实Alice在游戏开始时选定的那个编码表。同样Alice也会从Bob那里得到b,也可以核查b是否就是Bob选的。

牌放入信封

Alice只需计算f(x)就可以将牌x放入信封。当需要将x从信封中取出是,她用a的反函数计算a-1(a(x))因为这恰好是x。同样Bob可以用函数b将牌x放入信封。我们用的纸牌代码与它们的编码(即信封)均为051之间的整数,Alice就可以将Bob封口的信封b(x)放进自己的信封a(b(x))

在前面使用蜗牛邮政系统的扑克方案中,Bob先将纸牌放入信封,后来Alice又将这些信封放入另外更大的信封。Bob做的相当于计算b(0), b(1),…, b(51)显然这恰好是051之间的51个不同的数值。后面Alice计算a(b(0)), a(b(1)), …, a(b(51))得到的仍然是051之间那些数值。两人不用计算就知道这个集合。但Alice不知道b,她也就不可能知道某个特定编码究竟是什么牌。同样Boba也一无所知。

发牌给Bob

每发一张牌给Bob, Alice从未用过的编码中选一个。她并不知道b因此只能随机选择。Alice选定的编码可以看作a(b(x)),于是她能计算出b(x)=a-1(a(bx))=b(x)因为对任意z: a-1(a(z))=z然后Aliceb(x)a(b(x))发给Bob。用前者Bob能算出x就知道自己拿到哪张牌;用后者他可以更新尚未使用的牌的列表,即将a(b(x))从表中删除。

弃牌

要想丢弃一张牌x,还不能让对方知道有关x的任何信息,只需将a(b(x))发送给对方。对方并不能根据a(b(x))的值获知有关弃牌的信息。

电子信封的特性

我们来比较一下电子信封与纸质信封:首先我们看如何给Alice发牌。Bob将纸牌放入黄色信封对应于应用函数b。而Alice将这些黄色信封放入红色信封就相当于应用函数a。选定给Alice的牌之后,如果用的是电子信封,Bob无须打开外层的红色信封,就能在不知道黄色信封中究竟装了什么的情况下利用函数b-1撤掉黄色信封。这样Alice用函数a-1打开红色信封就直接可以看到其中的内容了。这说明电子信封具有纸质信封不具备的某些性质:

l Alice打不开黄色信封,Bob也打不开红色信封。这就意味着游戏结束后没有必要再去确认玩者没打开过不该用的信封,因为他们不可能打开。

l 可以复制信封连同其中的内容,但复制者不会得知内容甚至得不到任何线索。

l 一张纸牌放入黄色信封然后再一起放入红色信封,其效果完全等同于将这张牌先放入红色信

封然后再一起装入黄色信封。因此可以不打开红色信封直接去除其中的黄色信封。

如何检查对方是否作弊

游戏结束时,双方均将自己的编码表,分别为a,b,发送给对方。各人可自行计算f(a)f(b),并确认是否对方确实始终使用这个编码表。有了对方的编码表,玩者能验证所有计算。游戏过程中要打开信封必须两人合作,因为必须计算a-1b-1,这样见面检查是否打开过不该用的信封就没有必要了。

两个以上玩者的扑克游戏

我们只考虑了两个玩者的情况。如果有三个甚至更多的玩者会怎么样呢?我们当然可以试着将前面的讨论推广到更多的玩者。不过有个关键的问题。我们的前提是两人不见面。第三个玩者有办法防止AliceBob通过手机互通关于各自手中牌的信息吗?网上流行的扑克系统有可能让所有参与者互不相识。还有一种可能,分析其他玩者的行为特征,例如是否牌最差的玩者总是不下注。不管如何,要想证明其他玩者作弊是很困难的。所以要多人一起玩,还是聚在一起更好,那样也会更有气氛。