C++最精细求素数总结

491 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情


想必大家都遇到过素数的题目,我们先来追根溯源一下,什么是素数?

什么是素数?

素数又叫做质数,指的是在大于1的自然数的情况下,除了1和它本身以外不再有其他因数的自然数 image.png


接下来介绍几种常见的求素数的方法~

试除法:

这可以说是最简单的求素数的方法了,但是试除法有很多种变形,我们一种一种介绍:

循环到此数的前一个:

代码总览image.png 关键代码

bool isPrime(int x) {
	if (x == 1 || x == 0)
		return false;
	for (int i = 2; i < x; i++) {
		if (x % i == 0)
			return false;
	}
	return true;
}

测试案例image.png image.png 思考: 这种方法可以说是按照概念来求素数了,但是我们需要看到的是这样的时间复杂度太高了,如果数字太大的话,会很浪费时间,那么有没有改进方法呢?

sqrt函数:

代码总览image.png 关键代码

bool isPrime(int x) {
	if (x == 1 || x == 0)
		return false;
	for (int i = 2; i <= sqrt(x); i++) {
		if (x % i == 0)
			return false;
	}
	return true;
}

测试案例image.png image.png 思考:为什么能行呢?这是由于合数的概念,比如说12有因数2和6,对于非素数来说判断到2就结束了,但是对于13这样的素数却要执行11次,但如果开个平方就可以节省很多时间
但其实还有更优秀的做法:

x/i:

代码总览image.png 关键代码

bool isPrime(int x) {
	if (x == 1 || x == 0)
		return false;
	for (int i = 2; i <= x / i; i++) {
		if (x % i == 0)
			return false;
	}
	return true;
}

测试案例image.png image.png 思考:同样的这是利用了合数的性质,可以做进一步的优化


以上就是试除法的全部内容,试除法是最简单的求素数方法,接下来讲一讲高级一点的埃氏筛法

什么是埃氏筛法?

埃氏筛法,全名埃拉托斯特尼筛法,简称埃氏筛或爱氏筛,是一种由希腊数学家埃拉托斯特尼所提出的一种简单检定素数的算法。要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的倍数剔除,剩下的就是素数。 image.png 原理:创建一个boolean数组,初始化0和1为质数,将0和1筛掉,循环数组,若i为质数,将当前的i的倍数筛掉


比如这里求0~100的素数,如果用试除法的话会很慢,需要执行若干次,但是看完埃氏筛法的操作请直呼

代码如下

#include<iostream>
using namespace std;
const int N = 100;
bool isprime[N + 1];
int main()
{
	//0和1倍删掉,不为素数
	isprime[0] = isprime[1] = true;
	for (int i = 2; i <= N; i++) {
		//如果当前数为素数
		if (!isprime[i]) {
			for (int j = 2 * i; j <= N; j += i) {
				//将i的倍数筛掉
				isprime[j] = true;
			}
		}
	}
	//输出0~100的所有素数
	for (int i = 0; i <= N; i++) {
		if (!isprime[i])
			cout << i << " ";
	}
	return 0;
}

结果展示image.png 思考:埃氏筛法存在一个缺点——重复筛了,例如6会被2筛一次后再被3筛一次,有没有更加好的方法不会重复筛呢?有!那就是大名鼎鼎的欧拉筛法

欧拉筛法:

什么是欧拉筛法?

欧拉筛法是埃氏筛法思维的进阶版,它解决了埃氏筛重复筛的问题


将找到的素数储存质数数组,通过循环已经储存的质数来判断筛掉当前质数的i倍,Prime[j]即为i*Prime[j]的最小质数保证合数被它分解的最小质数筛掉

代码如下

#include<iostream>
using namespace std;
const int N = 100;
bool isPrime[N + 1];//判断质数
int Prime[N];//储存质数
int t;//存储下标
int main()
{
	//0和1倍删掉,不为素数
	isPrime[0] = isPrime[1] = true;
	for (int i = 2; i <= N; i++) {
		//如果当前数是质数的话,就将当前的数加入质数数组
		if (!isPrime[i])
			Prime[t++] = i;
		//如果当前数是质数的话,就将当前的数加入质数数组
		for (int j = 0; j < t && Prime[j] * i <= N; j++) {
			//如果当前数能够整除第j个质数,保证只标记一次
			isPrime[Prime[j] * i] = true;
			if (i % Prime[j] == 0)
				break;
		}
	}
	//输出0~100的所有素数
	for (int i = 0; i <= N; i++) {
		if (!isPrime[i])
			cout << i << " ";
	}
	return 0;
}

结果展示image.png PS:好的今日的分享就到这啦~