我正在尝试使用Jupyter笔记本(我使用python 2)并使用gmp算法在cython中实现一个简单的代码,以便处理非常大的整数。我不是gmp / cython专家。我的问题是:如何在函数fib()中打印 a 值。
以下代码返回{}。 我可以理解它与stdout有关。例如,我尝试了gmp_printf,但它没有用。
%%cython --link-args=-lgmp
cdef extern from "gmp.h":
ctypedef struct mpz_t:
pass
cdef void mpz_init(mpz_t)
cdef void mpz_init_set_ui(mpz_t, unsigned int)
cdef void mpz_add(mpz_t, mpz_t, mpz_t)
cdef void mpz_sub(mpz_t, mpz_t, mpz_t)
cdef void mpz_add_ui(mpz_t, const mpz_t, unsigned long int)
cdef void mpz_set(mpz_t, mpz_t)
cdef void mpz_clear(mpz_t)
cdef unsigned long int mpz_get_ui(mpz_t)
cdef void mpz_set_ui(mpz_t, unsigned long int)
cdef int gmp_printf (const char*, ...)
cdef size_t mpz_out_str (FILE , int , const mpz_t)
def fib(unsigned long int n):
cdef mpz_t a,b
mpz_init(a)
mpz_init(b)
mpz_init_set_ui(a,1)
mpz_init_set_ui(b,1)
cdef int i
for i in range(n):
mpz_add(a,a,b)
mpz_sub(b,a,b)
return a
结果
fib(10)
{}
如果我使用return mpz_get_ui(a)
代替return a
代码工作正常,但这不是我真正想要的东西(获得一个长整数)。
EDIT。 我在cython中再次将之前的代码与另一个代码进行了比较,但没有使用mpz。
%%cython
def pyfib(unsigned long int n):
a,b=1,1
for i in range(n):
a=a+b
b=a-b
return a
最后是相同的代码,但是使用了gmpy2的mpz
%%cython
import gmpy2
from gmpy2 import mpz
def pyfib_with_gmpy2(unsigned long int n):
cdef int i
a,b=mpz(1),mpz(1)
for i in range(n):
a=a+b
b=a-b
return a
然后
timeit fib(700000)
1 loops, best of 3: 3.19 s per loop
和
timeit pyfib(700000)
1 loops, best of 3: 11 s per loop
和
timeit pyfib_with_gmpy2(700000)
1 loops, best of 3: 3.28 s per loop
答案 0 :(得分:1)
(答案主要是总结一堆评论)
您遇到的直接问题是Python没有真正的方法来处理C结构。为了解决这个问题,Cython尝试在将结构传递给Python时将结构转换为字典(如果可能的话)。在这种特殊情况下,mpz_t
被C(因而是Cython)视为“不透明”,所以你不应该知道它的成员。因此,Cython“有帮助”将其转换为空字典(它所知道的所有成员的正确表示)。
对于立即修复我建议使用gmpy库,这是GMP的现有Python / Cython包装。这可能是比重复包装更好的选择。
作为这类问题的一般解决方案,有两个明显的选择。
您可以创建cdef
wrapper class。我链接的文档是针对C ++的,但这个想法也可以应用于C(new
/'del'替换为'malloc'/'free')。这最终是一个Python类(因此可以从Cython返回到Python)但包含一个C结构,您可以直接在Cython中操作它。该方法记录良好,不需要在此重复。
您可以在函数末尾将mpz_t
转换回Python整数。我觉得这最有意义,因为它们最终代表着同样的东西。下面显示的代码是粗略的大纲,尚未经过测试(我没有安装gmp
):
cdef mpz_to_py_int(mpz_t x):
# get bytes that describe the integer
cdef const mp_limb_t* x_data = mpz_limbs_read(x)
# view as a unsigned char* (i.e. as bytes)
cdef unsigned char* x_data_bytes = <unsigned char*>x_data
# cast to a memoryview then pass that to the int classmethod "from_bytes"
# assuming big endian (python 3.2+ required)
out = int.from_bytes(<unsigned char[:mpz_size(x):1]>x_data_bytes,'big')
# correct using sign
if mpz_sign(x) < 0:
return -out
else
return out