找素数

问题描述:

统计小于某个正整数的所有素数(质数)的个数。为了方便叙述,我们将这个数字设为n。

解题思路:

素数的定义是一个只能被1或自己整除的正整数,并且不包括0和1。

于是,一种朴素的算法思想就出现了:将搜索空间定位了2~n-1,然后遍历搜索空间中的所有元素,并逐一判断该元素是否仅能被1或自己整除。

显然这种算法是易于理解但效率极低的。因为在这个过程中,有很多显而易见不是素数的元素也被判断了(比如除2以外的偶数),
并且每进行一次判断时,都需要从头开始做取余操作,没有有效利用前面元素进行判断时的信息。所以这种思想是不可取。

所以在这介绍一个经典的找素数算法:Sieve of Eratosthenes
这是一个经典且古老的算法,算法的基本思想所有正整数可以被分为1,组合数和质数三种。而组合数一定可以分解为若干个质数的乘积。
所以当每找到一个质数时,将所有小于n且包含该素数的组合数剔除掉,剩下的元素就全是素数了。

算法步骤如下:
(1) 创建一个数组, 用于保存从 2 ~ n-1 之间的数;
(2) 令 p = 2(数列中最小的质数);
(3) 循环操作: 以p为步长,遍历所有2p ~ n-1 之间的数,所有被遍历过的数均标记为非质数
(4) 将第一个比p大的质数(还未被标记的数)赋值给p,并重复步骤(3);
(5) 当p的值大于等于n时,算法终止,数组中所有未被标记的数即是质数,统计这些数的个数即可;

为了进一步搜索搜索空间,我们可以使用如下2个思路:
(1) 每次搜索p的组合数时,从p^2开始而不是从2p。原因是当搜索到p时,比p^2小的组合数都已经被剔除了,不需要重复判断;
(2) 质数一定不包括除2以外的其他偶数,所以只需对奇数进行判断即可;

最后,贴上我自己的C++实现,供大家参考:

#include <vector>
#include <cmath>

int countPrimes(int n)
{
	int res = 0;
	if(n < 3) return res;
	std::vector<bool> status(n, true);
	
	res = n / 2;
	for(int i = 3; i < n; i +=2)
	{
		if(i >= sqrt(n)) continue; // 防止 i^2 越界翻转
		for(unsigned long long  j = i*i;  j < n; j += 2*i)
		{
			if(status[j])
			{
				--res;
				status[j] = false;
			}
		}
	}
	return res;
}

猜你喜欢

转载自blog.csdn.net/endurehero/article/details/82560241
今日推荐