Перетасовка огромного диапазона чисел с использованием минимального хранилища - PullRequest
4 голосов
/ 27 мая 2011

У меня есть очень большой диапазон / набор чисел, (1..1236401668096), который я бы хотел «перемешать», т. Е. Случайным образом пройти без повторного ввода того же номера.Я буду запускать веб-сервис, и каждый раз, когда поступит запрос, он будет увеличивать счетчик и вытягивать следующее «перемешанное» число из диапазона.Алгоритм должен учитывать, что сервер переходит в автономный режим, имея возможность возобновить обход, используя постоянное значение счетчика (что-то вроде того, как вы можете запустить генератор псевдослучайных чисел и получить то же самое псевдослучайное число с учетом начального числа ина какой итерации вы находитесь).

Мне интересно, существует ли такой алгоритм или выполнимо.Я видел Fisher-Yates Shuffle , но 1-й шаг - это «записать числа от 1 до N», что заняло бы терабайты памяти для всего моего диапазона.Генерация псевдослучайного числа для каждого запроса может работать некоторое время, но по мере заполнения базы данных / дерева коллизии станут более распространенными и могут ухудшить производительность (по моим расчетам вероятность столкновения составляет 0,08% после 1 миллиарда обращений).Есть ли более идеальное решение для моего сценария, или это просто несбыточная мечта?

Причина перетасовки заключается в том, что возможность правильно угадать следующий номер в последовательности может привести к незначительной уязвимости DOS вмое приложение, но также и потому, что уровень представления будет выглядеть намного лучше при более широком распределении чисел (я бы не стал вдаваться в подробности о , что приложение делает).На данный момент я рассматриваю только использование PRNG и работу с коллизиями или перетасовкой срезов диапазона (начиная с (1..10000000).to_a.shuffle, затем (10000001, 20000000).to_a.shuffle и т. Д., Когда номера каждого диапазона начинают исчерпываться).

У кого-нибудь из математиков есть лучшие идеи / предложения?

Ответы [ 3 ]

1 голос
/ 27 мая 2011

Разделяй и властвуй? Разбейте на управляемые куски и перемешайте их. Вы можете разделить диапазон номеров, например по их значению по модулю n. Список конструктивен и довольно мал в зависимости от n. Когда группа исчерпана, вы можете использовать следующую.

Например, если вы выбираете n из 1000, вы создаете 1000 разных групп. Выберите случайное число от 1 до 1000 (назовем это x) и перемешаем числа, значение которых по модулю 1000 равно x. После того, как вы исчерпали этот диапазон, вы можете выбрать новое случайное число от 1 до 1000 (очевидно, без x), чтобы следующий набор был перемешан. Не должно быть сложным отслеживать, какие числа из диапазона 1..1000 уже использовались, поэтому вам просто понадобится повторяющийся алгоритм перемешивания для чисел в подмножестве (например, индексы Фишера-Йейтса для их «индексов»). «).

1 голос
/ 27 мая 2011

Объединить последовательность PRNG или LFSR с /dev/random битами

Существует несколько алгоритмов, которые могут генерировать псевдослучайные числа с произвольно большими и известными периодами. Двумя очевидными кандидатами являются LCPRNG (LCG) и LFSR, но есть и другие алгоритмы, например Mersenne Twister.

Период этих генераторов может быть легко сконструирован в соответствии с вашими требованиями, и тогда у вас просто не будет столкновений.

Вы можете справиться с предсказуемым поведением PRNG и LFSR, добавив 10, 20 или 30 бит криптографически хешированной энтропии из интерфейса, подобного /dev/random. Поскольку детерминированная часть вашего числа, как известно, уникальна, она не имеет значения если ты когда-нибудь повторишь действительно случайную часть этого.

0 голосов
/ 27 мая 2011

Полагаю, лучше всего использовать GUID / UUID .Они созданы для такого рода вещей, и нетрудно найти существующую реализацию, отвечающую вашим потребностям.

Хотя столкновения теоретически возможны, они крайне маловероятны.Процитируем Википедию:

Вероятность одного дубликата составит около 50%, если каждый человек на земле будет иметь 600 миллионов UUID

...