【发布时间】:2021-12-24 02:19:10
【问题描述】:
我正在实施一个简单的 Eratosthenes 筛子。当我为结果创建一个恒定大小的堆栈数组时,这相当简单,但是这种方法会限制我可以计算的素数的数量。
所以我尝试使用一些动态堆数组;这里的问题是,在“cpdef”函数中,我想返回一个 python 列表,我必须将数组转换为一个列表,但这是不可能的。
如果我在没有取消引用的情况下执行此操作,Cython 会抱怨“Python 对象不能从原始类型的指针转换”,如果我取消引用数组,它将返回第一个元素,即不足为奇。
我知道 NumPy 或使用了一些列表解析,但我不想降低性能或使用第三方包。
考虑到限制,最快的方法是什么? CPython 数组很慢:可能是类型化的 memoryview?为什么 Cython 不能转换动态数组?
# cython: boundscheck=False
# cython: wraparound=False
from libc.stdlib cimport malloc, free
from libc.math cimport sqrt
from cython.operator cimport dereference
cdef inline int is_prime(unsigned int num, unsigned int *primes, unsigned int counter):
cdef unsigned int i
for i in range(counter):
if num % primes[i] == 0:
return 0
if primes[i] > <unsigned int> sqrt(num) + 1:
break
return 1
cpdef list primes_below(unsigned int x):
# cdef:
# unsigned int primes[1_000_000]
# unsigned int counter = 0
# unsigned int i
#
# for i in range(2, x):
# if is_prime(i, primes, counter):
# primes[counter] = i
# counter += 1
# return (<list> primes)[:counter]
# Alternative approach: (It doesn't work!)
cdef:
unsigned int *primes = <unsigned int *> malloc(sizeof(int) * x)
unsigned int counter = 0
unsigned int i
for i in range(2, x):
if is_prime(i, primes, counter):
primes[counter] = i
counter += 1
return <list> primes
# return <list> dereference(primes)
P.S.:我是 C 新手,所以我可能会遗漏一些细微的细节。
编辑: 首先,我应该感谢 @DavidW 对 Cython 问题的无尽全面回答。其次,他是对的!
列表理解真的很快
与我的想法相反。
这是新的实现:
# cython: boundscheck=False
# cython: wraparound=False
# cython: cdivision=True
from cython.view cimport array as cy_array
from libc.math cimport sqrt
cdef inline int is_prime(unsigned int num, unsigned int[::1] primes, unsigned int counter):
cdef unsigned int i
for i in range(counter):
if num % primes[i] == 0:
return 0
if primes[i] > <unsigned int> sqrt(num) + 1:
break
return 1
cpdef list primes_below(unsigned int x):
cdef:
unsigned int[::1] primes = cy_array(shape=(x,), itemsize=sizeof(int), format="I")
unsigned int counter = 0
unsigned int i
for i in range(2, x):
if is_prime(i, primes, counter):
primes[counter] = i
counter += 1
return [primes[i] for i in range(counter)]
我发现 Cython 数组既快速又简单,因此使用 malloc 分配内存是多余的。
【问题讨论】: