Spaces:
Running
Running
import random | |
class RandomCycler: | |
""" | |
Creates an internal copy of a sequence and allows access to its items in a constrained random | |
order. For a source sequence of n items and one or several consecutive queries of a total | |
of m items, the following guarantees hold (one implies the other): | |
- Each item will be returned between m // n and ((m - 1) // n) + 1 times. | |
- Between two appearances of the same item, there may be at most 2 * (n - 1) other items. | |
""" | |
def __init__(self, source): | |
if len(source) == 0: | |
raise Exception("Can't create RandomCycler from an empty collection") | |
self.all_items = list(source) | |
self.next_items = [] | |
def sample(self, count: int): | |
shuffle = lambda l: random.sample(l, len(l)) | |
out = [] | |
while count > 0: | |
if count >= len(self.all_items): | |
out.extend(shuffle(list(self.all_items))) | |
count -= len(self.all_items) | |
continue | |
n = min(count, len(self.next_items)) | |
out.extend(self.next_items[:n]) | |
count -= n | |
self.next_items = self.next_items[n:] | |
if len(self.next_items) == 0: | |
self.next_items = shuffle(list(self.all_items)) | |
return out | |
def __next__(self): | |
return self.sample(1)[0] | |