繁体   English   中英

具有非重复元素和变化范围的随机发生器

[英]A random generator with non-repeating elements and changing range

我想实现一个随机生成器,以便它能够生成从 0 到 n 的随机数,但除非范围用尽,否则它不应该返回已返回的元素。 例如,如果范围是从0到7,并且上一代是4,那么在返回0-7中的所有整数之前,4不会出现在随机生成中。 这是我到目前为止所拥有的 - 我将生成的元素交换到数组的前面并缩小范围。

现在我必须实现一个 function 以在生成时更改随机生成的范围,并且在更改范围后非重复条件仍然成立。 function 采用下限和上限,表示新范围,可以小于或大于旧范围。 比如返回4后,我把范围改成2到7,那么列表2-7用完后,4就不会出现在生成中了。

我不知道这样做最有效的方法是什么。 我试图创建一个黑名单并在该范围内重新生成一个数组,其中的数字不在黑名单中,但是在列表用完后重置数组时遇到了一些问题。

class RandomGenerator:
    def __init__(self, n):
        self.n = n
        self.start = 0
        self.arr = list(range(n+1))
        
    def generate(self):
        if self.start > self.n:
            self.arr = list(range(n+1))
        
        r = random.randint(self.start, self.n)
        out = self.arr[r]
        
        temp = self.arr[self.start]
        self.arr[self.start] = self.arr[r]
        self.arr[r] = temp
        
        self.start += 1
        
        return out

对于小的n ,您可以生成一个数字列表, shuffle它,然后pop元素; 当它用尽时,生成一个新的source ,然后重复。

import random


def get_random_without_repeats(n=8):
    source = []
    while True:
        if not source:
            source = list(range(n))
            random.shuffle(source)
        yield source.pop()
        
        
for elt in get_random_without_repeats():
    print(elt)

您可以添加更复杂的逻辑以在耗尽后重新生成源,以满足您的需求。 例如,以下示例在每次源耗尽时从底部缩小值的范围

import random


def get_random_without_repeats(n=8):
    source = []
    turn = 0
    while True:
        if not source:
            source = list(range(turn, n))
            if not source:
                return StopIteration
            turn += 1
            random.shuffle(source)
        yield source.pop()
        

for elt in get_random_without_repeats():
    print(elt)

除非我遗漏了一些明显的东西,否则你可以在生成器中无休止地从random.sample中提取数字——它们保证永远不会重复,如果你迭代像itertools.cycle这样的无限迭代器,你就可以拥有可能的数字池用尽给定范围内的所有数字后“重置”:

from itertools import islice

def get_numbers(minimum, maximum):
    # minimum and maximum are inclusive
    from itertools import cycle
    from random import sample
    for generator in cycle([sample]):
        yield from generator(range(minimum, maximum+1), k=maximum+1)


print(list(islice(get_numbers(0, 7), 16)))

Output:

[0, 1, 4, 3, 7, 2, 6, 5, 1, 6, 0, 3, 7, 4, 5, 2]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM