Есть ли способ в .NET генерировать последовательность all 32-битных целых чисел (Int32
) в случайном порядке без повторений и с эффективностью памяти? Эффективность памяти будет означать использование не более нескольких сотен мегабайт основной памяти.
В идеале последовательность должна быть чем-то вроде IEnumerable<int>
, и она лениво возвращает следующий номер в последовательности, только когда запрашивается.
Я сделал несколько быстрых исследований, и я нашел некоторые частичные решения для этого:
- Используя максимальный сдвиговый регистр с линейной обратной связью - если я правильно понял, он только генерирует числа в возрастающей последовательности и не охватывает весь диапазон
- Используя Fisher-Yates или другие алгоритмы перетасовки над коллекциями - это нарушит ограничения памяти, учитывая большой диапазон
- Сохранение набора-подобной коллекции и сохранение генерации случайного целого (возможно, используя
Random
) до тех пор, пока он не повторится, т.е. не в наборе - кроме того, что, возможно, не удовлетворяет требованиям к памяти, он будет смехотворно медленным при генерации последних чисел в последовательности. - Случайные перестановки более 32 бит, однако я не могу придумать способ обеспечения неповторяемости.
Есть ли другой способ взглянуть на эту проблему - возможно, используя фиксированный диапазон значений - это даст решение, удовлетворяющее требованиям к памяти? Может быть, в библиотеках классов .NET есть что-то полезное?
ОБНОВЛЕНИЕ 1
Спасибо всем за ваши идеи и творческие предложения для решения. Я постараюсь в ближайшее время реализовать и протестировать (как для правильности, так и для эффективности использования памяти) 2 или 3 наиболее перспективных решения, которые будут предлагаться здесь, опубликовать результаты и затем выбрать "победителя".
ОБНОВЛЕНИЕ 2
Я попытался выполнить предложение hvd в комментарии ниже. Я попытался использовать как BitArray
в .NET, так и мою собственную реализацию, так как .NET один ограничивается int.MaxValue
элементами, поэтому недостаточно для охвата всего диапазона целых чисел.
Мне понравилась простота идеи, и я был готов "пожертвовать" этими 512 МБ памяти, если бы он работал нормально. К сожалению, время работы довольно медленное, затрачивая до десятков секунд, чтобы генерировать следующий случайный номер на моей машине, который имеет процессор Core i7 с тактовой частотой 3,5 ГГц. К сожалению, это неприемлемо, если вы запрашиваете много, множество случайных чисел, которые должны быть сгенерированы. Я предполагаю, что это предсказуемо, но это алгоритм O (M x N), если я не ошибаюсь, где N равно 2 ^ 32, а M - количество запрошенных целых чисел, поэтому все эти итерации принимают свои потери.
В идеале я хотел бы генерировать следующее случайное число в O (1) раз, все еще удовлетворяя требованиям к памяти, возможно, следующие алгоритмы, предложенные здесь, подходят для этого. Я дам им попробовать, как только смогу.
ОБНОВЛЕНИЕ 3
Я только что протестировал Linear Congruential Generator, и могу сказать, что я очень доволен результатами. Он выглядит как сильный соперник за позицию победителя в этой теме.
Корректность: все целые числа, генерируемые ровно один раз (я использовал бит-вектор, чтобы проверить это).
Случайность: довольно хорошо.
Использование памяти: Отлично, всего несколько байтов.
Время выполнения: Генерирует следующее случайное целое очень быстро, как вы можете ожидать от O (1) алгоритма. Генерация каждого целого принимала в общей сложности ок. 11 секунд на моей машине.
В целом я бы сказал, что это очень подходящая техника, если вы не ищете высоко рандомизированные последовательности.
ОБНОВЛЕНИЕ 4
Модульный мультипликативный обратный метод, описанный ниже, ведет себя аналогично методу LCG - неудивительно, поскольку оба они основаны на модульной арифметике - хотя я нашел это немного менее простой для реализации, чтобы получить удовлетворительно случайные последовательности.
Одна интересная разница, которую я обнаружил, заключается в том, что эта техника кажется быстрее, чем LCG: генерация всей последовательности занимает около 8 секунд против 11 секунд для LCG. Помимо этого, все остальные замечания об эффективности памяти, правильности и случайности одинаковы.
ОБНОВЛЕНИЕ 5
Похож на пользователя TomTomудалили их ответ с помощью Mersenne Twister без уведомления после того, как я указал в комментарии, что узнал, что он генерирует повторяющиеся цифры раньше, чем это требуется. Поэтому я предполагаю, что это полностью исключает Mersenne Twister.
ОБНОВЛЕНИЕ 6
Я опробовал еще один предложенный метод, который выглядит многообещающим, Skip32, и, хотя мне действительно понравилось качество случайных чисел, алгоритм не подходит для создания всего диапазона целых чисел в приемлемом количестве времени. Так что, к сожалению, он не подходит по сравнению с другими методами, которые смогли завершить процесс. Я использовал реализацию в С# из здесь, кстати - я изменил код, чтобы уменьшить количество раундов до 1, но он все еще может" t закончить своевременно.
В конце концов, судя по результатам, описанным выше, мой личный выбор для решения относится к методу модульных мультипликативных инверсий, после чего тесно связан с линейным конгруэнтным генератором . Некоторые могут утверждать, что это уступает некоторым аспектам другим методам, но, учитывая мои первоначальные ограничения, я бы сказал, что он подходит им лучше всего.