Как вы можете пройти через целочисленный диапазон в непредсказуемом порядке?

Как вы можете перебрать фиксированный диапазон целых чисел (скажем, 100000-999999) в сложном для прогнозирования порядке?

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

т.е. Я бы хотел что-то более элегантное, чем просто A) выбрать случайное число, а затем B) проверить, было ли оно уже использовано, и если так, вернитесь к шагу A по следующим причинам (если вы не можете убедить меня иначе):

  • который действительно будет сосать, когда вы начнете заканчивать номера
  • сообщение о том, что у вас закончились неиспользованные номера, может быть непомерно дорогостоящим
  • Такой подход мог бы также иметь проблемы с concurrency, если у вас есть много клиентов или потоков, пытающихся сделать это в одно и то же время из того же диапазона.

Ответ 1

Линейные конгруэнтные генераторы случайных чисел (покрытые мучительной деталью в объеме 2 Кнута) будут проходить каждое значение в данном диапазоне без повторения, что непросто предсказать. Фундаментальное утверждение

v = k * v + l mod m

где m - размер множества, k и l взаимно просты к m (я считаю, что это достаточно, чтобы гарантировать, что он работает по желанию), а v - используемое значение. Выберите начальное значение более или менее случайным образом и оттуда.

Одно из преимуществ заключается в том, что он довольно быстро записывается, предполагая, что вы можете избежать переполнения (либо путем ограничения k и m, либо с помощью арифметических подпрограмм произвольной точности).

Ответ 2

Вы должны посмотреть на линейный регистр сдвига обратной связи. "Максимальная длина" LFSR будет ударять каждое число в диапазоне (2 ^ n -1), за исключением 0. Вы можете сохранить первый номер, так что вы узнаете, когда он вернется к началу, или вы можете просто подсчитать образцы. Проблема в том, что она детерминирована, поэтому технически вы можете предсказать ее, если знаете алгоритм. Также, учитывая любое число в последовательности, последовательность всегда одна и та же от этой точки.

У вас может быть список пучков алгоритмов LFSR и случайным образом выбирать один, прежде чем вы начнете, чтобы он был менее предсказуемым для запуска. Но метод, который вы используете для выбора алгоритма, вероятно, тоже предсказуем...

Если вам нужно сделать это с помощью истинной случайности, вам понадобится генератор случайных чисел (алгоритмический подход всегда предсказуем), и вам нужно будет вести список всех чисел в диапазоне, а затем использовать случайное число генератор, чтобы выбрать номер из списка и удалить его из списка одновременно, чтобы вы не выбрали его снова.

Ответ 3

Если вам нужна неочевидная, но не очень безопасная, выберите размер шага N для диапазона M, чтобы N и M были взаимно простыми и выполняли арифметический mod M.

Ответ 4

Если вам нужен криптографически безопасный способ сделать это, вы можете проверить мою статью на криптографически безопасные перестановки с блочными шифрами. В двух словах выберите блок-шифр, используя метод XOR folding, чтобы уменьшить его до наименьшей мощности 2, превышающей желаемый диапазон, а затем использовать следующий метод для генерации только чисел в пределах требуемого диапазона:

def permute(index, max):
  index = E(index)
  if index > max:
    return permute(index, max)

То есть просто "зашифруйте" любое число, которое вы создаете, за пределами требуемого диапазона. Объем работы, требуемый для генерации всей последовательности, ограничен количеством элементов в исходной последовательности. Наихудший случай для создания одного элемента - 1 + unused_range, но это исчезающе тонкая возможность.

Вы можете применить это ко всему, что генерирует отображение 1:1, конечно, не только пример блочного шифрования. И если вы имеете дело с другим видом RNG - например, LFSR, вместо повторного применения функции просто пропустите этот элемент.

Ответ 5

Если порядок не обязательно должен быть по-настоящему случайным (просто непредсказуемым), и если нам разрешено хранить малозначный диапазон значений (скажем, N), то:

  • Сохранять первые значения N в подсписке
  • Возвращает случайное значение из подсписок до тех пор, пока не будет указано значение N/2.
  • Добавьте еще N/2 значений из основного списка в список подписчиков.
  • Повторяйте 2-3 до тех пор, пока все значения не будут выполнены.

Шаг 2-3 необходим, или любое N-е возвращаемое значение будет предсказуемым. Vary size (N) и порог перезарядки (N/2), чтобы найти хороший компромисс между использованием памяти, частотой перезагрузки и "случайностью".

Ответ 6

Используйте линейный конгруэнтный генератор случайных чисел и отбросьте любые целые числа вне вашего диапазона.

Ответ 7

Создайте базу данных с цифрами от 1 до N.

Создает ли база данных случайное число для каждого из значений N.

Возвращает значения N, отсортированные по случайному числу.