我知道这个标题听起来很无聊,因为已经有很多人问过这个话题了。我希望它能帮助我深入了解随机模块的工作原理。问题是,我写了两个我认为应该相同的不同函数,但我得到的结果并不相同,我不明白为什么。
我希望最终得到一个“洗牌良好的套牌”。我只关心牌是红的还是黑的,所以我的套牌很简单。我称“1”为红色,“0”为黑色。
我的想法是通过在 random.random() > .5 时附加 1(红色)或在其他情况下附加 0(黑色)来构建套牌,然后在我达到 26 时自动附加 1 或 0(半副牌)一种颜色。但是出了点问题。 deckmaker() 无法正常工作,但 deckmaker2() 可以。谁能提供见解?
import random
def deckmaker():
deck = []
for i in range(52):
if deck.count(0) == 26:
deck.append(1)
elif deck.count(1) == 26:
deck.append(0)
elif random.random() > .5:
deck.append(0)
else:
deck.append(1)
return deck
def deckmaker2():
newdeck = []
for i in range(26):
newdeck.append(0)
for i in range(26):
newdeck.append(1)
deck = []
for i in range(52):
x = random.randint(0,len(newdeck)-1)
deck.append(newdeck.pop(x))
return deck
注意:在写这个问题时,我发现了 random.shuffle 列表运算符,它与我的第二个函数做同样的事情,所以当然得到洗牌很容易。但我仍然想知道为什么我的原始代码没有做同样的事情。
已编辑:很抱歉对 deckmaker() 的确切问题含糊不清。问题是,我不完全明白出了什么问题。它与这样一个事实有关,即在它产生的牌组中,当你一张一张地“翻开”牌时,有一些策略可以让你预测“下一张牌”是红色还是黑色,而不是使用使用 random.shuffle 创建的套牌
编辑 2: [更多信息] 我将解释我是如何确定 deckmaker 不起作用的,以防它很重要。 我正在编写这个程序来模拟此处发布的拼图:http://www.thebigquestions.com/2013/12/17/tuesday-puzzle-4/
我的策略是记住最后发的几张牌,并使用这些信息来决定何时拿下一张牌。我想也许在连续拿到 5 张“黑”牌后,是预测“红”的好时机。我是这样实现的:
mycards = []
for j in range(1000):
mydeck = deckmaker(52)
mem_length = 5
mem = []
for c in range(mem_length):
mem.append(4)
for i in range(len(mydeck)):
if mem.count(0) == mem_length:
mycards.append(mydeck[i])
break
elif i == len(mydeck)-1:
mycards.append(mydeck[i])
break
else:
mem.append(mydeck[i])
mem.pop(0)
x = float(mycards.count(1))
print x/len(mycards)
结果我拿的牌(放入列表 mycards 中)有一半以上是“红色”,这是我在连续抽到 5 张 红色 牌后拿牌的结果。这毫无意义,所以我寻找了一种不同的方式来创建套牌并获得了更正常的结果。但我仍然不知道我原来的牌组出了什么问题。
最佳答案
一般来说,除非您可以严格证明它是正确的,否则您永远不应该相信随机化方法是正确的。这通常很难。
为了深入了解您的问题,让我们概括一下有问题的函数:
import random
def deckmaker(n):
half = n // 2
deck = []
for i in range(n):
if deck.count(0) == half:
deck.append(1)
elif deck.count(1) == half:
deck.append(0)
elif random.random() > .5:
deck.append(0)
else:
deck.append(1)
return deck
这是一个小驱动程序:
from collections import Counter
c = Counter()
for i in range(1000):
c[tuple(deckmaker(2))] += 1
for t in sorted(c):
print t, c[t]
运行:
(0, 1) 495
(1, 0) 505
所以这两种可能性的可能性差不多。好的!现在试试 4 号牌;只需像这样更改相关行:
c[tuple(deckmaker(4))] += 1
运行:
(0, 0, 1, 1) 236
(0, 1, 0, 1) 127
(0, 1, 1, 0) 133
(1, 0, 0, 1) 135
(1, 0, 1, 0) 130
(1, 1, 0, 0) 239
糟糕!如果愿意,您可以运行正式的卡方检验,但通过检查可以明显看出两个排列(第一个和最后一个)的可能性大约是其他四个排列的两倍。所以输出甚至不能说是随机的。
这是为什么呢?想想看 ;-)
提示
对于尺寸为 2*M
的甲板,第一个M
的机会是多少?条目都是0?有两个答案:
如果
M
的所有排列零和M
可能性相等,机会是(2*M)-choose-M
中的 1 (选择M
零位置的方法数)。在函数构造一副牌的方式中,
2**M
中的机会为 1 (0 和 1 在第一个M
个位置中出现的可能性相同)。
一般来说,(2*M)-choose-M
比 2**M
大得多,因此该函数构造一个全零开始的牌组的频率远高于“它应该”的频率。对于一副 52 张牌 ( M == 26
):
>>> from math import factorial as f
>>> one = f(52) // f(26)**2
>>> two = 2**26
>>> float(one) / two
7389761.998476148
因此,“以 26 个零开头”的可能性比应有的可能性高出 700 万倍以上。很酷:-)
“一次一个”地做
那么是否有可能一次正确地选择 0 或 1 个?是的!您只需要使用正确的概率:当有 nzero
时剩余的零被挑选,和nremaining
剩余的“卡片”总数,概率为零 nzero / nremaining
:
def deckmaker(n=52):
deck = [None] * n
nremaining = float(n)
nzero = nremaining / 2.0
for i in range(n):
if random.random() < nzero / nremaining:
deck[i] = 0
nzero -= 1.0
else:
deck[i] = 1
nremaining -= 1.0
return deck
请注意,无需计数。当nzero
变为 0.0,if
测试永远不会成功(random() < 0.0
不可能发生);一旦我们选择n/2
一个,nzero == nremaining
将是真实的,if
测试将始终成功(random() < 1.0
始终为真)。很可爱 ;-)
关于python - 调试 : Shuffle deck of cards in Python/random,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20650030/