Я хотел бы генерировать уникальные случайные числа от 0 до 1000, которые никогда не повторяются (т.е. 6 не появляется дважды), но это не прибегает к чему-то вроде поиска O (N) предыдущих значений, чтобы сделать это. Это возможно?
algorithm
math
random
language-agnostic
dicroce
источник
источник
O(n)
во времени или в памяти), то многие из приведенных ниже ответов неверны, включая принятый ответ.Ответы:
Инициализируйте массив из 1001 целого числа со значениями 0-1000 и установите переменную max для текущего максимального индекса массива (начиная с 1000). Выберите случайное число r от 0 до max, поменяйте местами число в позиции r и число в позиции max и верните число теперь в положение max. Уменьшите максимум на 1 и продолжайте. Когда max равно 0, установите max обратно к размеру массива - 1 и начните заново без необходимости повторной инициализации массива.
Обновление: хотя я и придумал этот метод самостоятельно, когда ответил на вопрос, после некоторых исследований я понял, что это модифицированная версия Фишера-Йетса, известная как Дюрстенфельд-Фишер-Йейтс или Кнут-Фишер-Йейтс. Поскольку описание может быть немного сложным для подражания, я привел пример ниже (используя 11 элементов вместо 1001):
Массив начинается с 11 элементов, инициализированных в массив [n] = n, max начинается с 10:
На каждой итерации случайное число r выбирается между 0 и max, массив [r] и массив [max] меняются местами, возвращается новый массив [max] и значение max уменьшается:
После 11 итераций все числа в массиве были выбраны, max == 0, и элементы массива перемешиваются:
На этом этапе max можно сбросить до 10, и процесс можно продолжить.
источник
N
итерации (11 в этом примере), чтобы каждый раз получать желаемый результат, означает, что это такO(n)
? Так как вам нужно делатьN
итерации, чтобы получитьN!
комбинации из одного и того же начального состояния, в противном случае ваш вывод будет только одним из N состояний.Ты можешь сделать это:
Так что это не требует поиска старых значений каждый раз, но все равно требует O (N) для начального перемешивания. Но, как отметил Нильс в комментариях, это амортизированный O (1).
источник
Используйте максимальный регистр сдвига с линейной обратной связью .
Он может быть реализован в нескольких строках C и во время выполнения выполняет всего несколько тестов / веток, небольшое добавление и сдвиг битов. Это не случайно, но это обманывает большинство людей.
источник
Вы можете использовать линейный конгруэнтный генератор . Где
m
(модуль) будет ближайшим простым числом больше 1000. Когда вы выберете число из диапазона, просто получите следующее. Последовательность будет повторяться только после того, как все элементы будут выполнены, и вам не нужно будет использовать таблицу. Имейте в виду недостатки этого генератора (в том числе отсутствие случайности).источник
k
в последовательности, никогда не могут встречаться вместе).Вы можете использовать шифрование с сохранением формата для шифрования счетчика. Ваш счетчик просто идет от 0 вверх, и шифрование использует ключ по вашему выбору, чтобы превратить его в, казалось бы, случайное значение любой ширины и радиуса, которое вы хотите. Например, для примера в этом вопросе: основание 10, ширина 3.
Блочные шифры обычно имеют фиксированный размер блока, например, 64 или 128 бит. Но шифрование, сохраняющее формат, позволяет вам взять стандартный шифр, такой как AES, и создать шифр меньшей ширины, любого желаемого радиуса и ширины, с алгоритмом, который все еще криптографически устойчив.
Гарантируется, что никогда не будет коллизий (поскольку криптографические алгоритмы создают отображение 1: 1). Он также обратим (двухстороннее сопоставление), поэтому вы можете взять полученное число и вернуться к значению счетчика, с которого вы начали.
Этот метод не требует памяти для хранения перемешанного массива и т. Д., Что может быть преимуществом в системах с ограниченной памятью.
AES-FFX является одним из предложенных стандартных методов для достижения этой цели. Я экспериментировал с некоторым базовым кодом Python, который основан на идее AES-FFX, хотя и не полностью соответствует - см. Код Python здесь . Он может, например, зашифровать счетчик на случайное 7-значное десятичное число или 16-разрядное число. Вот пример с основанием 10, шириной 3 (чтобы дать число от 0 до 999 включительно) в качестве поставленного вопроса:
Чтобы получить разные неповторяющиеся псевдослучайные последовательности, измените ключ шифрования. Каждый ключ шифрования создает различную неповторяющуюся псевдослучайную последовательность.
источник
k
друг над другом в последовательности, никогда не встречаются вместе).k
?1,2,...,N
последовательностью тех же чисел в каком-то другом, но все же постоянном порядке. Затем номера извлекаются из этой последовательности по одному.k
это количество выбранных значений (ОП не указывал букву для него, поэтому мне пришлось ввести одно).Для младших чисел, таких как 0 ... 1000, создать список, содержащий все числа, и перетасовать его просто. Но если набор чисел для рисования очень большой, есть другой элегантный способ: вы можете построить псевдослучайную перестановку, используя ключ и криптографическую хеш-функцию. Смотрите следующий C ++ - ish пример псевдокода:
Вот
hash
лишь некоторая произвольная псевдослучайная функция, которая отображает строку символов в возможно огромное целое число без знака. Функцияrandperm
представляет собой перестановку всех чисел в пределах 0 ... pow (2, бит) -1, предполагая фиксированный ключ. Это следует из конструкции, потому что каждый шаг, который изменяет переменную,index
является обратимым. Это вдохновлено шифром Фейстеля .источник
hash()
, как используется в приведенном выше коде, является безопасной псевдослучайной функцией, эта конструкция будет доказуемо (Luby & Rackoff, 1988) давать псевдослучайную перестановку , которую нельзя отличить от настоящей случайной случайной последовательности, использующей значительно меньше усилий, чем исчерпывающая. поиск всего пространства ключей, которое экспоненциально по длине ключа. Даже для ключей разумного размера (скажем, 128 бит) это превышает общую вычислительную мощность, доступную на Земле.hash( key + "/" + int2str(temp) )
выше специальную конструкцию на HMAC , безопасность которого, в свою очередь, можно достоверно снизить до уровня базовой функции хеш-сжатия. Кроме того, использование HMAC может привести к менее вероятно, что кто-то по ошибке попытается использовать эту конструкцию с небезопасной не криптографической хэш-функцией.)Вы можете использовать мой алгоритм Xincrol, описанный здесь:
http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html
Это чисто алгоритмический метод генерации случайных, но уникальных чисел без массивов, списков, перестановок или большой загрузки процессора.
Последняя версия позволяет также установить диапазон чисел, например, если я хочу уникальные случайные числа в диапазоне 0-1073741821.
Я практически использовал его для
Это открыто, бесплатно. Попробуйте ...
источник
k
в последовательности, никогда не могут встречаться вместе).Вам даже не нужен массив, чтобы решить этот.
Вам нужна битовая маска и счетчик.
Инициализируйте счетчик на ноль и увеличивайте его при последующих вызовах. XOR счетчик с битовой маской (случайно выбранной при запуске или фиксированной) для генерации псевдослучайного числа. Если вы не можете иметь числа, превышающие 1000, не используйте битовую маску шире, чем 9 бит. (Другими словами, битовая маска является целым числом не выше 511.)
Убедитесь, что когда счетчик проходит 1000, вы сбрасываете его на ноль. В это время вы можете выбрать другую случайную битовую маску - если хотите - чтобы создать тот же набор чисел в другом порядке.
источник
Я думаю, что линейный конгруэнтный генератор будет самым простым решением.
и есть только три ограничения на а , с и м значений
PS метод уже упоминался, но пост содержит неверные предположения о постоянных значениях. Константы ниже должны нормально работать для вашего случая
В вашем случае вы можете использовать
a = 1002
,c = 757
,m = 1001
источник
Вот код, который я набрал, который использует логику первого решения. Я знаю, что это «не зависит от языка», но просто хотел представить это в качестве примера на C # на тот случай, если кто-то ищет быстрое практическое решение.
источник
Результаты этого метода соответствуют, когда предел велик, и вы хотите сгенерировать только несколько случайных чисел.
Обратите внимание, что числа генерируются в порядке возрастания, но затем вы можете перемешать их.
источник
(top,n)=(100,10)
являются:(0.01047705, 0.01044825, 0.01041225, ..., 0.0088324, 0.008723, 0.00863635)
. Я тестировал на Python, поэтому здесь могут сыграть небольшую разницу в математике (я действительно следил за тем, чтобы все операции для вычисления выполнялись сr
плавающей точкой).Вы могли бы использовать хороший генератор псевдослучайных чисел с 10 битами и выбросить 1001–1023, оставив от 0 до 1000.
Из здесь мы получаем дизайн для 10 битного ПСЧА ..
10 бит, полином обратной связи x ^ 10 + x ^ 7 + 1 (период 1023)
используйте Galois LFSR, чтобы получить быстрый код
источник
N Неповторяющиеся случайные числа будут иметь сложность O (n), как требуется.
Примечание: Random должен быть статическим с применением безопасности потока.
источник
Допустим, вы хотите просматривать списки
O(n)
в случайном порядке снова и снова, без задержки каждый раз, когда вы начинаете снова перемешивать, в этом случае мы можем сделать это:Создание 2 списков A и B, с 0 по 1000, занимает
2n
место.Перемешать список А, используя Фишера-Йейтса, требуется
n
время.Рисуя число, сделайте 1-шаговый переход Фишера-Йейтса в другом списке.
Когда курсор находится в конце списка, переключитесь на другой список.
Preprocess
Привлечь
источник
[1,3,4,5,2]
даст тот же результат, что и перетасовка[1,2,3,4,5]
.Вопрос Как эффективно создать список из K неповторяющихся целых чисел от 0 до верхней границы N связан как дубликат, и если вам нужно что-то, что является O (1) на сгенерированное случайное число (без O (n)») стоимость запуска)) есть простой твик из принятого ответа.
Создайте пустую неупорядоченную карту (пустая упорядоченная карта будет принимать O (log k) на элемент) от целого к целому - вместо использования инициализированного массива. Установите максимум на 1000, если это максимум,
Единственное отличие по сравнению с использованием инициализированного массива состоит в том, что инициализация элементов откладывается / пропускается, но она будет генерировать точно такие же числа из одного и того же PRNG.
источник
Еще одна возможность:
Вы можете использовать массив флагов. И возьмите следующий, когда он уже выбран.
Но будьте осторожны после 1000 вызовов, функция никогда не закончится, поэтому вы должны принять меры предосторожности.
источник
Вот пример кода COBOL, с которым вы можете поиграть.
Я могу отправить вам файл RANDGEN.exe, чтобы вы могли поиграть с ним, чтобы узнать, хочет ли он этого.
источник
Большинство ответов здесь не гарантируют, что они не вернут одно и то же число дважды. Вот правильное решение:
Я не уверен, что ограничение четко указано. Предполагается, что после 1000 других выходов значение может повторяться, но это наивно позволяет 0 следовать сразу после 0, пока они оба появляются в конце и в начале наборов 1000. И наоборот, пока можно сохранять расстояние 1000 других значений между повторениями, при этом возникает ситуация, когда последовательность воспроизводит себя одинаково каждый раз, потому что нет другого значения, которое произошло за пределами этого предела.
Вот метод, который всегда гарантирует не менее 500 других значений, прежде чем значение может быть повторено:
источник
Когда N больше 1000, и вам нужно нарисовать K случайных выборок, вы можете использовать набор, который пока содержит выборки. Для каждого розыгрыша вы используете выборку отклонения , которая будет «почти» операцией O (1), поэтому общее время работы составляет почти O (K) при хранении O (N).
Этот алгоритм сталкивается с коллизиями, когда K «около» N. Это означает, что время работы будет намного хуже, чем O (K). Простое решение состоит в том, чтобы изменить логику так, чтобы при K> N / 2 вы вели учет всех выборок, которые еще не были отрисованы. Каждый тираж удаляет образец из набора отклонений.
Другая очевидная проблема с выборкой отклонения состоит в том, что это O (N) хранилище, что является плохой новостью, если N исчисляется миллиардами или более. Тем не менее, есть алгоритм, который решает эту проблему. Этот алгоритм называется алгоритмом Виттера после его изобретателя. Алгоритм описан здесь . Суть алгоритма Виттера заключается в том, что после каждого розыгрыша вы вычисляете случайный пропуск с использованием определенного распределения, которое гарантирует равномерную выборку.
источник
Фишер Йейтс
На самом деле это O (n-1), так как вам нужен только один своп для двух последних
Это C #
источник
Пожалуйста, смотрите мой ответ на https://stackoverflow.com/a/46807110/8794687
Это один из самых простых алгоритмов , которые имеют среднюю временную сложность O ( ы лог - ы ), ы , обозначающая размер выборки. Там также есть несколько ссылок на алгоритмы хеш-таблиц, сложность которых, как утверждают, равна O ( s ).
источник
Кто-то написал "создание случайных чисел в Excel". Я использую этот идеал. Создайте структуру из 2 частей: str.index и str.ran; Для 10 случайных чисел создайте массив из 10 структур. Установите str.index от 0 до 9 и str.ran к другому случайному числу.
Сортировать массив по значениям в arr [i] .ran. Индекс str.index теперь в случайном порядке. Ниже приведен код c:
источник