2015-06-04 2 views
2

фон

Я хочу, чтобы проверить свой код, который зависит от random модуля.random.choice дает разные результаты на Python 2 и 3

Проблематика ПР https://github.com/Axelrod-Python/Axelrod/pull/202 и код здесь https://github.com/Axelrod-Python/Axelrod/blob/master/axelrod/strategies/qlearner.py

Проблема

Поскольку random модуль производит псевдослучайные числа, я всегда устанавливают random.seed(X) к известному значению X. Это работает для последовательных тестовых прогонов. Однако, Python 3, кажется, дают различные значения, чем Python 2 при использовании random.choice([D, C])

После сниппета:

import random 
random.seed(1) 

for i in range(10): 
    print(random.choice(['C', 'D']), end=', ') 

дает другой результат для Python 2 и 3

$ python2 test.py                                      
C, D, D, C, C, C, D, D, C, C 

$ python3 test.py 
C, C, D, C, D, D, D, D, C, C 

Однако random.random метод работает такие же на 2.x и 3.x:

import random 
random.seed(1) 

for i in range(10): 
    print(random.random()) 

$ python3 test.py 
0.13436424411240122 
0.8474337369372327 
0.763774618976614 
0.2550690257394217 
0.49543508709194095 
0.4494910647887381 
0.651592972722763 
0.7887233511355132 
0.0938595867742349 
0.02834747652200631 

$ python2 test.py 
0.134364244112 
0.847433736937 
0.763774618977 
0.255069025739 
0.495435087092 
0.449491064789 
0.651592972723 
0.788723351136 
0.0938595867742 
0.028347476522 

Обходной путь

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

Вопрос

ли я сделал что-то неправильно при вызове метода random.choice?

+1

Я предполагаю, что в фрагменте это должно быть 'для i в диапазоне (10):' и не 'для i в случайном порядке (10):' –

+0

Почему вы ожидаете, что случайный модуль будет производить одни и те же результаты в разных версиях Python? –

+0

Возможно, что-то с 'random.seed()'? – Zizouz212

ответ

1

Согласно https://docs.python.org/2/library/random.html, RNG был изменен на Python 2.4 и может использовать ресурсы операционной системы. Основываясь на этом и другом ответе на этот вопрос, небезосновательно ожидать, что Random даст тот же результат в двух разных версиях Python, двух разных операционных системах или даже двух разных компьютерах. Для всех, кого вы знаете, следующая версия Python может реализовать функцию Random, которая использует микрофон системы для генерации случайной последовательности.

Краткая версия: вы не должны зависеть от ГСЧ, чтобы дать детерминированный результат. Если вам нужна известная последовательность для выполнения единичного теста, вам необходимо либо перепроектировать ваш метод, либо ваш модульный тест.

Один из способов сделать это - разделить свой метод на две части: одна часть генерирует случайное число. Вторая часть потребляет значение и действует на него. Затем вы должны написать два единичных теста: один для проверки покрытия сгенерированных значений и отдельный для проверки вывода вашего метода на основе конкретных входных данных.

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

Или, может быть, ваш модульный тест может быть изменен, чтобы просто запустить тест n и найти спред, который подтверждает какую-то случайность.

+2

Хотя верно, что использование случайных в тестах редко является хорошей идеей, документы четко указывают, что 'random' использует детерминированную реализацию Mersenne Twister. – UloPe

+0

Хорошо, возможно, лучшим решением является функция с известными результатами и не полагаться на 'random.seed', правильно? –

+0

@UloPe полагается на то, что это плохая идея, так как алгоритм не гарантирует, что он останется прежним. Поскольку ресурсы ОС задействованы, мы не можем гарантировать одну и ту же последовательность в двух разных средах. –

5

В каждой версии есть совершенно другая реализация random.choice.

Python 2.7:

def choice(self, seq): 
    """Choose a random element from a non-empty sequence.""" 
    return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty 

https://hg.python.org/cpython/file/2.7/Lib/random.py

Python 3.4:

def choice(self, seq): 
    """Choose a random element from a non-empty sequence.""" 
    try: 
     i = self._randbelow(len(seq)) 
    except ValueError: 
     raise IndexError('Cannot choose from an empty sequence') 
    return seq[i] 

https://hg.python.org/cpython/file/3.4/Lib/random.py

Метод _randbelow можно назвать случайным() более чем один раз, или может позвонить getrandbits который имеет другой базовый вызов _urandom.

+0

Спасибо, это может быть проблема! –

Смежные вопросы