质数相关算法:统计小于整数n的质数个数
问题:现给定一个大于1的整数 N,求出小于N 的质数的个数。
例如:
输入 10
输出:4(2,3,5,7)
输入:20
输出: 8(2,3,5,7,11,13,17,19)
以下介绍5种算法,前3种思路一致,主要是效率的差异;
第5种采用了埃拉托斯特尼筛法进行计算(比较巧妙)
算法1:
针对小于N的每个正整数x,我们可以遍历从2到x-1遍历去试除,当出现一个数能被整除,那这个数就不是质数;当然这种方法也是最容易想到的方法,但效率也是最低的方法;
代码如下:
def prime(n):
m = 0
for i in range(2, n):
j = 2
for j in range(2, i-1):
if i % j == 0:
break
else:
m += 1
return m
算法2:
我们可以遍历从2到x/2的数去试除,这样子相对于第一种方法节省了近一半的时间;
代码如下:
def prime(n):
m = 0
for i in range(2, n):
j = 2
for j in range(2, int(i/2)+1):
if i % j == 0:
break
else:
m += 1
return m
算法3:
我们可以针对第二种算法进一步优化,遍历从2到√x;
因为数学的因数都是成对出现的,如果出现一个大于√x的因数,必然有一个小于√x的因数存在,因此我们遍历到√x就可以判定一个数是不是质数;
例如:16, 1*16, 2*8, 4*4;
代码如下:
def prime(n):
m = 0
for i in range(2, n):
j = 2
for j in range(2, int(i**0.5)+1):
if i % j == 0:
break
else:
m += 1
return m
算法4:
对于任意一个大于3的合数(除了1和自身外,还能被其他整数整除的数),我们都可以将其拆分成至少包含一个质数的因子相乘;
例如:8=2 x 4(2是质数),9=3 x 3;
因此,我们可以采用遍历小于这个数的所有质数来确认这个数是不是质数。
代码如下:
def prime(n):
if n <= 2:
return 0
else:
prime_list = list()
prime_list.append(2) # 先将2加入到要遍历的列表中
for x in range(3, n):
y = False
for y in prime_list:
if x % y == 0: # 对小于n的x进行遍历,当某个数整除为0时,不执行添加列表操作
y = False
break
else:
y = True
if y is True:
prime_list.append(x) # 对小于x的质数进行遍历后,没有能整除的质数,则这个数也是质数
return len(prime_list)
算法5: 埃拉托斯特尼筛法:
从2开始,将每个素数的各个倍数,标记成合数。一个素数的各个倍数,是一个差为此素数本身的等差数列。此为这个筛法和试除法不同的关键之处,前者是先找出所有合数,最后再剔除掉这些合数小于,得到所有素数。后者是逐一尝试每个待测数能否被大于2且小于它的整数整除,直接判断是否为素数。(摘自维基百科)
具体的实现方式:
先留下第一个素数2,将所有2的倍数的合数全部剔除;下一个素数3,将3的倍数的所有合数全部剔除;接着用5进行筛选剔除;当所要使用的素数的平方大于设定值时,停止筛选,则剩下的所有数均为素数。
例如:求出小于120的所有素数,依次使用2,3,5,7进行筛选,当到11时,11*11>120,停止筛选,剩余数即为质数。
这个代码写法非常巧妙,先用列表is_right ,存放n个True,代表小于n的所有数我们先看成质数小于,过程中判断为质数倍数的(也就是合数),用is_right[i],巧用下标改为Fasle
def prime(n):
if n <= 2:
return 0
is_right = [True] * n
is_right[1] = False
for i in range(2, int(n ** 0.5) + 1):
if is_right[i]:
for j in range(i * i, n, i):
is_right[j] = False
m = 0
for x in range(2, n):
if is_right[x]:
m += 1
return m
我来说两句