本帖最后由 happy886rr 于 2016-4-17 13:25 编辑
在python中分片的扩展形式:str[I,J,K]意思是从I到J-1,每隔K个元素索引一次,如果K为负数,就是按从右往左索引。那么这个分片到底有什么作用呢?承接上一集“吹蜡烛”里的“打印出100万以内的所有素数“。
当时使用的是for循环去吹蜡烛,外层for生成素数,内层for吹烛,感觉for循环嵌套的效率非常低,发现吹蜡烛这个事用分片就可以做,没必要使用for。于是,全部替换成分片操作,效率瞬间提升10倍。但是每个蜡烛如果都用普通数组表示也忒非内存了!无意中发现python教程中还有bytearray这种字节型数组,好家伙这不是要逆天了吗?一个字节8位,为了少写点代码,就不研究怎么用bit来表示蜡烛了,直接用了byte表示蜡烛,效率再次提升3倍,最后利用素数循环节(直接跳过前4轮)再提速0.4秒。实现0.8秒左右便可构建出一亿内的素数(当然不算打印时间),只比julia慢0.2秒。- #---------------------Python Primes---------------------
- def primes(N):
- print("构建{0}内的素数...".format(N))
- L=[0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1]
- r=bytearray(L)*(N//210)+bytearray(L[0:N%210])
- r[1]=0;r[2]=r[3]=r[5]=r[7]=1
- for i in range(11,int(N**0.5),2):
- if r[i]:r[i*i::i*2]=bytearray([0])*((N-i*i)//(i*2)+1)
- input("是否开始打印?(回车)")
- for i,p in enumerate(r):
- if p:
- print(i,",",end="")
- #-------------------------------------------------------
- primes(10**8)
复制代码 后来又想,吹蜡烛太费气力了,如果要打印出一亿以内的所有素数,那么我要吹灭数千万根蜡烛,还不如改吹蜡烛为点蜡烛,这个算法没有办法用分片实现,用C语言试了下,效率很满意,0.130秒出一亿内素数。可能由于python是脚本语言,同样的算法速度总是无法跟C比。
以下是模块化后的版本,筛选一亿内素数速度接近0.6秒。- #---------------------Count Time------------------------
- def Tim(X):
- import time
- def F(*a, **b):
- t=time.time()
- a=X(*a, **b)
- print("[{0}s @{1}]".format(time.time() - t, X.__name__))
- return a
- return F
- #---------------------Primes----------------------------
- @Tim
- def primes(N):
- print("构建{0}内的素数...".format(N))
- r=bytearray([1])*(N//2)
- for i in range(3,int(N**0.5)+1,2):
- if r[i//2]:r[i*i//2::i]=bytearray((N-i*i-1)//(i*2)+1)
- return r
- #---------------------Print Primes----------------------
- def printf(R):
- input("开始打印?(回车)")
- R[0]=0;print(2,",",end="")
- for i,p in enumerate(R):
- if p:
- print(i*2+1,",",end="")
- #---------------------Main------------------------------
-
- R=primes(10**8)
- printf(R)
复制代码 将分片函数改造一下,变成身为Primest(K)函数,返回第K个素数。高性能的分片函数筛选器,寻找第K个素数,只需筛选K*24范围内的数。- #---------------------Count Time------------------------
- def Tim(X):
- import time
- def F(*a, **b):
- t=time.time()
- a=X(*a, **b)
- print("[{0}s @{1}]".format(time.time()-t, X.__name__))
- return a
- return F
- #---------------------Primest---------------------------
- @Tim
- def Primest(K):
- N=K*24
- r=bytearray([1])*(N//3);r[0]=0;j=2
- for i in range(int(N**0.5)//3+1):
- if r[i]:
- I=3*i+1|1
- r[((I*I)//3)::2*I]=bytearray([0])*((N//6-(I*I)//6-1)//I+1)
- r[(I*I+4*I-2*I*(i&1))//3::2*I]=bytearray([0])*((N//6-(I*I+4*I-2*I*(i&1))//6-1)//I+1)
- for i,p in enumerate(r):
- if p:
- j+=1
- if j==K:
- print("* The {0}st prime number is :{1}".format(K,i*3+1|1))
- break
- #-------------------------------------------------------
-
- Primest(10001) #调用Primest(K)函数,返回第K个素数
复制代码 如此解欧拉计划第七题:求第一万零一个素数?可直接调用Primest(K)函数,0.001秒出答案。 |