mirror of
https://github.com/babysor/MockingBird.git
synced 2024-03-22 13:11:31 +08:00
38 lines
1.3 KiB
Python
38 lines
1.3 KiB
Python
|
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]
|
||
|
|