质数

本文深入探讨了质数的判断与分解方法,包括试除法、分解质因数及筛法。试除法通过枚举小于n的平方根来判断质数;分解质因数时,保证每个合数在枚举到其质因子前已被分解干净;筛法中,朴素筛法与埃氏筛法分别通过标记倍数和仅标记质数倍数来筛选质数,线性筛法则利用每个合数仅被最小质因子筛掉的特点,实现线性时间复杂度的优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

质数

试除法判断质数

因为一个数的因数都是成对出现的,比如 121212 的因数是 2,62,6263,43,434 等等,所以我们只要枚举较小的那个因数就可以了,设较小的因数为 ddd ,要求的数为 nnnnd\frac{n}{d}dnddd 都能整除 nnn ,即 d∣nd|ndnnd∣n\frac{n}{d}|ndnn

所以枚举时,只枚举 d≤ndd \le \frac{n}{d}ddn 的数

整理得 d≤nd \le \sqrt{n}dn

Code

bool is_prime(long long x) {
	if (x < 2) return false;
	for (long long i = 2; i <= x / i; i ++ )
		if (x % i == 0)
			return false;
	return true;
}

分解质因数

试除法

从小到大枚举所有数(既枚举了质数,又枚举了合数为什么不会出问题?因为在枚举到这个合数之前,就意味着把 2∼i−12\sim i-12i1 之间是质因子全部除干净了,则当前的 nnn 当中已经不含所有 2∼i−12\sim i-12i1 之间的质因子了,iii 中也不包含任何 2∼i−12\sim i-12i1 之间的质因子,则 iii 一定是质数)

注意到,nnn 中最多只包含一个大于 n\sqrt{n}n 质因子,所有可以先枚举完小于 n\sqrt{n}n 的质因子,单独处理那个大于 n\sqrt{n}n 的质因子,优化时间复杂度 O(n)→O(logn∼n)O(n)\to O(logn\sim\sqrt{n})O(n)O(lognn) (上界)

Code

给定 111 个正整数 aia_iai ,将每个数分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。

void div(int n) {
	for(int i = 2; i <= n / i; i++) {
		if(n % i == 0) { //i 一定是质数
			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("");
}

筛质数

https://www.acwing.com/problem/content/870/

朴素筛法

从前往后取质数,把所有数的倍数全部删掉,这样筛过之后,剩下的数就是质数了

证明

对于任何一个没有被筛掉的数 ppp 而言,2∼p−12\sim p-12p1 中的任何一个数都不是 ppp 的约数,所以 ppp 就是一个质数

Code

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

//筛从2->n 之间的质数
void get_prime(int n) {
	for(int i = 2; i <= n; i++) {
		if(!st[i]) { //如果一个数没有被筛过,那么它是质数
			prime[cnt++] = n;
		}
		for(int j = i + i; j <= n; j += i) {
			st[j] = true;
		}
	}
}

时间复杂度

O(n2+n3+n4+...+nn)=O(n(12+13+14+...+1n))O(\frac{n}{2}+\frac{n}{3}+\frac{n}{4}+...+\frac{n}{n})=O(n(\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+...+\frac{1}{n}))O(2n+3n+4n+...+nn)=O(n(21+31+41+...+n1))

注意到,12+13+14+...+1n\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+...+\frac{1}{n}21+31+41+...+n1 为调和级数

12+13+14+...+1n=ln(n)+c(c=0.57...)\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+...+\frac{1}{n}=ln(n)+c(c=0.57...)21+31+41+...+n1=ln(n)+c(c=0.57...)

所以,时间复杂度为

O(nln(n))≤O(nlog(n))O(nln(n))\le O(nlog(n))O(nln(n))O(nlog(n))

朴素筛法的优化,埃氏筛法

不用把所有数的倍数删掉,只要把质数的倍数删掉就可以了,当一个数不是质数时,就不需要筛掉它所有的倍数,所以只需要稍微更改一下上面的循环即可

void get_prime(int n) {
	for(int i = 2; i <= n; i++) {
		if(!st[i]) { //如果一个数没有被筛过,那么它是质数
			prime[cnt++] = n;
            for(int j = i + i; j <= n; j += i) st[j] = true;
		}
	}
}

时间复杂度

根据质数定理:1∼n1\sim n1n 中有 nln(n)\frac{n}{ln(n)}ln(n)n 个质数,时间复杂度为 O(nlog(logn))→O(n)O(nlog(logn))\to O(n)O(nlog(logn))O(n) (趋近于)

线性筛法

基本思路

把每个合数用它的质因子筛掉,但核心是 nnn 只会被它的最小质因子筛掉,每次筛掉当前质数和 iii 的乘积,当 imod  primes[j]=0i \mod{primes[j]}=0imodprimes[j]=0breakbreakbreak

正确性证明

  1. imod  primes[j]=0i \mod{primes[j]}=0imodprimes[j]=0 时,primes[j]primes[j]primes[j] 一定是 iii 的最小质因子,primes[j]primes[j]primes[j] 一定是 prime[j]×iprime[j]\times iprime[j]×i 的最小质因子 →\to 只用最小质因子筛掉了某个数
  2. imod  primes[j]≠0i \mod{primes[j]}\ne0imodprimes[j]=0 时,由于是从小到大枚举的所有质数,并且没有枚举到 iii 的任何一个质因子,说明 primes[j]primes[j]primes[j] 一定小于 iii 的所有质因子,prime[j]prime[j]prime[j] 也一定是 primes[j]×iprimes[j]\times iprimes[j]×i 的最小质因子
  3. 任何一个合数都会被筛掉

对于一个合数 xxx 一定存在一个最小质因子,假设 primes[j]primes[j]primes[j]xxx 的最小质因子,当 iii 枚举到 x÷primes[j]x\div primes[j]x÷primes[j] ,一定在枚举到 xxx 之前,所有 xxx 一定会被筛掉,并且,每个数都只会被筛一次,所以它的时间复杂度是 O(n)O(n)O(n)

Code

void get_prime(int n) {
	for(int i = 2; i <= n; i++) {
		if(!st[i]) prime[cnt++] = i;
		for(int j = 0; prime[j] <= n / i; j++) { //primes[j]*i<=n 只筛去小于n的质数
			st[prime[j]*i] = true;
			if(i % prime[j] == 0) break; //primes[j] 一定是 i 的最小质因子
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值