(算法初学者)质数筛法

一边用与找质数,不会单独出题,但是会成为题目的一部分(先找出质数再去解题)

以下3个为时间复杂度依次降低的方法

首先要了解质数的定义:质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定1既不是质数也不是合数)。

1普通的筛选质数(时间复杂度为n^2)

基本思路:在prime数组中从2到i-1(排除1和本身)遍历如果能整除的就是质数然后是质数返回1,不是返回0,再在main中从2到n-1中找到1(质数)然后再输出。

#include
using namespace std;
int n;
int prime[10005];
bool isPrime(int i) {
	for (int j = 2; j <= i - 1; j++) {
		if (i % j == 0) {
			return 0;//i不是质数
		}
	}
	return 1;
}
int main() {
	cin >> n;
	for (int i = 2; i <= n; i++) {
		if (isPrime(i) == 1) {
			cout << i << " ";
		}
	}
	return 0;
}

2优化1(埃氏筛法)时间复杂度O(nloglogn)

基本思路:在prime数组中遍历2到i-2整除找质数都等于1(质数),不同的是后面不是直接套2到n-1来一个一个找。而是每找到一个合数比如4=2*2(然后把他的倍数4*2,4*3等给排除掉(排除即在数组中对应值赋值为合数(0))查找时是质数就是1不是为0;以下为代码

#include
using namespace std;
int prime[10005], k = 0;
bool isPrime[10005];
int main() {
	cin >> n;
	//初始化认为全部是质数
	for (int i = 2; i <= n; i++) {
		isPrime[i] = 1;
	}
	//埃氏筛法
	for (int i = 2; i <= n; i++) {
		if (isPrime[i] == 1) {//说明i是质数
			k++;
			prime[k] = i;
			for (int j = i * 2; j <= n; j += i) {//枚举i的倍数---是合数
				isPrime[j] = 0;
			}
		}
	}
	//2---n之间的美每个数的性质,查询isPrime[10005]
	//得到质数数组prime[10005]
	cin >> x;
	if (isPrime[x] == 1)cout << "Y" << endl;
	else cout << "N" << endl;
	return 0;
}

缺点是把合数筛了几次比如4(把4*4=16的16筛了,8把8*2=16筛了)

3优化2(欧拉筛法)时间复杂度O(n)原理规定每个合数只会被他的最小质因数筛去。最小质因数是(比如12的最小质因数就是2)(6的最小质因数就是2)(15的最小质因数是3)

基本思路:用i遍历从2到n-1把数组中是质数的值变成质数本身i,再枚举质数表把质数的i倍筛掉超过范围的就break,通过赋值为(0)筛掉,(重点)if(i%prime[j]==0)break;这个挺难理解,看了两集视频。我的理解如下

i            2 3 4 5 6 7 8 9 10 11 12 

prime[j] 2

一开始p等于2时把4筛掉然后发现2能整除2break了,

下一轮p等于3时把6 9筛掉

再下一轮p等于4把8筛掉而不用筛12(注意)这里因为i能把prime[j]整除所以不要筛,因为3*4能被后面的6*2筛掉的(数学问题别问为什么)。最后每一个合数只筛一次所以时间复杂度为O(n)

以下为代码

#include
using namespace std;
int prime[100005], k = 0;
int isPrime[100005];
int main() {
	cin >> n;
	//初始化认为全部是质数
	for (int i = 2; i < n; i++) {
		isPrime[i] = 1;
	}
	//欧拉筛法
	for (int i = 2; i <= n; i++) {//枚举需要判断的每一个数
		if (isPrime[i] == 1) {//i是质数
			k++;
			prime[k] = i;
		}
		//枚举质数表---质数表数据是有序的
		for (int j = 1; j <= k; j++) {
			x = i * prime[j];//第j个质数的i倍
			if (x > n) {//超过范围跳出
				break;
			}
			isPrime[x] = 0;//把x标记成合数//筛掉
			//看看后面的质数还能不能枚举
			if (i % prime[j] == 0) {//保证了只筛选到以prime
				//2 3 4
				//2中4能整除2筛掉
				//说明p是i的最小质因数
				//换言之,i之前被prime[j]筛选过,prime[j]是升序的,如果继续
				//循环,让i乘上后面的质数,得到的合数不是被prime[j]筛掉的
				break;
			}
		}
	}

}

你可能感兴趣的:(算法,c++)