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

6
什么是生成伪随机均匀分布的最佳方法(一个在[0,1)范围内的双精度数),即:
  1. 跨平台(理想情况下使用相同的样本序列)
  2. 线程安全(显式传递prng的变异状态或在内部使用线程本地状态)
  3. 无需GIL锁定
  4. 易于在Cython中包装
三年前有一个类似的帖子,链接为post,但很多答案不符合所有标准。例如,drand48是特定于POSIX的。
我所知道的唯一满足某些标准的方法是:
from libc.stdlib cimport rand, RAND_MAX

random = rand() / (RAND_MAX + 1.0)

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

编辑

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


2
我认为 rand 不是线程安全的。建议使用 C++ 标准库的随机函数进行封装(但我自己没有例子可提供)。 - DavidW
1个回答

9
重要说明:本回答推荐使用C++,因为问题明确要求不使用GIL。如果您没有这个要求(您很可能没有...),那么Numpy是最简单、最容易的解决方案。只要一次生成大量数字,您会发现Numpy非常快速。不要被误导去尝试复杂的C++包装,因为有人要求不使用GIL。

原始回答:

我认为最简单的方法是使用C++11标准库,它提供了漂亮封装的随机数生成器及其使用方式。当然,这并不是唯一的选择,您也可以封装任何合适的C/C++库(一个好的选择可能是使用Numpy使用的任何库,因为那很可能已经安装好了)。

我的一般建议是只包装需要的部分,不必担心完整的层次结构和所有可选的模板参数。例如,我展示了一个默认生成器,用于产生均匀浮点分布的示例。
# 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)

起始处的-std=c++11是为了GCC。对于其他编译器,您可能需要进行微调。越来越多的情况下,c++11已经成为默认设置,因此可以省略它。

关于您的标准:

  1. 跨平台,在支持C ++的任何平台上都可以使用。我认为应该指定序列以便重复使用。
  2. 线程安全,因为状态完全存储在mt19937对象中(每个线程应该有自己的mt19937)。
  3. 无GIL - 这是C ++,没有Python部分
  4. 相当容易。

编辑:关于使用discrete_distribution

这有点困难,因为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)

显然,这比均匀分布要复杂一些,但并不是难以理解的(并且令人讨厌的部分可以隐藏在Cython函数中)。

谢谢。顺便问一下,如何使用在http://www.cplusplus.com/reference/random/discrete_distribution/中找到的离散分布?我还不熟悉将C++和Cython合并。 - Kamil Sindi
太好了!感谢你的帮助。 - Kamil Sindi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接