Я пытаюсь создать генератор, который возвращает числа в заданном диапазоне, которые проходят конкретный тест, заданный функцией foo
. Однако я хотел бы, чтобы номера проверялись в произвольном порядке. Следующий код достигнет этого:
from random import shuffle
def MyGenerator(foo, num):
order = list(range(num))
shuffle(order)
for i in order:
if foo(i):
yield i
Эта проблема
Проблема с этим решением заключается в том, что иногда диапазон будет довольно большим (num
может быть порядка 10**8
и выше). Эта функция может стать медленной, имея такой большой список в памяти. Я попытался избежать этой проблемы со следующим кодом:
from random import randint
def MyGenerator(foo, num):
tried = set()
while len(tried) <= num - 1:
i = randint(0, num-1)
if i in tried:
continue
tried.add(i)
if foo(i):
yield i
Это работает хорошо в большинстве случаев, так как в большинстве случаев num
будет довольно большим, foo
будет принимать разумное количество чисел, а общее количество раз, __next__
будет __next__
методом __next__
, будет относительно небольшим (скажем, не более 200 часто значительно меньше). Поэтому разумно предположить, что мы наткнемся на значение, которое проходит тест foo
и размер tried
никогда не становится большим. (Даже если она пропускает только 10% времени, мы не ожидали бы tried
получить больше, чем примерно 2000 примерно.)
Однако, когда num
мал (близок к количеству раз, что __next__
метод вызывается, или foo
терпит неудачу большую часть времени, указанное решение становится очень неэффективным - случайно угадывая номера, пока он не предполагает тот, который не tried
.
Мое решение...
Я надеялся использовать какую-то функцию, которая отображает числа 0,1,2,..., n
на себя грубо случайным образом. (Это не используется для каких-либо целей безопасности, и поэтому не имеет значения, является ли это не самая "случайная" функция в мире). Функция здесь (создание случайной биективной функции, которая имеет тот же домен и диапазон) отображает на себя 32-битные целые числа, но я не уверен, как адаптировать отображение к меньшему диапазону. Учитывая num
мне даже не нужна биекция на 0,1,..num
только значение n
больше, чем и 'close' to num
(используя любое определение закрытия, которое вы считаете нужным). Тогда я могу сделать следующее:
def mix_function_factory(num):
# something here???
def foo(index):
# something else here??
return foo
def MyGenerator(foo, num):
mix_function = mix_function_factory(num):
for i in range(num):
index = mix_function(i)
if index <= num:
if foo(index):
yield index
(до тех пор, пока биекция не находится на множестве чисел, массой больше, чем num
число index <= num
не верно, будет небольшим).
Мой вопрос
Можете ли вы подумать об одном из следующих:
- Потенциальное решение для
mix_function_factory
или даже несколько других потенциальных функций дляmix_function
которые я мог бы попытаться обобщить для разных значенийnum
? - Лучший способ решить исходную проблему?
Спасибо заранее....