【问题标题】:Empty range for randrange() over a while loop in pythonpython中while循环中randrange()的空范围
【发布时间】:2019-04-07 21:38:54
【问题描述】:

我在通过 while 循环后从函数中获取随机 int 时遇到问题。该函数的目的是洗牌:

def shuffling(maindeck, shuffle_steps):
random.seed()
# sets a number of steps and creates a new array to be returned
steps = 0
shuffler = maindeck

while steps < shuffle_steps:
    firstR = random.randrange(len(maindeck) - 1)
    secondR = random.randrange(len(maindeck) - 1)

    shuffler[firstR], shuffler[secondR] = shuffler[secondR], shuffler[firstR]

    steps +=1

return shuffler

这是使用该函数的代码:

from deck import *
from shuffle import shuffling
gameState = True


while gameState:
    input("Welcome to War! Press any key to continue... \n")

    game_deck = shuffling(total_deck, 500)

    while gameState and len(game_deck) > 1:
        print("Both players draw a card...")

        playerCard = game_deck.pop(0)
        opponentCard = game_deck.pop(0)

        # some code

        keep_playing = input("Play again? (y/n) \n")
        if keep_playing is not ('y' or 'Y'):
           gameState = False

gameState = False

if len(game_deck) < 2:
    print("No cards left!")
    keepPlaying = input("Play again? (y/n) \n")
    if keepPlaying is ('y' or 'Y'):
        gameState = True

total_deck 是来自文件deck.py 的数组

这段代码在 while 循环的第一次迭代中运行良好,但是当循环迭代时出现错误:

 ValueError: empty range for randrange()

而且那个错误发生的时候

random.randrange(len(Maindeck) - 1) 

被调用,因为

len(Maindeck) - 1 

现在求值为等于或小于 0 的数字?为什么是这样?

【问题讨论】:

  • 您的洗牌算法不是统一洗牌。无论如何,除此之外,您可以使用 random.shuffle(maindeck) 代替您自己的洗牌功能。
  • 你注意到牌组的最后一张牌永远不会被洗牌吗?那是因为random.randrange(n) 永远不会返回n(甚至在文档中也这么说)。所以你应该把-1放到random.randrange(len(maindeck)-1)中。
  • @SilverSlash 关于non-uniform shuffle (maths details) 是正确的。还 +1 指向标准库。

标签: python python-3.x


【解决方案1】:

选项:

  • 如果您在列表中有一组牌并且想要重新排列整个序列,只需使用random.shuffle(sequence) 进行就地洗牌。

  • 如果您想获得它的随机副本,请使用random.sample() 并将长度设置为序列的长度。


这一行:

shuffler = maindeck

假设maindeck 是某种可变列表数据结构,shuffler 引用了与maindeck 相同的数据——你正在改组你的maindeck(伪装)并返回它——再往下你正在操纵它.你可以改用random.sample(),它会返回你主甲板的洗牌副本。

random.seed() 设置生成随机性的Mersenne_Twister 的起始内部状态 - 如果需要,请设置一次,但不是每次洗牌时都设置一次。 Seeding 具有固定值的随机数将导致相同的随机数,因为您使用相同的随机操作,因为每个随机操作都会更改内部状态 - 不需要任何种子的情况下使用它,默认情况下在源代码中完成:请参阅Python's random: What happens if I don't use seed(someValue)?

您的代码应使用random.sample(maindeck,k=len(maindeck))


代码:

import random
random.seed(42) # generate same states if same operations are used 

# https://en.wikipedia.org/wiki/Standard_52-card_deck
ranks = ["ace"] + [str(c) for c in range(2,11)] + ["jack", "queen", "king"]
colors = ["clubs","diamonds","hearts","spades"]

def generate_52_card_deck():
    """Returns a new deck of cards. Each card is a tuple of (rank,color)."""
    return [ (r,c) for c in colors for r in ranks  ]

deck = generate_52_card_deck()    
print(deck)

# inplace
random.shuffle(deck)
print(deck)

new_deck = random.sample(deck,k=52)
print("sampled deck:  ", new_deck[:10])
print("original deck: ", deck[::5])

输出:

# after generation (shortened)
[('ace', 'clubs'), ('2', 'clubs'), ('3', 'clubs'), ('4', 'clubs'), 
 ('5', 'clubs'), ('6', 'clubs'), ('7', 'clubs'), ('8', 'clubs'), 
 ('9', 'clubs'), ('10', 'clubs'), ('jack', 'clubs'), ('queen', 'clubs'),
 ('king', 'clubs'), 
 ('ace', 'diamonds'), ('2', 'diamonds'), ('3', 'diamonds'), ('4', 'diamonds'), 
 ('5', 'diamonds'), ('6', 'diamonds'), ('7', 'diamonds'),  ... , 
 ('jack', 'spades'), ('queen', 'spades'), ('king', 'spades')]


# after shuffling once (shortened)
[('10', 'clubs'), ('jack', 'diamonds'), ('king', 'diamonds'), ('4', 'clubs'),
 ('9', 'diamonds'), ('king', 'hearts'), ('4', 'diamonds'), ('ace', 'spades'), 
 ('7', 'diamonds'), ('queen', 'clubs'), ('8', 'spades'), 
 ('queen', 'diamonds'), ('8', 'hearts'), ('4', 'hearts'), ..., 
 ('9', 'spades'), ('2', 'clubs'), ('8', 'clubs'), ('2', 'spades')]

# first 10 cards ...
sampled deck:   [('4', 'spades'), ('king', 'hearts'), ('ace', 'diamonds'), 
                 ('jack', 'clubs'), ('queen', 'hearts'), ('2', 'hearts'),
                 ('6', 'diamonds'), ('3', 'spades'), ('8', 'hearts'), 
                 ('9', 'diamonds')]

# first 10 cards
original deck:  [('10', 'clubs'), ('jack', 'diamonds'), ('king', 'diamonds'), 
                 ('4', 'clubs'), ('9', 'diamonds'), ('king', 'hearts'), 
                 ('4', 'diamonds'), ('ace', 'spades'), ('7', 'diamonds'), 
                 ('queen', 'clubs')]

如果您需要卡片价值,请使用:

def get_base_card_value(c):
    # ace == 11 not done here
    v = {"ace":1 ,"jack":10, "queen":10, "king":10}
    return v.get(c[0]) or int(c[0])

【讨论】:

  • random.seed() 不带参数使用系统时间作为种子,这对于此任务应该足够了(除非@frank 在每场比赛前重置系统时钟:-))。
  • @digitalarbeiter 它没有任何用处,因为使用任何随机函数而不播种它,也需要系统时间来给它一个随机的内部状态 - 所以你可以跳过它......
  • 确实如此。我提出这个问题是因为你最初的回答似乎暗示 frank 种子他的洗牌是有问题的(而不仅仅是多余的)。
【解决方案2】:

啊!我看到了问题!您的游戏“用完”主牌组,因为 shuffling() 函数置换并返回 原始牌组:

shuffler = maindeck

不会创建主牌组的副本。因此

game_deck.pop(0)

也从主牌组中取出卡片。

修复:制作主卡组的深层副本并使用它(shuffling()):

import copy
...
shuffler = copy.deepcopy(maindeck)

瞧!每场比赛都有一个全新的游戏牌组。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-15
    • 1970-01-01
    • 2011-12-25
    • 2023-04-02
    • 2016-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多