【问题标题】:Debugging: Shuffle deck of cards in Python/ random调试:在 Python/随机中洗牌
【发布时间】:2014-01-06 03:39:21
【问题描述】:

我知道这个标题听起来很无聊,因为很多人已经问过这个话题了。我希望它可以帮助我深入了解随机模块的工作原理。问题是,我写了两个我认为应该相同的不同函数,但我得到的结果并不相同,我不明白为什么。

我希望最终得到一个“洗牌良好的套牌”。我只关心卡片是红色还是黑色,所以我的套牌很简单。我称“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)

结果,超过一半的牌(放入我的牌列表)是“红色的”,这是我在连续抽出 5 张 红色 牌后拿下这张牌的结果。这毫无意义,所以我寻找了一种不同的方式来创建套牌并得到了更正常的结果。但我仍然不知道我原来的套牌出了什么问题。

【问题讨论】:

  • “deckmaker 无法正常工作”... 有什么问题?它似乎对我有用。
  • 对不起,我当然应该更具体,但我自己不太明白这个问题。如果有帮助,我将添加我用来确定问题存在的代码。
  • 这是另一种提问方式:当您说“deckmaker 无法正常工作”时,是什么导致您得出这个结论?程序是否产生了您所期望的输出,如果是,输出是什么?您认为它会做什么?

标签: python algorithm random shuffle


【解决方案1】:

一般来说,除非您能够严格证明它可以正常工作,否则您永远不应该相信随机化方法可以正常工作。这通常很难。

为了深入了解您的问题,让我们概括一下有问题的函数:

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 的概率是多少?有两个答案:

  1. 如果M 零和M 的所有排列的可能性相等,则(2*M)-choose-M 中的概率为1(选择M 零位置的方法数)。

  2. 在函数构造牌组的方式中,2**M 中的机会是 1(0 和 1 在第一个 M 位置中的每个位置都有相同的可能性)。

一般来说,(2*M)-choose-M2**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() &lt; 0.0 不会发生);一旦我们选择了n/2nzero == nremaining 将是真的,if 测试总是会成功(random() &lt; 1.0 总是真的)。很可爱;-)

【讨论】:

  • 很好地完成了简化。我认为这很好地阐明了这个问题(并提供了对“非随机性”原因的洞察)——也许我没有仔细考虑过这一点,但这让我想起了Berand's Box Paradox
  • 知道了!当用一副纸牌进行真正的洗牌时,在第一张牌是红色之后,第二张牌不太可能是红色的,但我的功能每次都以 50% 的概率继续构建牌组。所以结果都是曲折的。感谢您的帮助。
  • 请注意:从“另一端”开始可能更容易看到问题:在开始时您已经获得了 25 张红牌之后,第 26 张红牌的机会太“应该”只有 27 分之一。2 分之一比这大得多;-)
【解决方案2】:

我不确定你的意思是什么,但我认为第一种方法生成的最后一张卡片很可能是相同的。

从 50 个或更少的样本中得到 26 个 1 或 26 个 0 的可能性似乎很大(我认为您可以使用交换二项分布来计算它)。即使是很小的机会也意味着您应该押注最后一张牌的颜色相同(因此其他牌的颜色相反)。

您可以做的只是添加 0 和 1,直到有 52 张牌,然后最后将随机的 1 更改为 0(或其他方式),直到每张有 26 张。

编辑: 假设这副牌有 4 张牌。前两种颜色是随机的:

25% (0, 0)
25% (0, 1)
25% (1, 0)
25% (1, 1)

但对于(0, 0)(1, 1),最后两张牌已经确定,将分别为(1, 1)(0, 0)。其他人需要多一个数字来确定结果:

25.0% (0, 0, 1, 1)
12.5% (0, 1, 0) > (0, 1, 0, 1)
12.5% (0, 1, 1) > (0, 1, 1, 0)
12.5% (1, 0, 0) > (1, 0, 0, 1)
12.5% (1, 0, 1) > (1, 0, 1, 0)
25.0% (1, 1, 0, 0)

如您所见,并非所有选项的可能性都相同。当然,52 张卡的差异更小,但想法保持不变。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-20
    • 1970-01-01
    • 1970-01-01
    • 2015-10-30
    • 2016-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多