【问题标题】:random iteration in PythonPython中的随机迭代
【发布时间】:2012-03-04 08:58:30
【问题描述】:

当你想按顺序迭代一个数字列表时,你会写:

for i in range(1000):
  # do something with i

但是,如果您想随机遍历 (0..999) 范围内的数字列表怎么办?需要(在每次迭代中)随机选择在任何先前迭代中未选择的数字,并且需要迭代范围 (0..999) 中的所有数字。

你知道怎么做吗(聪明)?

【问题讨论】:

    标签: python random for-loop


    【解决方案1】:

    这是一种以随机顺序迭代列表的不同方法。与使用 shuffle() 的解决方案不同,这不会修改原始列表

    lst=['a','b','c','d','e','f']
    for value in sorted(lst,key=lambda _: random.random()):
        print value
    

    或:

    for value in random.sample(lst,len(lst)):
        print value
    

    【讨论】:

    • 高度低估的解决方案,如果需要保留原始列表,则不需要创建列表的临时副本进行改组 - 并且不需要调用 numpy(尽管 numpy 的排列也是一个不错的选择解决方案)。 +1
    【解决方案2】:

    您可以使用random.shuffle() 来随机播放列表:

    import random
    
    r = list(range(1000))
    random.shuffle(r)
    for i in r:
      # do something with i
    

    顺便说一句,在许多情况下,您会在其他编程语言中对一系列整数使用 for 循环,您可以直接描述要在 Python 中迭代的“事物”。
    例如,如果你想使用i 的值来访问列表的元素,你最好直接打乱列表:

    lst = [1970, 1991, 2012]
    random.shuffle(lst)
    for x in lst:
      print x
    

    注意:在使用random.shuffle() 时应牢记以下警告(取自docs

    请注意,即使是相当小的 len(x),总 x 的排列大于大多数随机数的周期 发电机;这意味着长序列的大多数排列可以 永远不会生成。

    【讨论】:

    • @Greg:实际上我注意到 random.shuffle 修改了操作数,所以我什至不能将它用作表达式:/ 谢谢你的提示,不过,我改变了它。跨度>
    • 不用担心,我删除了我的评论,因为一旦你改变了它就不再适用了。 :)
    • 另外,Python 会自动为其随机数生成器播种,因此不需要调用 random.seed()
    【解决方案3】:

    numpy 中有一个函数 random.permutation() 可以为您完成此任务。 你的代码看起来像

    from numpy.random import permutation
    
    for i in permutation(1000):
        # do something with i
    

    【讨论】:

    • 不要把它误认为是 itertools.permutations() :)
    【解决方案4】:

    演示 Python 生成器和 Fisher–Yates shuffle

    import random
    
    def shuffled(sequence):
        deck = list(sequence)
        while len(deck):
            i = random.randint(0, len(deck) - 1) # choose random card
            card = deck[i]                       # take the card
            deck[i] = deck[-1]                   # put top card in its place
            deck.pop()                           # remove top card
            yield card
    

    您只会生成尽可能多的随机数。但老实说,它可能不会节省多少,所以你通常应该使用random.shuffle

    注意:如果选择顶部卡片,deck[i] = deck.pop() 将不安全,因此移除顶部卡片分两步完成。

    【讨论】:

    • 这可能是一个最被低估的答案。其他的(包括接受的)都将可迭代对象变成一个列表并打乱它。这适用于大多数(简单)情况,但如果您的迭代器(非常)大,内存效率低并且会杀死您的程序。例如,使用其他解决方案无法处理长列表的笛卡尔积。
    • @Fred:嗯?该解决方案的第一行是deck = list(sequence)。您可能可以通过懒惰地构建列表而侥幸逃脱,但它与其他人一样是渐近的时间和记忆。此解决方案仅允许您生成所需数量的随机数,而且随机数很便宜。
    • @Fred:如果您的迭代非常大,您应该考虑退后几级。例如,要获取长(已知大小)列表的笛卡尔积的一小部分,您可以假装该产品是一个扁平列表,然后以一种或另一种方式在该列表中生成一个不同索引的随机列表,您转换为每个组件列表的索引。您将使用源是列表的产品这一事实。
    • 是的,你显然是完全正确的。当我写下这个答案时,我不知道发生了什么。使用扁平列表的好提示!
    • @Fred 需要明确的是,它不是一个实际的列表,而是一个“假装”扁平列表,它会根据请求创建元素。您不需要存储任何元素。
    【解决方案5】:

    人们经常错过模块化的机会。你可以定义一个函数来封装“随机迭代”的思想:

    def randomly(seq):
        shuffled = list(seq)
        random.shuffle(shuffled)
        return iter(shuffled)
    

    然后:

    for i in randomly(range(1000)):
        #.. we're good to go ..
    

    【讨论】:

    • 好奇:为什么返回 iter 而不是 list?
    • 不知道为什么我退回了 iter。返回一个列表也应该没问题。
    【解决方案6】:

    使用 random.shuffle 方法:

    itrange = list(range(100))
    random.shuffle(itrange)
    for i in itrange:
        print i
    

    【讨论】:

    • 为了保证这个答案的未来,您需要在 Python 3 中使用 list(range(100)),因为 range 在 3.x 中返回一个迭代器。
    • 谢谢,现在正确了。请注意,对于长数组,并非所有排列都是可能的:docs.python.org/library/random.html#random.shuffle
    猜你喜欢
    • 2015-08-02
    • 2019-11-10
    • 2015-10-26
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    • 2017-06-10
    • 2012-11-07
    相关资源
    最近更新 更多