质数

质数

试除法判断质数

因为一个数的因数都是成对出现的,比如 12 12 12 的因数是 2 , 6 2,6 26 3 , 4 3,4 34 等等,所以我们只要枚举较小的那个因数就可以了,设较小的因数为 d d d ,要求的数为 n n n n d \frac{n}{d} dn d d d 都能整除 n n n ,即 d ∣ n d|n dn n d ∣ n \frac{n}{d}|n dnn

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

整理得 d ≤ n d \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 − 1 2\sim i-1 2i1 之间是质因子全部除干净了,则当前的 n n n 当中已经不含所有 2 ∼ i − 1 2\sim i-1 2i1 之间的质因子了, i i i 中也不包含任何 2 ∼ i − 1 2\sim i-1 2i1 之间的质因子,则 i i i 一定是质数)

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

Code

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

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/

朴素筛法

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

证明

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

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 ( n 2 + n 3 + n 4 + . . . + n n ) = O ( n ( 1 2 + 1 3 + 1 4 + . . . + 1 n ) ) 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))

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

1 2 + 1 3 + 1 4 + . . . + 1 n = l n ( 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 ( n l n ( n ) ) ≤ O ( n l o g ( 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 ∼ n 1\sim n 1n 中有 n l n ( n ) \frac{n}{ln(n)} ln(n)n 个质数,时间复杂度为 O ( n l o g ( l o g n ) ) → O ( n ) O(nlog(logn))\to O(n) O(nlog(logn))O(n) (趋近于)

线性筛法

基本思路

把每个合数用它的质因子筛掉,但核心是 n n n 只会被它的最小质因子筛掉,每次筛掉当前质数和 i i i 的乘积,当 i m o d    p r i m e s [ j ] = 0 i \mod{primes[j]}=0 imodprimes[j]=0 b r e a k break break

正确性证明

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

对于一个合数 x x x 一定存在一个最小质因子,假设 p r i m e s [ j ] primes[j] primes[j] x x x 的最小质因子,当 i i i 枚举到 x ÷ p r i m e s [ j ] x\div primes[j] x÷primes[j] ,一定在枚举到 x x x 之前,所有 x x x 一定会被筛掉,并且,每个数都只会被筛一次,所以它的时间复杂度是 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 的最小质因子
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值