快速寻找给定上限内质数的3个算法

定义:
一个大于1的自然数,除了1和它本身外,不能被其他自然数整除(除0以外)的数称之为质数(素数)。

判断一个大于1的自然数n是否为质数的方法:
1. 遍历法
分析:

根据定义,质数只能被1和本身整出,最简单的方法就是遍历,看[2,n-1]之间有没有整数能整除n。
优化:
区间的上限可以缩小,假设可以缩小到m。如何确定m呢?我们希望m能够达到这样一个条件:对于任意的正整数x,m*x>=n。因为我们是从2开始遍历到m,所以x>=m,则m=ceil(sqrt(n))。注意,这里对n的平方根(sqrt)取上整(ceil),为的是保证m*m>=n,因为sqrt(n)的小数点可能会出现.99999...的情况。
代码:

def trial_division(n):
	numbers = [False for i in range(n + 1)]
	
	for i in range(2, n + 1):
		if i == 2 or i ==3:
			#print i,
			numbers[i] = True
			continue

		# Ceil no floor! Because we must make sure root ** 2 > n
		root = int(math.ceil(math.sqrt(i)))
		flag = True
		for j in range(2, root + 1):
			if i % j == 0:
				flag = False
				break

		if flag:
			#print i,
			numbers[i] = True

	return numbers

2. Sieve of Eratosthenes
核心思想:

筛掉每一个质数的倍数。
算法描述:
1. 创建一个从2到n的连续数列(2, 3, 4, ..., n)。
2. 初始化下一个质数p为2。
3. 从p开始,通过递增p来遍历p的倍数,将除p本身以外的倍数(2p, 3p, 4p, ...)标记为合数。
4. 寻找大于p的第一个质数。若不存在,算法终止;若存在,更新p,重复步骤3。
例子:

http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#mediaviewer/File:Sieve_of_Eratosthenes_animation.gif


分析:
按照我的理解,这个算法类似于构建一个特殊的乘法表,乘法表以外的数为质数。
优化:
1. 步骤3可以从p*p开始来遍历p的倍数,原因在于所有小于p*p的p的倍数在之前已经被遍历了。同时,当p*p大于等于给定上限n时,算法就可以终止了,因为所有小于p*p的合数都已经被遍历过了。
2. 步骤1创建只包含奇数的连续数列 (3, 5, ..., n),步骤3递增2p,即3p, 5p, 7p, ...标记为合数。这么做实际上是过滤了所有2的倍数,即留下的都是与2互质的数。
代码:

# Core idea: Delete the multiples of each prime
def sieve_of_eratosthenes(n):
	numbers = [True for i in range(n + 1)]
	for i in range(4, n + 1, 2):
		numbers[i] = False 

	# Ceil no floor! Because we must make sure root ** 2 > n
	root = int(math.ceil(math.sqrt(i)))

	next_prime = 3
	while next_prime <= root:
		next_composite = next_prime ** 2
		for i in range(next_composite, n + 1, 2 * next_prime):
			numbers[i] = False

		next_prime += 2
		while next_prime <= root:
			if numbers[next_prime]:
				break
			next_prime += 2
	'''
	for i in range(2, n + 1):
		if numbers[i]:
			print i,
	'''
	return numbers	

3. Sieve of Euler
核心思想:
Sieve of Euler是Sieve of Eratosthenes的一个变种,数列中留下的数都是与之前找到的质数互质的数(每一个大于2的质数都是与小于它本身的所有质数互质的数)。
算法描述:
1. 创建一个从2到n的连续数列(2, 3, 4, ..., n)。
2. 下一个质数p为数列中的第一个数,将p与数列中的每一个数相乘(包括p本身),将相乘结果从数列中删除。
3. 重复步骤2,直到p*p>n,即没有数可以再从数列中删除了。
分析:
Sieve of Euler与Sieve of Eratosthenes的不同在于:对每个合数只访问一次。从手工计算上来说,Sieve of Euler比Sieve of Eratosthenes要方便简单;但是,对计算机来说,Sieve of Euler却比Sieve of Eratosthenes要慢,原因在于,Sieve of Euler需要额外维护一个数列,来存储p与数列中的每一个数相乘的结果。
代码:
# Core idea: Leave out numbers that are coprime with already found prime numbers 
def sieve_of_euler(n):
	numbers = [True for i in range(n + 1)]

	# Ceil no floor! Because we must make sure root ** 2 > n
	root = int(math.ceil(math.sqrt(i)))

	next_prime = 2
	while next_prime <= root:
		deltions = []
		for i in range(next_prime, n + 1):
			if numbers[i]:
				next_composite = next_prime * i
				if next_composite > n:
					break
				else:
					deltions.append(next_composite)
		
		for i in deltions:
			numbers[i] = False

		next_prime += 1
		while next_prime <= root:
			if numbers[next_prime]:
				break
			next_prime += 1
	'''
	for i in range(2, n + 1):
		if numbers[i]:
			print i,
	'''
	return numbers

运行时间比较:
我们通过循环1000次,每次循环寻找从0到99999的质数,来比较代码中3种算法的性能。运行时间见下图:

Reference:

http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EnjoyCodingAndGame

愿我的知识,成为您的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值