数学知识:质数相关讲解——质数判定,分解质因数及筛质数

🎬 博主名称个人主页

🔥 个人专栏 : 《算法通关》《Java讲解》

⛺️心简单,世界就简单

序言

今天讲一下质数的内容,下面是目录可以看看
目录

😍质数

😍质数的判定

😁试除法(暴力)

😁优化

😍分解质因数

😁试除法(暴力)

😁优化

😍筛质数

😁思想

😁最朴素算法

😁稍微优化算法---埃氏筛

😁线性筛(欧拉筛)


质数

定义:在大于1的整数中,只含有1和自身这两个约数,就被称为质数

质数的判定

试除法(暴力)

这个是最暴力的写法,时间复杂度是o( n )

cpp 复制代码
bool is_prime(int n){
	if(n < 2) return false;
	for(int i = 2; i < n; i ++){
		if(n % 2 == 0) return false;
	}
	return true; 
} 

优化

如果d | n(| 这个是整除符号,就是如果d能整除n),那么(n/d) | n,比如12,如果2是12的约数,那6也是,3是12的约数,那4也是,所以我们发现他们都是成对存在的。那我们枚举时候,是不是就可以只枚举那一对里较小的一个,也就是d <= n/d,也就是d^2 <= n,也就是d <= sqrt(n),我们就吧时间1优化到了o(sqrt(n)).

我们的写法也很多,比如循环时候写 i <= sqrt(n),这个我们每次循环都要调用,时间比较慢,还有的会写 i * i < n,这个如果我们的 i * i超过了int 范围就会变成负数,我们最终就写i <= n / i,这个就不会超出范围了,

cpp 复制代码
bool is_prime(int n){
	if(n < 2) return false;
	for(int i = 2; i <= n / i; i ++){
		if(n % 2 == 0) return false;
	} 
	return true; 
} 

分解质因数

试除法(暴力)

我们小时候正常写法,可能就是从小到大开始一直枚举所有数

cpp 复制代码
void get_prime(int n){
	for(int i = 2; i <= n; i++){
		if(n % i == 0){
			int s =0 ;
			while(n % i == 0){
				n/=i;
				s ++;
			} 
			printf("%d %d\n", i, s); 
		} 
	} 
} 

优化

我们的循环条件是可以变一下的,我们知道n中最多只包含一个大于sqrt(n)的质因子 ,所以我们枚举时候可以先把小于sqrt(n)的质因子枚举出来,然后最后判断n是不是 > 1,是的话就说明n就是那个大于sqrt(n)的质因子了,这里需要注意的是n是不断在变化的,这里n虽然不断在变化但也不会影响这个算法的正确性,可以拿 n = 34试试

cpp 复制代码
void get_prime(int n){
	for(int i = 2; i <= n / i; i++){
		if(n % i == 0){
			int s =0 ;
			while(n % i == 0){
				n/=i;
				s ++;
			} 
			printf("%d %d\n", i, s); 
		} 
	} 
	if(n > 1) printf("%d %d\n",n , 1);
	puts("");
} 

筛质数

思想

我们基本思想是这样筛的,先写出一串数2 3 4 5 6 7 8 9 10 11 12 ......我们把每个数的倍数都删掉,我们先看2,那我们就把2的倍数都删掉,然后再看3再把3的倍数都删掉........

对于一个P来说,如果他没有被删掉,那就说明从2~P-1里都没有把他删掉,说明P不是2~P-1里任何一个数的倍数,那就说明2~P-1里没有任何一个数是P的约数,那他就是一个质数


最朴素算法

时间复杂度大约是这个o(nlogn),因为计算上稍微繁琐,就不写时间复杂度怎么算的了,可以自己问问ai

cpp 复制代码
void get_prime(int n){
	for(int i = 2; i <= n; i++){
		if(!st[i]){
			primes[cnt ++] = i;
		}
	} 
	for(int j = i + i; j <= n; j += i) st[j] = true; 
} 

稍微优化算法---埃氏筛

我们可以把下面那个循环放入 if 里,我们只需要把质数的倍数给筛掉就行

时间复杂度是o(nloglogn)

cpp 复制代码
void get_prime(int n){
	for(int i = 2; i <= n; i++){
		if(!st[i]){
			primes[cnt ++] = i;
			for(int j = i + i; j <= n; j += i) st[j] = true; 
		}
	} 

} 

线性筛(欧拉筛)

这个基本思路也差不多,就是把每个合数用它的某一个质因子把他筛掉就行

他的思想有两种情况

1, i % primes[ j ] == 0. primes[ j ]一定是 i 的最小质因子,primes[ j ]一定是primes[ j ] * i 的最小质因子

2,i % primes[ j ] != 0. primes[ j ]一定小于 i 的最小质因子,primes[ j ]一定是primes[ j ] * i的最小质因子

核心思想:每个合数只被它的最小质因子筛掉一次

st[primes[ j ] * i] = true ; 这个就是核心思想

每个合数 x 都可以表示为:

复制代码
x = 最小质因子 × 某个数
在欧拉筛中:primes[j] 是质数i 是当前遍历的数primes[j] × i 就是要标记的合数关键:通过控制 i和 primes[j] 的组合,确保每个合数只被它的最小质因子标记一次

然后为了防止出现重复,如果(i % primes[j] == 0)我们就break就行

还有就是循环条件

我们要标记的合数是 primes[j] × i,必须满足:primes[j] × i ≤ n

变形得到 primes[j] ≤ n / i

cpp 复制代码
const int N = 1e6 +10; 

int primes[N], cnt;
bool st[N]; 

void get_prime(int n){
	for(int i = 2; i <= n; i++){
		if(!st[i]) 	primes[cnt ++] = i;
			for(int j = 0; primes[j] <= n / i; j ++){
				st[primes[j] * i] = true;
				if(i % primes[j] == 0) break;
			} 
	} 

} 

完结下一章讲约数,给个三连吧,谢谢了

相关推荐
一叶落4381 天前
167. 两数之和 II - 输入有序数组【C语言题解】
c语言·数据结构·算法·leetcode
地平线开发者1 天前
征程6 MCU safetylib sample
算法·自动驾驶
小范自学编程1 天前
算法训练营 Day37 - 动态规划part06
算法·动态规划
星空露珠1 天前
迷你世界UGC3.0脚本Wiki角色模块管理接口 Actor
开发语言·数据库·算法·游戏·lua
我星期八休息1 天前
深入理解哈希表
开发语言·数据结构·c++·算法·哈希算法·散列表
一叶落4381 天前
LeetCode 54. 螺旋矩阵(C语言详解)——模拟 + 四边界收缩
java·c语言·数据结构·算法·leetcode·矩阵
寻寻觅觅☆1 天前
东华OJ-进阶题-19-排队打水问题(C++)
开发语言·c++·算法
Storynone1 天前
【Day27】LeetCode:56. 合并区间,738. 单调递增的数字
python·算法·leetcode
Boop_wu1 天前
[Java 算法] 模拟
算法
khddvbe1 天前
C++中的代理模式实战
开发语言·c++·算法