Как создать повторяемую последовательность случайных чисел? - PullRequest
14 голосов
/ 26 января 2012

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

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

Библиотека для модуля random, по-видимому, не говорит о том, что одно и то же начальное число всегда будет давать одинаковую последовательность на любом компьютере.

РЕДАКТИРОВАТЬ: Если вы собираетесь посеять данные (как я уже говорил выше), предоставьте документацию, в которой говорится, что подход действителен, и он будет работать на ряде машин / реализаций.

РЕДАКТИРОВАТЬ: CPython 2.7.1 и PyPy 1.7 в Mac OS X и CPython 2.7.1 и CPython 2.52 = .2 Ubuntu, похоже, дают те же результаты. Тем не менее, нет документов, которые предусматривают это в черно-белом.

Есть идеи?

Ответы [ 8 ]

21 голосов
/ 25 сентября 2013

Для этой цели я использовал повторяющийся хеш-код MD5, поскольку целью хеширования является кросс-платформенное преобразование один-к-одному, поэтому оно всегда будет одинаковым на разных платформах.

import md5

def repeatable_random(seed):
    hash = seed
    while True:
        hash = md5.md5(hash).digest()
        for c in hash:
            yield ord(c)

def test():
    for i, v in zip(range(100), repeatable_random("SEED_GOES_HERE")):
        print v

Вывод:

184 207 76 134 103 171 90 41 12 142 167 107 84 89 149 131 142 43 241 211 224 157 47 59 34 233 41 219 73 37 251 194 15 253 75 145 96 80 39 179 249 202 159 83 209 225 250 7 69 218 6 118 30 4 223 205 91 10 122 203 150 202 99 38 192 105 76 100 117 19 25 131 17 60 251 77 246 242 80 163 13 138 36 213 200 135 216 173 92 32 9 122 53 250 80 128 6 139 49 94

По сути, код будет брать ваше начальное число (любую допустимую строку) и многократно хэшировать его, генерируя целые числа от 0 до 255.

10 голосов
/ 04 октября 2013

Существуют различия между платформами, поэтому, если вы перемещаете свой код между различными платформами, я бы использовал метод, описанный DrRobotNinja.

Пожалуйста, посмотрите на следующий пример.Python на моем настольном компьютере (64-битная Ubuntu с Core i7, Python 2.7.3) дает мне следующее:

> import random
> r = random.Random()
> r.seed("test")
> r.randint(1,100)
18

Но если я запускаю тот же код на Raspberry Pi (Raspbian на ARM11), Я получаю другой результат (для той же версии Python)

> import random
> r = random.Random()
> r.seed("test")
> r.randint(1,100)
34
7 голосов
/ 26 января 2012

Укажите начальное число для генератора случайных чисел. Если вы предоставляете одинаковое начальное число, ваши случайные числа также должны совпадать.

http://docs.python.org/library/random.html#random.seed

6 голосов
/ 27 октября 2014

Также ответ, почему пример из этого ответа дает различный вывод на разных машинах:

Это потому, что при заполнении генератора случайных чисел начальное число должно быть целым числом. Если вы заполняете генератор не целым числом, его нужно сначала хэшировать. Сами хэш-функции не зависят от платформы (очевидно, по крайней мере, не все из них, поправьте меня, если знаете больше).

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

Это означает: пока вы инициализируете свой генератор случайных чисел с тем же состоянием, он будет производить одинаковую последовательность чисел. Для перехода в одно и то же состояние можно использовать одно целое начальное число или сохранить и повторно применить старое состояние (random.getstate () и random.setstate ()).

5 голосов
/ 27 января 2012

Если качество случайных чисел не так критично, как повторяемость на разных платформах, вы можете использовать один из традиционных линейных конгруэнтных генераторов :

class lcg(object):
    def __init__( self, seed=1 ):
        self.state = seed

    def random(self):
        self.state = (self.state * 1103515245 + 12345) & 0x7FFFFFFF
        return self.state

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

5 голосов
/ 26 января 2012

В документации явно не говорится, что предоставление начального числа всегда будет гарантировать одинаковые результаты, но это гарантировано с помощью случайной реализации Python на основе используемого алгоритма.

Согласно документации, Python использует Mersenne Twister в качестве генератора ядра. Как только этот алгоритм будет заполнен, он не получит никакого внешнего выхода, который мог бы изменить последующие вызовы, поэтому дайте ему то же начальное значение, и вы получите те же результаты.

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

Я не проверял другие реализации Python, кроме CPython, но я очень сомневаюсь, что они реализуют случайный модуль, используя совершенно другой алгоритм.

5 голосов
/ 26 января 2012

Использование random.seed (...) Вы можете создать повторяемую последовательность. Демонстрация:

import random

random.seed(321)
list1 = [random.randint(1,10) for x in range(5)]

random.seed(321)
list2 = [random.randint(1,10) for x in range(5)]

assert(list1==list2)

Это работает, потому что random.seed (...) не является по-настоящему случайным: это псевдослучайное, в результате чего последовательные числа создаются путем перестановки некоторого конечного автомата при заданном начальном условии начала, 'seed'.

1 голос
/ 26 января 2012

Я только что попробовал следующее:

import random
random.seed(1)
random.random()
random.random()
random.random()

random.seed(1)
random.random()
random.random()
random.random()

Я вводил каждую строку в CLI с различной скоростью в течение нескольких раз.Выдает одинаковые значения каждый раз.

...