在Cython中生成随机数的规范方法

ksi*_*ndi 2 random multithreading cython

生成伪统一随机数([0,1中的双精度数])的最佳方法是:

  1. 跨平台(理想情况下具有相同的样品序列)
  2. 线程安全(显式传递prng的突变状态或在内部使用线程局部状态)
  3. 没有GIL锁
  4. 可轻松包装在Cython中

3年前也有类似的文章,但是很多答案并不符合所有标准。例如,drand48特定于POSIX。

我知道的(但不确定)满足所有某些条件的唯一方法是:

from libc.stdlib cimport rand, RAND_MAX

random = rand() / (RAND_MAX + 1.0)
Run Code Online (Sandbox Code Playgroud)

注意@ogrisel 大约在3年前了同样的问题。

编辑

调用rand不是线程安全的。感谢您指出@DavidW。

Dav*_*idW 5

我认为最简单的方法是使用C ++ 11标准库,该库提供了不错的封装随机数生成器以及使用它们的方法。当然,这不是唯一的选择,并且您可以包装几乎所有合适的C / C ++库(一个不错的选择可能是使用numpy使用的任何库,因为它很可能已经安装了)。

我的一般建议是只包装所需的位,而不必理会完整的层次结构和所有可选的模板参数。举例来说,我已经展示了默认生成器之一,它被馈送到统一的float分布中。

# distutils: language = c++
# distutils: extra_compile_args = -std=c++11

cdef extern from "<random>" namespace "std":
    cdef cppclass mt19937:
        mt19937() # we need to define this constructor to stack allocate classes in Cython
        mt19937(unsigned int seed) # not worrying about matching the exact int type for seed

    cdef cppclass uniform_real_distribution[T]:
        uniform_real_distribution()
        uniform_real_distribution(T a, T b)
        T operator()(mt19937 gen) # ignore the possibility of using other classes for "gen"

def test():
    cdef:
        mt19937 gen = mt19937(5)
        uniform_real_distribution[double] dist = uniform_real_distribution[double](0.0,1.0)
    return dist(gen)
Run Code Online (Sandbox Code Playgroud)

-std=c++11开始时是针对GCC的。对于其他编译器,您可能需要进行调整。无论如何,默认情况下c ++ 11越来越多,因此可以将其删除)

参考您的标准:

  1. 跨平台的任何支持C ++的东西。我认为应该指定顺序,以便其可重复。
  2. 线程安全,因为状态完全存储在mt19937对象内(每个线程应具有自己的mt19937)。
  3. 没有GIL-它是C ++,没有Python部件
  4. 相当容易。

编辑:关于使用discrete_distribution

这有点困难,因为for的构造函数discrete_distribution不太容易包装(它们涉及迭代器)。我认为最简单的方法是通过C ++向量,因为对此的支持内置在Cython中,并且可以轻松地与Python列表进行相互转换。

# use Cython's built in wrapping of std::vector
from libcpp.vector cimport vector

cdef extern from "<random>" namespace "std":
    # mt19937 as before

    cdef cppclass discrete_distribution[T]:
        discrete_distribution()
        # The following constructor is really a more generic template class
        # but tell Cython it only accepts vector iterators
        discrete_distribution(vector.iterator first, vector.iterator last)
        T operator()(mt19937 gen)

# an example function
def test2():
    cdef:
        mt19937 gen = mt19937(5)
        vector[double] values = [1,3,3,1] # autoconvert vector from Python list
        discrete_distribution[int] dd = discrete_distribution[int](values.begin(),values.end())
    return dd(gen)
Run Code Online (Sandbox Code Playgroud)

显然,这比均匀分布要复杂得多,但是它并没有那么复杂(并且讨厌的位可能隐藏在Cython函数中)。