我知道标题听起来很无聊,因为很多人已经问过这个话题了.我希望它可以帮助我深入了解随机模块的工作原理.问题是,我写的,我认为应该是相同的两个不同的功能,但我得到的结果是不相同的,我不明白为什么.
我希望最终得到一个"洗得很好的套牌".我只关心卡片是红色还是黑色,所以我的套牌非常简单.我称之为"1"红色和"0"黑色.
我的想法是通过附加1(红色),如果random.random()是> .5,或者除了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: [更多信息]我将解释我如何确定甲板制造商不起作用,以防重要.我在写这个程序模型拼图张贴在这里: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
0和M
1的排列可能相等,则机会为1 in (2*M)-choose-M
(选择M
零位置的方式的数量).
在函数构造牌组的方式中,机会为1英寸2**M
(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
总是如此).它真可爱 ;-)