Самый быстрый способ генерации 1 000 000 + случайных чисел в python
В настоящее время я пишу приложение на питоне, которое должно генерировать большое количество случайных чисел, FAST. В настоящее время у меня есть схема, которая использует numpy для генерации всех чисел в гигантской партии (около ~ 500 000 за раз). Хотя это, кажется, быстрее, чем реализация python. Мне все еще нужно, чтобы он ехал быстрее. Есть идеи? Я открыт для написания его на C и встраивания его в программу или выполнения w/e, которое требуется.
Ограничения на случайные числа:
- Набор из 7 чисел, которые могут иметь разные границы:
- например: [0-X1, 0-X2, 0-X3, 0-X4, 0-X5, 0-X6, 0-X7]
- В настоящее время я создаю список из 7 чисел со случайными значениями из [0-1), а затем умножается на [X1..X7]
- Набор из 13 номеров, все из которых составляют до 1
- В настоящее время генерируется только 13 номеров, делящихся на их сумму
Любые идеи? Предварительно вычислил бы эти числа и сохранил их в файле быстрее?
Спасибо!
Ответы
Ответ 1
Вы можете немного ускорить работу над тем, что выложили 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])
Аналогично, для вашей "строки sum to one" part...
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, я не уверен, что вы сможете много бить много на этом... Я мог бы быть очень неправ, хотя!
Ответ 2
EDIT Созданные функции, которые возвращают полный набор чисел, а не только одну строку за раз.
РЕДАКТИРОВАТЬ 2 Сделать функции более питонными (и быстрее), добавить решение для второго вопроса
Для первого набора чисел вы можете рассмотреть numpy.random.randint
или numpy.random.uniform
, которые принимают параметры low
и high
. Генерация массива из 7 х 1000000 номеров в указанном диапазоне, по-видимому, 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 ГГц.
Для второго набора я использовал предложение @Joe Kingston.
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-й набор данных.
Ответ 3
Попробуйте r = 1664525*r + 1013904223
от "еще более быстрого генератора"
в "Numericical Recipes in C" 2nd edition, Press et al., isbn 0521431085, p. 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
'
Ответ 4
Проведение вашего кода параллельно, конечно, не повредит. Попробуйте адаптировать его для SMP с помощью Parallel Python
Ответ 5
Как уже отмечалось другими, numpy
- очень хороший старт, быстрый и простой в использовании.
Если вам нужны случайные числа в массовом масштабе, рассмотрите eas-ecb или rc4. Оба могут быть параллельными, вы должны достичь производительности в нескольких ГБ/с.
достижимые числа, размещенные здесь
Ответ 6
Просто быстрый пример numpy
в действии:
data = numpy.random.rand(1000000)
Нет необходимости в цикле, вы можете передать, сколько чисел вы хотите сгенерировать.