defEratosthenes(n): primes = [1for _ inrange(n+1)] primes[0] = primes[1] = 0 count = 0 for i inrange(2, n + 1): if primes[i]: for j inrange(i * 2, n + 1, i): primes[j] = 0 count += 1 c = 0 for i in primes: if i: c += 1 return count,c defEuler(n): primes = [1for _ inrange(n+1)] primes[0] = primes[1] = 0 res = [] count = 0 for i inrange(2, n + 1): if primes[i]: res.append(i) j = 0 index = res[j] * i while j < len(res) and index <= n: count += 1 primes[index] = 0 if i % res[j] == 0: break else: j += 1 index = res[j] * i return count,len(res)
其中第一个返回值是筛除次数,第二个返回值是素数个数。
由于 Python 中数组的处理可以使用 Numpy,所以我又对代码进行了改进,得到了一系列函数:
首先,对于 Eratosthenes 方法,从 2i 开始显然是低效的,改为从 i2 开始:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
defEratosthenes_improved(n): primes = [1for _ inrange(n+1)] primes[0] = primes[1] = 0 count = 0 for i inrange(2, n + 1): if primes[i]: for j inrange(i * i, n + 1, i): primes[j] = 0 count += 1 c = 0 for i in primes: if i: c += 1 return count,c
考虑使用 Numpy。事实上,之后的实验发现,不恰当的使用 Numpy 并不会提升速度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
defEratosthenes_numpy(n): primes = np.ones(n + 1) primes[0] = primes[1] = 0 count = 0 for i inrange(2, int(n ** 0.5) + 1): if primes[i]: for j inrange(i * i, n + 1, i): primes[j] = 0 count += 1 c = 0 for i in primes: if i: c += 1 return count,c
对于 Euler 方法,append 的使用会影响速度。事实上,在埃氏筛的算法中,缺少了生成素数列表的操作,额外的实验表明 ,这些操作所占的时间并不会影响整体的结果。不过,这里还是对 append 进行了改进,另外也消除了 len 对于速度的影响:
defEuler_improved(n): primes = [1for _ inrange(n + 1)] primes[0] = primes[1] = 0 res = [1for _ inrange(int(n / 2))] count = 0 c = 0 for i inrange(2, n + 1): if primes[i]: res[c] = i c += 1 j = 0 index = res[j] * i while j < c and index <= n: count += 1 primes[index] = 0 if i % res[j] == 0: break else: j += 1 index = res[j] * i return count
for i inrange(2, n + 1): if primes[i]: res.append(i) j = 0 index = res[j] * i while j < len(res) and index <= n: count += 1 primes[index] = 0 if np.mod(i, res[j]) == 0: break else: j += 1 index = res[j] * i return count
defEuler_improved_numpy(n): primes = np.ones(n + 1) primes[0] = primes[1] = 0 res = np.zeros(int(n / 2)) count = 0 c = 0 for i inrange(2, n + 1): if primes[i]: res[c] = i c += 1 j = 0 index = int(res[j] * i) while j < c and index <= n: count += 1 primes[index] = 0 if i%res[j] == 0: break else: j += 1 index = int(res[j] * i) return count
defEuler_improved_numpy_with_mod(n): primes = np.ones(n + 1) primes[0] = primes[1] = 0 res = np.zeros(int(n / 2)) count = 0 c = 0 for i inrange(2, n + 1): if primes[i]: res[c] = i c += 1 j = 0 index = int(res[j] * i) while j < c and index <= n: count += 1 primes[index] = 0 if np.mod(i,res[j]) == 0: break else: j += 1 index = int(res[j] * i) return count
defEratosthenes_numpy_improved(n): primes = np.ones(n + 1) primes[0] = primes[1] = 0 for i inrange(2, int(n ** 0.5) + 1): if primes[i]: primes[i * i::i] = 0 c = 0 for i in primes: if i: c += 1 return c
defprimes(n): sieve = [True] * n for i inrange(3,int(n**0.5)+1,2): if sieve[i]: sieve[i*i::2*i]=[False]*((n-i*i-1)//(2*i)+1) res = [2] + [i for i inrange(3,n,2) if sieve[i]] returnlen(res) defprimesfrom2to(n): sieve = np.ones(n//3 + (n%6==2), dtype=bool) for i inrange(1,int(n**0.5)//3+1): if sieve[i]: k=3*i+1|1 sieve[k*k//3::2*k] = False sieve[k*(k-2*(i&1)+4)//3::2*k] = False res = np.r_[2,3,((3*np.nonzero(sieve)[0][1:]+1)|1)] returnlen(res)
jit = df[11:] plt.figure(figsize=(10,10)) for i inrange(8,0,-1): res = to_float(jit.loc[:,str(10 ** i)]) if i == 1: plt.barh(funcs,res['y'],alpha = 0.3) else: plt.barh(funcs,res['y']) plt.yticks(fontsize = 10) plt.subplots_adjust(left=0.25) plt.legend(loc = 'upper right',labels = [10 ** i for i inrange(8,0,-1)]) plt.savefig('jit.svg') plt.show()