小素数,大智慧

定义

素数(质数):在大于 1 的自然数中,只有 1 和该数本身两个因数的数 素数(质数):在大于1的自然数中,只有1和该数本身两个因数的数 素数(质数):在大于1的自然数中,只有1和该数本身两个因数的数

判断方法

方法1

时间复杂度 O ( n ) 时间复杂度O(n) 时间复杂度O(n)
定义法 定义法 定义法

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

方法2

时间复杂度 O ( n 2 ) 时间复杂度O(\sqrt{\frac{n}{2}}) 时间复杂度O(2n )

性质:对于合数 a ,一定存在一个最小素数 p ≤ a 使得 p 性质:对于合数a,一定存在一个最小素数p≤\sqrt{a}使得p 性质:对于合数a,一定存在一个最小素数pa 使得p ∣ | a (定性理解) a(定性理解) a(定性理解)

定量证明(反证法) 定量证明(反证法) 定量证明(反证法)
不妨设 a 为合数, p 是 a 的最小质因子,则 a p 是 a 的另一个因子,且 a p < p 不妨设a为合数,p是a的最小质因子,则\frac{a}{p}是a的另一个因子,且\frac{a}{p}<p 不妨设a为合数,pa的最小质因子,则paa的另一个因子,且pa<p
假设 p > a 假设p>\sqrt{a} 假设p>a
但是,由于 p 是 a 的最小质因子,所以 a p 不是质数,而是合数,那么 a p 中必定存在一个质因子 q (不妨设另一个因子为 r ) 但是,由于p是a的最小质因子,所以\frac{a}{p}不是质数,而是合数,那么\frac{a}{p}中必定存在一个质因子q(不妨设另一个因子为r) 但是,由于pa的最小质因子,所以pa不是质数,而是合数,那么pa中必定存在一个质因子q(不妨设另一个因子为r
假设 q > a ,那么 p × q × r > a × a × r = a × r > a 假设q>\sqrt{a},那么p×q×r>\sqrt{a}×\sqrt{a}×r=a×r>a 假设q>a ,那么p×q×r>a ×a ×r=a×r>a
又因为 a = p × q × r ,假设矛盾 又因为a=p×q×r,假设矛盾 又因为a=p×q×r,假设矛盾
所以, q ≤ a ,最小质因子 p > a 假设也矛盾 所以,q≤\sqrt{a},最小质因子p>\sqrt{a}假设也矛盾 所以,qa ,最小质因子p>a 假设也矛盾
所以, p ≤ a 所以,p≤\sqrt{a} 所以,pa

原理:检验 [ 1 , n ] 区间里的数是否有约数,检验区间从 [ 1 , n ] 缩小到 [ 1 , n ] 原理:检验[1,\sqrt{n}]区间里的数是否有约数,检验区间从[1,n]缩小到[1,\sqrt{n}] 原理:检验[1,n ]区间里的数是否有约数,检验区间从[1,n]缩小到[1,n ]

bool is_prime(int n){
    if(n <= 1) return false;
    for(int i = 2; i * i <= n; i++)
        if(n % i == 0) return false;
    return true;
}

方法3

在方法 2 的基础上,只筛奇数 在方法2的基础上,只筛奇数 在方法2的基础上,只筛奇数
因为除 2 以外的偶数都是合数,直接跳过,只关心奇数即可 因为除2以外的偶数都是合数,直接跳过,只关心奇数即可 因为除2以外的偶数都是合数,直接跳过,只关心奇数即可

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

方法4

原理:所有大于 3 的素数都可以表示为 6 n ± 1 的形式 原理:所有大于3的素数都可以表示为6n±1的形式 原理:所有大于3的素数都可以表示为6n±1的形式

证明: n ∈ N , n 可以用 6 n 、 6 n + 1 、 6 n − 1 、 6 n + 2 、 6 n − 2 、 6 n + 3 、 6 n − 3 表示 证明:n∈N,n可以用6n、6n+1、6n-1、6n+2、6n-2、6n+3、6n-3表示 证明:nNn可以用6n6n+16n16n+26n26n+36n3表示
其中, 6 n 是 6 的倍数,不是素数 其中,6n是6的倍数,不是素数 其中,6n6的倍数,不是素数
6 n ± 2 是偶数,只有 2 才是素数 6n±2是偶数,只有2才是素数 6n±2是偶数,只有2才是素数
6 n ± 3 是 3 的倍数,只有 3 才是是质数 6n±3是3的倍数,只有3才是是质数 6n±33的倍数,只有3才是是质数
所以,当 n > 0 时, 6 n ± 1 是质数 所以,当n>0时,6n±1是质数 所以,当n>0时,6n±1是质数
证毕 . 证毕. 证毕.

bool is_prime(int n){
	if(n <= 1) return false;
	if(n <= 3) return true;
	if(n % 2 == 0 || n % 3 == 0) return false;
	for(int i = 5; i * i <= n; i += 6) if(n % i == 0 || n % (i + 2) == 0) return false;//i : 6n - 1; i + 2 : 6n + 1
	return true;
}

方法5

埃拉托斯特尼( E r a t o s t h e n e s )筛法 埃拉托斯特尼(Eratosthenes)筛法 埃拉托斯特尼(Eratosthenes)筛法
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n, p = 0;//p:素数的个数 
	cin >> n;
	bool is_prime[n + 5];//标记是否为素数 
	int prime[n + 5];//prime[p]:第p位素数 
	for(int i = 0; i <= n; i++) is_prime[i] = true;
	is_prime[0] = is_prime[1] = false;
	for(int i = 2; i <= n; i++){
		if(is_prime[i] != 0){
			prime[p++] = i;
			for(int j = i + i; j <= n; j += i) is_prime[j] = false;
		}
	}
	return 0;
}

优化 1 优化1 优化1
只筛奇数 只筛奇数 只筛奇数
把 2 的倍数筛掉,直接让 i 从 3 开始循环每次加 2 把2的倍数筛掉,直接让i从3开始循环每次加2 2的倍数筛掉,直接让i3开始循环每次加2
这样做内存需求减半,操作大约也减半 这样做内存需求减半,操作大约也减半 这样做内存需求减半,操作大约也减半

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n, p = 1;//p:素数的个数 
	cin >> n;
	bool is_prime[n + 5];//标记是否为素数 
	int prime[n + 5];//prime[p]:第p位素数 
	for(int i = 0; i <= n; i++) is_prime[i] = true;
	is_prime[0] = is_prime[1] = false;
	prime[1] = 2;
	for(int i = 3; i <= n; i += 2){
		if(i % 2 != 0 && is_prime[i] != 0){
			prime[p++] = i;
			for(int j = i + i; j <= n; j += i) is_prime[j] = false;
		}
	}
	return 0;
}

优化 2 优化2 优化2
假设存在 x < i 2 ,不妨设 x = a × b ( a < b ) 假设存在x<i^2,不妨设x=a×b(a<b) 假设存在x<i2,不妨设x=a×b(a<b)
如果 a , b > i ,那么 a × b > i 2 ,与假设 x < i 2 矛盾 如果a,b>i,那么a×b>i^2,与假设x<i^2矛盾 如果a,b>i,那么a×b>i2,与假设x<i2矛盾
因此,有 a ≤ i 因此,有a≤i 因此,有ai
这意味着当我们循环 f o r 到 i 之前,就早已经将这个数 x 筛过 这意味着当我们循环for到i之前,就早已经将这个数x筛过 这意味着当我们循环fori之前,就早已经将这个数x筛过
所以我们优化从 i 2 开始标记 所以我们优化从i^2开始标记 所以我们优化从i2开始标记

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n, p = 1;//p:素数的个数 
	cin >> n;
	bool is_prime[n + 5];//标记是否为素数 
	int prime[n + 5];//prime[p]:第p位素数 
	for(int i = 0; i <= n; i++) is_prime[i] = true;
	is_prime[0] = is_prime[1] = false;
	prime[1] = 2;
	for(int i = 3; i <= n; i += 2){
		if(i % 2 != 0 && is_prime[i] != 0){
			prime[p++] = i;
			for(int j = i * i; j <= n; j += i) is_prime[j] = false;
		}
	}
	return 0;
}

优化 3 优化3 优化3
vector

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n, p = 1;//p:素数的个数 
	cin >> n;
	vector<int> prime;//prime[p]:第p位素数
	vector<bool> is_prime(n + 5, true);//标记是否为素数 
	is_prime[0] = is_prime[1] = false;
	prime.push_back(2);
	for(int i = 3; i <= n; i += 2){
		if(i % 2 != 0 && is_prime[i] != 0){
			prime.push_back(i);
			for(int j = i * i; j <= n; j += i) is_prime[j] = false;
		}
	}
	return 0;
}

优化 4 优化4 优化4
bitset
⚠ ⚠ b i t s e t 的大小得是确定的 bitset的大小得是确定的 bitset的大小得是确定的

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1000000;
int main(){
	int n, p = 1;//p:素数的个数 
	cin >> n;
	vector<int> prime;//prime[p]:第p位素数
	bitset<MAXN + 5> is_prime; //标记是否为素数
	is_prime.set();//都标记为true 
	is_prime[0] = is_prime[1] = false;
	prime.push_back(2);
	for(int i = 3; i <= n; i += 2){
		if(i % 2 != 0 && is_prime[i] != 0){
			prime.push_back(i);
			for(int j = i * i; j <= n; j += i) is_prime[j] = false;
		}
	}
	return 0;
}

方法6

分块筛选

方法7

线性筛法
Euler 筛法
欧拉筛法

参考
https://blog.csdn.net/way_back/article/details/122760006

#include <iostream>
#include <vector>
#include <fstream>
using namespace std;

int main(){
    vector<int> prime(10000, 1);
    for(int i=2; i<100; i++){
        if(prime[i]){
            for(int j=i; i*j<10000; j++)
                prime[i*j] = 0;
        }
    }
    ifstream in("prime.txt");
    for(int k; in>>k && k>1 && k<10000; )
        cout << k << " is " << (prime[k] ? "":"not ") << "a prime." << endl;
    return 0;
}
bool isPrime4(int n) {
 bool yes = false;
 int num[SIZE] = { 0 };
 for (int i = 2; i < SIZE; i++) {
  if (!num[i]) {
   for (int j = i + i; j < SIZE; j += i) {
    num[j] = 1;
   }
  }
 }
 if (!num[n]) {
  yes = true;
 }
 return yes;
}
bool isPrime5(int n) {
 bool yes = false;
 int num[SIZE] = { 0 };
 if (n == 2) {
  yes = true;
 }
 else {
  for (int i = 0; i < SIZE; i++) {
   if (!num[i]) {
    for (int j = (2 * i + 3) * (2 * i + 3); j < (2 * SIZE + 3); j += 2 * (2 * i + 3)) {
     num[(j - 3) / 2] = 1;
    }
   }
  }
 }
 if ((n - 3) % 2 == 0) {
  if (!num[(n - 3) / 2]) {
   yes = true;
  }
 }
 return yes;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值