回答前的重要警告:此答案建议使用 C++,因为该问题特别要求提供无需 GIL 即可运行的解决方案。如果您没有此要求(并且您可能没有...),那么 Numpy 是最简单和最简单的解决方案。如果您一次生成大量数字,您会发现 Numpy 非常快。不要因为有人要求提供无 gil 解决方案而被误导为包装 C++ 的复杂练习。
原答案:
我认为最简单的方法是使用提供nice encapsulated random number generators and ways to use them 的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 越来越多地成为默认值,因此您可以放弃它)
参考您的标准:
- 任何支持 C++ 的跨平台。我认为应该指定序列以便它是可重复的。
- 线程安全,因为状态完全存储在
mt19937 对象中(每个线程都应该有自己的mt19937)。
- 没有 GIL - 它是 C++,没有 Python 部分
- 相当容易。
编辑:关于使用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 函数中)。