Самый быстрый способ генерировать более 1 000 000+ случайных чисел в Python - PullRequest
23 голосов
/ 26 апреля 2010

В настоящее время я пишу приложение на python, которое должно генерировать большое количество случайных чисел, БЫСТРО. В настоящее время у меня есть схема, которая использует NumPy для генерации всех чисел в гигантской партии (около 500 000 одновременно). Хотя это кажется быстрее, чем реализация Python. Мне все еще нужно, чтобы идти быстрее. Есть идеи? Я открыт для написания этого на C и встраивания в программу или выполнения необходимых действий.

Ограничения на случайные числа:

  • Набор из 7 чисел, которые могут иметь разные границы:
    • например: [0-X1, 0-X2, 0-X3, 0-X4, 0-X5, 0-X6, 0-X7]
    • В настоящее время я генерирую список из 7 чисел со случайными значениями из [0-1), затем умножая на [X1..X7]
  • Набор из 13 чисел, которые все складываются в 1
    • В настоящее время просто генерируется 13 чисел, а затем делится на их сумму

Есть идеи? Может ли предварительный расчет этих чисел и их сохранение в файле сделать это быстрее?

Спасибо!

Ответы [ 6 ]

12 голосов
/ 26 апреля 2010

Вы можете немного ускорить процесс, описанный выше в mtrw, просто выполнив то, что вы первоначально описали (сгенерировав несколько случайных чисел, умножив и разделив соответственно) ...

Кроме того, вы, вероятно, уже знаете это, но обязательно выполняйте операции на месте (* =, / =, + = и т. Д.) При работе с массивами больших чисел. Это имеет огромное значение в использовании памяти с большими массивами и также значительно увеличит скорость.

In [53]: def rand_row_doubles(row_limits, num):
   ....:     ncols = len(row_limits)
   ....:     x = np.random.random((num, ncols))
   ....:     x *= row_limits                  
   ....:     return x                          
   ....:                                       
In [59]: %timeit rand_row_doubles(np.arange(7) + 1, 1000000)
10 loops, best of 3: 187 ms per loop

По сравнению с:

In [66]: %timeit ManyRandDoubles(np.arange(7) + 1, 1000000)
1 loops, best of 3: 222 ms per loop

Это не большая разница, но если вы действительно беспокоитесь о скорости, это что-то.

Просто чтобы показать, что это правильно:

In [68]: x.max(0)
Out[68]:
array([ 0.99999991,  1.99999971,  2.99999737,  3.99999569,  4.99999836,
        5.99999114,  6.99999738])

In [69]: x.min(0)
Out[69]:
array([  4.02099599e-07,   4.41729377e-07,   4.33480302e-08,
         7.43497138e-06,   1.28446819e-05,   4.27614385e-07,
         1.34106753e-05])

Аналогично, для вашей "суммы строк в одну" часть ...

In [70]: def rand_rows_sum_to_one(nrows, ncols):
   ....:     x = np.random.random((ncols, nrows))
   ....:     y = x.sum(axis=0)
   ....:     x /= y
   ....:     return x.T
   ....:

In [71]: %timeit rand_rows_sum_to_one(1000000, 13)
1 loops, best of 3: 455 ms per loop

In [72]: x = rand_rows_sum_to_one(1000000, 13)

In [73]: x.sum(axis=1)
Out[73]: array([ 1.,  1.,  1., ...,  1.,  1.,  1.])

Честно говоря, даже если вы заново внедрите что-то в C, я не уверен, что вам удастся сильно обыграть этого ... но я могу ошибаться!

6 голосов
/ 26 апреля 2010

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

Для первого набора чисел вы можете рассмотреть numpy.random.randint или numpy.random.uniform, которые принимают параметры low и high. Генерация массива 7 x 1 000 000 чисел в указанном диапазоне занимает менее 0,7 секунды на моем компьютере с частотой 2 ГГц:

def LimitedRandInts(XLim, N):
    rowlen = (1,N)
    return [np.random.randint(low=0,high=lim,size=rowlen) for lim in XLim]

def LimitedRandDoubles(XLim, N):
    rowlen = (1,N)
    return [np.random.uniform(low=0,high=lim,size=rowlen) for lim in XLim]

>>> import numpy as np
>>> N = 1000000 #number of randoms in each range
>>> xLim = [x*500 for x in range(1,8)] #convenient limit generation
>>> fLim = [x/7.0 for x in range(1,8)]
>>> aa = LimitedRandInts(xLim, N)
>>> ff = LimitedRandDoubles(fLim, N)

Возвращает целые числа в [0, xLim-1] или с плавающей точкой в ​​[0, fLim). Целочисленная версия заняла ~ 0,3 секунды, двойная ~ 0,66 на моей одноядерной машине с частотой 2 ГГц.

Для второго сета я использовал предложение Джо Кингстона.

def SumToOneRands(NumToSum, N):
    aa = np.random.uniform(low=0,high=1.0,size=(NumToSum,N)) #13 rows by 1000000 columns, for instance
    s = np.reciprocal(aa.sum(0))
    aa *= s
    return aa.T #get back to column major order, so aa[k] is the kth set of 13 numbers

>>> ll = SumToOneRands(13, N)

Это занимает ~ 1,6 секунды.

Во всех случаях result[k] дает вам k-й набор данных.

4 голосов
/ 04 мая 2010

Попробуйте r = 1664525*r + 1013904223
от "еще более быстрого генератора" в "Числовые рецепты в C" 2-е издание, Press и др., Isbn 0521431085, с. 284.
np.random безусловно «более случайный»; увидеть Линейный конгруэнтный генератор .

В Python используйте np.uint32, например:

python -mtimeit -s '
import numpy as np
r = 1
r = np.array([r], np.uint32)[0]  # 316 py -> 16 us np 
    # python longs can be arbitrarily long, so slow
' '
r = r*1664525 + 1013904223  # NR2 p. 284
'

Для генерации больших блоков одновременно:

# initialize --
np.random.seed( ... )
R = np.random.randint( 0, np.iinfo( np.uint32 ).max, size,  dtype=np.uint32 )
...
R *= 1664525
R += 1013904223
1 голос
/ 08 октября 2013

Как уже отмечали другие, numpy - очень хорошее начало, быстрое и простое в использовании.

Если вам нужны случайные числа в массовом масштабе, рассмотрите eas-ecb или rc4. Оба могут быть распараллелены, вы должны достичь производительности в несколько ГБ / с.

достижимые числа размещены здесь

1 голос
/ 26 апреля 2010

Выполнение вашего кода параллельно должно определенно не повредить. Попробуйте адаптировать его для SMP с Parallel Python

0 голосов
/ 07 июля 2016

Просто краткий пример numpy в действии:

data = numpy.random.rand(1000000)

Нет необходимости в цикле, вы можете указать, сколько чисел вы хотите сгенерировать.

...