Non Transitive Dice 之我探

来源:互联网 发布:java上传本地文件 编辑:程序博客网 时间:2024/04/29 05:10

NonTransitive Dice之我探

前些天在网上突然看到NonTransitiveDice,也就是非传递性骰子。它们的面值不是规则的1~6,而是可以重复,当然其面值也不只到6而已。其特性是非传递的,也即若A胜B,B胜C,则A不一定胜C,可以做到C胜A。比如下面的三个骰子:

A :2, 2, 4, 4, 9, 9

B :1, 1, 6, 6, 8, 8

C :3, 3, 5, 5, 7, 7

通过概率的计算,可以证明,A胜B的概率为20/36,B胜C的概率和C胜A的概率都是20/36

我希望通过程序来寻找这种非传递的骰子。还是使用python比较方便,但我不使用穷举法,因为当我需要寻找多个骰子的时候,穷举法的解空间太大,所以我还是进行一定次数的随机尝试吧,当然两次运行程序得到的结果可能不同。

首先,我需要一个函数来生成骰子的各个面,面值是从已知的数值范围内随机选择的,所以先定义面值取值列表:

#!/usr/bin/python

#-*- coding:utf-8 -*-

#FileName : dice.py

importrandom

'''

计算非传递性骰子的胜率

'''

DiceFace= range(1, 7)

这里设定面值为1~6.随机生成面值的函数(v1.0)如下:

# GenerateDie v1.0

defGenerateDie(facenum):

'''生成骰子的各个面值,共facenum'''

d= []

i= 0

whilei < facenum:

v= random.choice(DiceFace)

d.append(v)

i+= 1

d.sort()

returnd

diceFace中随机选择facenum个值来生成一个骰子的面值。

其次,我还需要一个函数来计算骰子A对骰子B的胜率,但为了获得更全面的信息,该函数还应该返回负率及和率。函数如下:

# countLtEqGt v1.0

defcountLtEqGt(num, L):

'''计算numL中小、等、大的个数'''

cl= 0

ce= 0

cg= 0

fort in L:

ifnum < t:

cl+= 1

elifnum == t:

ce+= 1

else:

cg+= 1

return(cl, ce, cg)


# pAB v1.0

defpAB(A, B):

'''计算骰子AB胜率,和率及负率'''

facenum= len(A)

pF= 0

pE= 0

pW= 0

fora in A:

(cl,ce, cg) = countLtEqGt(a, B)

pF+= cl

pE+= ce

pW+= cg

return(pF, pE, pW)

由于骰子各个面出现的概率相同,所以统计面值大小的个数,就相当于统计概率。有了函数pAB,就可以掌握任意两个骰子之间的战斗情况了。

最后,在主函数里,尝试一定的次数,生成3个骰子,使得骰子A胜B,B胜C,C胜A,并打印各个骰子的面值以及胜率。函数如下:

#主函数v1.0

if__name__ == '__main__':

N= 6 #骰子面数

T= N*N/2 #胜数之半


i= 0

n= 0

whilei < 100000:

dieA= GenerateDie(N)

dieB= GenerateDie(N)

dieC= GenerateDie(N)

(pFab,pEab, pWab) = pAB(dieA, dieB)

(pFbc,pEbc, pWbc) = pAB(dieB, dieC)

(pFca,pEca, pWca) = pAB(dieC, dieA)


cond1= (pWab > T) and (pWbc > T) and (pWca > T)

ifcond1:

n+= 1

print'dieA :', dieA

print'dieB :', dieB

print'dieC :', dieC

print'num %d : pWab = %d, pWbc = %d, pWca = %d' % (n, pWab, pWbc, pWca)

print''

i+= 1

print'find %d pairs' % n


在此寻找6面骰子,尝试100000次,先生成3个骰子,然后计算其相互之间的胜率,如果相互胜率都过半,则满足条件,成为非传递骰子,将其打印输出,并统计总共找到多少对骰子。

前面已经确定面值空间为1~6,运行该程序。等待...居然没有找到这样的骰子!即使把尝试次数增加10倍也无法找到。

把面值空间增加2倍,即取

DiceFace= range(1, 13)

再运行,找出了5对:

dieA: [1, 7, 9, 10, 10, 11]

dieB: [6, 6, 7, 7, 7, 12]

dieC: [1, 2, 4, 11, 12, 12]

num1 : pWab = 22, pWbc = 19, pWca = 19


dieA: [2, 3, 4, 5, 6, 12]

dieB: [1, 1, 3, 10, 11, 11]

dieC: [2, 6, 7, 7, 9, 9]

num2 : pWab = 19, pWbc = 19, pWca = 24


dieA: [4, 6, 6, 6, 6, 12]

dieB: [3, 3, 5, 6, 11, 12]

dieC: [1, 2, 7, 8, 8, 10]

num3 : pWab = 19, pWbc = 20, pWca = 20


dieA: [5, 5, 5, 5, 7, 12]

dieB: [2, 3, 4, 10, 11, 11]

dieC: [2, 4, 7, 8, 8, 10]

num4 : pWab = 21, pWbc = 19, pWca = 19


dieA: [2, 2, 3, 3, 12, 12]

dieB: [1, 1, 7, 9, 10, 11]

dieC: [2, 5, 6, 6, 7, 12]

num5 : pWab = 20, pWbc = 19, pWca = 20

观察一下文章开头给出的3个骰子,发现B里不含有A的面值,C里不含有B的面值,当然,A里也不含有B的面值。所以我们修改一个生成骰子的函数,加入排斥表,即生成的面值不能包含在该排斥表中:

# GenerateDie v1.1

defGenerateDie(facenum, exclude = []):

'''生成骰子的各个面值,共facenum'''

d= []

i= 0

whilei < facenum:

v= random.choice(DiceFace)

#如果该面值包含在排斥表中,则丢弃

ifv in exclude:

continue


d.append(v)

i+= 1

d.sort()

returnd


然后在主函数中把生成骰子的语句加入排斥表,函数如下:

dieA= GenerateDie(N)

dieB= GenerateDie(N, dieA)

dieC= GenerateDie(N, dieA+dieB)

如此一来,B中不含有A的面值,C中不含有A和B中的面值,把面值空间还原成1~6,运行程序。

等到天荒地老,都没有结果,再把尝试次数降低10倍,仍然没有结果。突然想到面值只有6个,在生成前面2个骰子之后,可能排斥列表就已经包含全部的6个值了,导致骰子C永远无法生成。有什么好的办法呢?

先别管上面那个问题了,再改进一下生成骰子的函数。再仔细观察文章开头给出的3个骰子,发现每个骰子的面值都可重复。为获得重复面值的骰子,这里再随机选择0和1,如果选择1则重复,否则不重复。函数如下:

# GenerateDie v1.2

defGenerateDie(facenum, exclude = []):

'''生成骰子的各个面值,共facenum'''

d= []

i= 0

whilei < facenum:

v= random.choice(DiceFace)

#如果该面值包含在排斥表中,则丢弃

ifv in exclude:

continue


d.append(v)

#50%的概率决定是否重复该面值

j= random.choice([0,1])

ifj==1 and len(d) < facenum:

d.append(v)

i+= 2

else:

i+= 1

d.sort()

returnd

再运行程序,发现程序轻松的在10000次尝试中找到30多对骰子。列举几个如下:

dieA: [1, 3, 9, 9, 9, 9]

dieB: [2, 5, 5, 7, 8, 8]

dieC: [4, 4, 4, 4, 10, 12]

num1 : pWab = 25, pWbc = 20, pWca = 20


dieA: [3, 3, 6, 8, 8, 12]

dieB: [2, 4, 5, 5, 11, 11]

dieC: [1, 1, 9, 10, 10, 10]

num2 : pWab = 20, pWbc = 20, pWca = 20


dieA: [3, 3, 3, 5, 9, 9]

dieB: [2, 2, 7, 7, 8, 8]

dieC: [1, 1, 4, 6, 10, 10]

num3 : pWab = 20, pWbc = 20, pWca = 19

如果把面值空间还原成1~6,仍然没有解,设置成1~9,同时把生成骰子的语句改成如下:

dieA= GenerateDie(N)

dieB= GenerateDie(N, dieA)

dieC= GenerateDie(N, dieB)


则可以在10000次尝试中找到7对如下:

dieA: [3, 4, 5, 5, 9, 9]

dieB: [1, 1, 8, 8, 8, 8]

dieC: [6, 6, 6, 7, 7, 9]

num1 : pWab = 20, pWbc = 20, pWca = 24


dieA: [2, 2, 3, 3, 8, 8]

dieB: [1, 1, 6, 6, 6, 6]

dieC: [3, 3, 4, 4, 5, 8]

num2 : pWab = 20, pWbc = 20, pWca = 20


dieA: [2, 3, 4, 5, 9, 9]

dieB: [1, 1, 7, 7, 7, 7]

dieC: [5, 5, 5, 5, 5, 9]

num3 : pWab = 20, pWbc = 20, pWca = 19


dieA: [4, 6, 6, 6, 6, 9]

dieB: [1, 1, 3, 8, 8, 8]

dieC: [2, 4, 7, 7, 7, 7]

num4 : pWab = 21, pWbc = 19, pWca = 20


dieA: [2, 6, 6, 6, 6, 6]

dieB: [3, 3, 4, 5, 8, 9]

dieC: [2, 2, 6, 7, 7, 7]

num5 : pWab = 20, pWbc = 20, pWca = 19


dieA: [3, 3, 4, 4, 9, 9]

dieB: [1, 1, 7, 7, 7, 7]

dieC: [5, 5, 6, 6, 6, 6]

num6 : pWab = 20, pWbc = 24, pWca = 24


dieA: [2, 2, 7, 7, 7, 7]

dieB: [3, 3, 5, 6, 6, 9]

dieC: [2, 2, 4, 8, 8, 8]

num7 : pWab = 20, pWbc = 19, pWca = 20


6

现在我想找5个骰子,每个骰子都是6个面,如果全部不重复,则需要30个面值,所以面值空间设置成130,即DiceFace= range(1, 31),尝试100000次。当然,骰子生成函数使用v1.2.

我不只是要得到简单的循环胜的骰子,而是要得到五行相克的骰子,即A胜B,B胜C,C胜D,D胜E,最后E胜A,同时还要保证C胜A,D胜B,E胜C,A胜D,B胜E,见下图

所以主函数不但要计算A胜B,B胜C,C胜D,D胜E和E胜A的概率,还要计算C胜A,D胜B,E胜C,A胜D,B胜E的概率,两个条件都满足才输出。函数如下:

#主函数v1.1

if__name__ == '__main__':

N= 6

T= N*N/2


i= 0

n= 0

whilei < 100000:

dieA= GenerateDie(N)

dieB= GenerateDie(N, dieA)

dieC= GenerateDie(N, dieA+dieB)

dieD= GenerateDie(N, dieA+dieB+dieC)

dieE= GenerateDie(N, dieA+dieB+dieC+dieD)

(pFab,pEab, pWab) = pAB(dieA, dieB)

(pFbc,pEbc, pWbc) = pAB(dieB, dieC)

(pFcd,pEcd, pWcd) = pAB(dieC, dieD)

(pFde,pEde, pWde) = pAB(dieD, dieE)

(pFea,pEea, pWea) = pAB(dieE, dieA)


(pFca,pEca, pWca) = pAB(dieC, dieA)

(pFdb,pEdb, pWdb) = pAB(dieD, dieB)

(pFec,pEec, pWec) = pAB(dieE, dieC)

(pFad,pEad, pWad) = pAB(dieA, dieD)

(pFbe,pEbe, pWbe) = pAB(dieB, dieE)


cond1= (pWab > T) and (pWbc > T) and (pWcd > T) and (pWde > T)and (pWea > T);

cond2= (pWca > T) and (pWdb > T) and (pWec > T) and (pWad > T)and (pWbe > T);

ifcond1 and cond2:

n+= 1

print'dieA :', dieA

print'dieB :', dieB

print'dieC :', dieC

print'dieD :', dieD

print'dieE :', dieE

print'num %d : pWab = %d, pWbc = %d, pWcd = %d, pWde = %d, pWea = %d' %(n, pWab, pWbc, pWcd, pWde, pWea)

print'num %d : pWca = %d, pWdb = %d, pWec = %d, pWad = %d, pWbe = %d' %(n, pWca, pWdb, pWec, pWad, pWbe)

print''


i+= 1

print'find %d pairs' % n

运行程序,没有找到,增加尝试次数到10倍也无法找到,将排斥列表改为如下:

dieA= GenerateDie(N)

dieB= GenerateDie(N, dieA)

dieC= GenerateDie(N, dieB)

dieD= GenerateDie(N, dieC)

dieE= GenerateDie(N, dieD+dieA)

也无法找到。

我怒了,把面值空间改为1~99,即DiceFace= range(1,100)。修改骰子生成函数为v1.3,即单个面值按照66.7%的概率重复3次,同时把尝试次数设为无穷大。完整的程序如下:

#!/usr/bin/python

#-*- coding:utf-8 -*-

#FileName : dice.py


importrandom

'''

计算非传递性骰子的胜率

'''

DiceFace= range(1, 100)

# GenerateDie v1.3

defGenerateDie(facenum, exclude = []):

'''生成骰子的各个面值,共facenum'''

d= []

i= 0

whilei < facenum:

v= random.choice(DiceFace)

#如果该面值包含在排斥表中,则丢弃

ifv in exclude:

continue

d.append(v)

i+= 1

#66.7%的概率决定是否重复该面值

j= random.choice([0, 1, 2])

ifj>=1 and i < facenum:

d.append(v)

i+= 1

ifi < facenum:

d.append(v)

i+= 1

d.sort()

returnd

defcountLtEqGt(num, L):

'''计算numL中小、等、大的个数'''

cl= 0

ce= 0

cg= 0

fort in L:

ifnum < t:

cl+= 1

elifnum == t:

ce+= 1

else:

cg+= 1

return(cl, ce, cg)

defpAB(A, B):

'''计算骰子AB胜率,和率及负率'''

facenum= len(A)

pF= 0

pE= 0

pW= 0

fora in A:

(cl,ce, cg) = countLtEqGt(a, B)

pF+= cl

pE+= ce

pW+= cg

return(pF, pE, pW)

if__name__ == '__main__':

N= 6

T= N*N/2

i= 0

n= 0

whileTrue:#i < 1000000:#尝试无穷次

dieA= GenerateDie(N)

dieB= GenerateDie(N, dieA)

dieC= GenerateDie(N, dieB)

dieD= GenerateDie(N, dieC)

dieE= GenerateDie(N, dieD+dieA)

(pFab,pEab, pWab) = pAB(dieA, dieB)

(pFbc,pEbc, pWbc) = pAB(dieB, dieC)

(pFcd,pEcd, pWcd) = pAB(dieC, dieD)

(pFde,pEde, pWde) = pAB(dieD, dieE)

(pFea,pEea, pWea) = pAB(dieE, dieA)

(pFca,pEca, pWca) = pAB(dieC, dieA)

(pFdb,pEdb, pWdb) = pAB(dieD, dieB)

(pFec,pEec, pWec) = pAB(dieE, dieC)

(pFad,pEad, pWad) = pAB(dieA, dieD)

(pFbe,pEbe, pWbe) = pAB(dieB, dieE)

cond1= (pWab > T) and (pWbc > T) and (pWcd > T) and (pWde > T)and (pWea > T);

cond2= (pWca > T) and (pWdb > T) and (pWec > T) and (pWad > T)and (pWbe > T);

ifcond1 and cond2:

n+= 1

print'dieA :', dieA

print'dieB :', dieB

print'dieC :', dieC

print'dieD :', dieD

print'dieE :', dieE

print'num %d : pWab = %d, pWbc = %d, pWcd = %d, pWde = %d, pWea = %d' %(n, pWab, pWbc, pWcd, pWde, pWea)

print'num %d : pWca = %d, pWdb = %d, pWec = %d, pWad = %d, pWbe = %d' %(n, pWca, pWdb, pWec, pWad, pWbe)

print''

#i+= 1

print'find %d pairs' % n

注意,骰子生成时使用的排斥列表只是上一个骰子,而不是前面全部的骰子。程序经过一个晚上的运行计算,得到几个结果:

dieA: [6, 37, 37, 37, 78, 78]

dieB: [22, 22, 22, 47, 63, 83]

dieC: [4, 45, 45, 45, 60, 60]

dieD: [30, 36, 36, 36, 93, 93]

dieE: [15, 15, 15, 64, 83, 91]

num1 : pWab = 19, pWbc = 19, pWcd = 20, pWde = 24, pWea = 19

num1 : pWca = 20, pWdb = 24, pWec = 21, pWad = 20, pWbe = 19

dieA: [22, 22, 29, 35, 85, 94]

dieB: [15, 20, 79, 79, 79, 83]

dieC: [42, 45, 45, 45, 72, 85]

dieD: [20, 20, 20, 81, 81, 90]

dieE: [4, 50, 50, 50, 60, 60]

num2 : pWab = 20, pWbc = 20, pWcd = 20, pWde = 21, pWea = 20

num2 : pWca = 24, pWdb = 19, pWec = 20, pWad = 23, pWbe = 26

dieA: [24, 24, 44, 44, 44, 95]

dieB: [4, 35, 35, 35, 59, 93]

dieC: [25, 25, 25, 57, 57, 57]

dieD: [3, 41, 41, 41, 51, 53]

dieE: [28, 28, 28, 46, 46, 96]

num3 : pWab = 20, pWbc = 21, pWcd = 21, pWde = 19, pWea = 22

num3 : pWca = 21, pWdb = 20, pWec = 21, pWad = 20, pWbe = 19

dieA: [30, 30, 30, 46, 91, 91]

dieB: [7, 7, 40, 80, 80, 80]

dieC: [31, 37, 37, 37, 65, 69]

dieD: [25, 25, 25, 60, 84, 84]

dieE: [23, 50, 50, 50, 55, 58]

num4 : pWab = 21, pWbc = 22, pWcd = 20, pWde = 21, pWea = 20

num4 : pWca = 20, pWdb = 21, pWec = 20, pWad = 24, pWbe = 19

dieA: [48, 48, 56, 56, 56, 83]

dieB: [18, 20, 47, 81, 81, 81]

dieC: [39, 39, 58, 59, 59, 59]

dieD: [32, 32, 32, 41, 87, 94]

dieE: [12, 29, 64, 70, 70, 70]

num5 : pWab = 21, pWbc = 20, pWcd = 22, pWde = 20, pWea = 20

num5 : pWca = 20, pWdb = 20, pWec = 24, pWad = 24, pWbe = 22

dieA: [26, 28, 28, 28, 61, 61]

dieB: [2, 8, 49, 49, 49, 57]

dieC: [23, 30, 30, 30, 41, 86]

dieD: [12, 12, 12, 47, 92, 92]

dieE: [3, 32, 32, 45, 45, 45]

num6 : pWab = 20, pWbc = 20, pWcd = 19, pWde = 21, pWea = 20

num6 : pWca = 22, pWdb = 20, pWec = 23, pWad = 20, pWbe = 25

dieA: [24, 24, 24, 48, 91, 91]

dieB: [1, 16, 76, 77, 77, 77]

dieC: [38, 38, 60, 60, 60, 94]

dieD: [19, 19, 19, 79, 79, 79]

dieE: [15, 62, 63, 63, 63, 66]

num7 : pWab = 20, pWbc = 20, pWcd = 21, pWde = 21, pWea = 20

num7 : pWca = 24, pWdb = 24, pWec = 25, pWad = 24, pWbe = 25

dieA: [14, 36, 59, 62, 62, 62]

dieB: [22, 24, 24, 24, 95, 95]

dieC: [1, 10, 70, 70, 70, 88]

dieD: [29, 29, 39, 39, 39, 90]

dieE: [17, 17, 17, 75, 75, 75]

num8 : pWab = 20, pWbc = 20, pWcd = 20, pWde = 21, pWea = 21

num8 : pWca = 24, pWdb = 24, pWec = 21, pWad = 22, pWbe = 24

以上就是五行骰子。

7

本文是我在网站http://www.guokr.com/blog/90573/上看到非传递性骰子:一个模型及猜想》文章,受到启发而作的。似乎它有一个结论:欲使胜率最大,则面值一定会重复。所以完全可能通过重新设计骰子生成函数GenerateDie来获得特殊骰子,这或许就是本文的建模了。

现在修改GenerateDiev1.4,即得到一个面值后,一定重复它。

DiceFace= range(1, 10)

#GenerateDie v1.4

defGenerateDie(facenum, exclude = []):

'''生成骰子的各个面值,共facenum'''

d= []

i= 0

whilei < facenum:

v= random.choice(DiceFace)

#如果该面值包含在排斥表中,则丢弃

ifv in exclude:

continue

d.append(v)

i+= 1

#重复该面值

ifi < facenum:

d.append(v)

i+= 1

d.sort()

returnd

此时仍然使用主函数生成3个骰子,函数为:

if__name__ == '__main__':

N= 6 #骰子面数

T= N*N/2 #胜数之半

i= 0

n= 0

whilei < 10000:

dieA= GenerateDie(N)

dieB= GenerateDie(N, dieA)

dieC= GenerateDie(N, dieB+dieA)

(pFab,pEab, pWab) = pAB(dieA, dieB)

(pFbc,pEbc, pWbc) = pAB(dieB, dieC)

(pFca,pEca, pWca) = pAB(dieC, dieA)

cond1= (pWab > T) and (pWbc > T) and (pWca > T)

ifcond1:

n+= 1

print'dieA :', dieA

print'dieB :', dieB

print'dieC :', dieC

print'num %d : pWab = %d, pWbc = %d, pWca = %d' % (n, pWab, pWbc, pWca)

print''

i+= 1

print'find %d pairs' % n

程序可以轻松找出一大堆骰子来。其中有一对是这样的:

dieA: [2, 2, 4, 4, 9, 9]

dieB: [1, 1, 7, 7, 8, 8]

dieC: [3, 3, 5, 5, 6, 6]

num28 : pWab = 20, pWbc = 24, pWca = 20

文章开头给出的骰子是:

A :2, 2, 4, 4, 9, 9

B :1, 1, 6, 6, 8, 8

C :3, 3, 5, 5, 7, 7

其相互胜率为20/36



原创粉丝点击