Cython使用gmp算法

2024-04-28 10:12:07 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图用cython实现一个简单的代码,使用Jupyter笔记本(我使用python2)并使用gmp算法来处理非常大的整数。我不是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

结果呢

^{pr2}$

如果我使用return mpz_get_ui(a)而不是return a 代码运行良好,但这不是我真正想要的(得到一个长整数)。在

编辑。 我在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

Tags: 代码uireturninitcythonlongintset
1条回答
网友
1楼 · 发布于 2024-04-28 10:12:07

(答案主要是总结一堆评论)

您面临的直接问题是Python没有真正的方法来处理C结构。为了解决这个问题,Cython尝试在将结构传递给Python时将它们转换为字典(如果可能)。在这个特殊的例子中,mpz_t被C(以及Cython)视为“不透明”的,所以您不应该知道它的成员。因此,Cython“有帮助地”将其转换为空字典(它所知道的所有成员的正确表示)。在

为了立即修复,我建议使用gmpy库,这是GMP的一个现有Python/Cython包装。这可能是一个比重复包装它的努力更好的选择。在


作为这类问题的一般解决方案,有两个明显的选择。在

  1. 您可以创建一个^{} wrapper class。我已经链接的文档是C++,但是这个想法也可以应用到C(用{MaLoc’/‘Field’替换为{{CD3}}/del`)。这最终是一个Python类(因此可以从Cython返回到Python),但包含一个C结构,可以直接在Cython中操作它。这种方法有很好的文档记录,不需要在这里重复。

  2. 您可以在函数末尾将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
    

相关问题 更多 >