那天的ppt讲的不是很清楚 下来后好多同学都说没听懂(。。。。。)
我再补充一下
首先 关于原理(ppt最后一行多了一个9,小细节 不用管)
ppt上讲的很清楚了 这个原理是相对简单的 很好理解
如果原理你都理解了 那么你直接去后边看用法
如果 你还没有理解 那么 我再举个例子
最朴素的判断素数方法 是这样的 对吧
那么 现在我让你判断1-20 之内的素数 你会怎么做
从1 遍历到20 挨个判断一下是不是素数 对吧
那么我们来分析一下复杂度 如果不理解什么是复杂度 你就认为我们算一下循环次数好了
从1-20 需要遍历20次 对于每个数 极端情况下 需要sqrt(n)次才完成能判断
这一共是多少次呢 sqrt(2)+sqrt(3)+sqrt(4)+sqrt(5)+.........sqrt(20) 次 对吗
通过计算器 计算得到 答案是60 也就是需要60次
那么我们再来看下 我们使用埃氏筛需要几次
首先 外面的循环 毋庸置疑 从2-20遍历 需要20次(其实是19 我们忽略 进为20)
我们来看里面的操作
从2开始 2是素数 那么4不是 6不是 8也不是 10、12、14、16、18、20 都不是 这是9次操作
然后3开始 6 9 12 15 18 都不是素数 这是5次操作
然后4 跳过 5 跳过 6 跳过
从7开始 14 不是素数 这是1次操作
后边的 8 9 10 11 12 14 15 16 18 20 都会跳过
而11 13 17 19 都不会操作 因为他们的倍数 大于20了
那么一共进行了20+9+5+1=35次操作 比那个60少了将近一半
其实因为我举的例子很小 只到20 所以你看到的才快了这么点
你可以想一下 如果数字变得很大的时候 比如一千万 你用普通的筛法 是需要更多次数的
所以 这个埃氏筛法快的其实不止这么一点--(至于究竟快多少 先不管了 你只要知道很快就好了)
下面说下 怎么用它
明白了 原理 用起来就很灵活了
比如
问题一 求1-20之间的素数个数
int main(){
int cnt=0;//用来计数
bool isprime[21];
for(int i=0;i<=20;i++)isprime[i]=true;//先全部置为真
isprime[0]=isprime[1]=false;//1 0 不是素数
for(int i=2;i<=20;i++){//从2开始往后筛
if(isprime[i]){
for(int j=2*i;j<=20;j+=i){
isprime[j]=false;
}
}
if(isprime[i]){
cnt++;//如果是素数 就计数
}
}
printf("%d",cnt);
}
问题二
就是ppt上的题目
#include<stdio.h>
#include<algorithm>
#include<math.h>
const int maxn=1e6+7;//总的范围规定在这里
using namespace std;
//我们将这个埃氏筛法写成一个函数
bool isprime[maxn];
void sieve(){
for(int i=0;i<=maxn;i++)isprime[i]=true;
isprime[0]=isprime[1]=false;
for(int i=2;i<=maxn;i++){//从2开始往后筛
if(isprime[i]){
for(int j=2*i;j<=maxn;j+=i){
isprime[j]=false;
}
}
}
}
int l,r;
int main(){
//我们在程序刚开始 先调用这个函数
//把这个isprime数组处理成我们想要的样子 用来判断素数
//这就是预处理的思想 我们在开头处理这一次
//把isprime数组 里面 下标是素数的全部变成了true
//后边想判断是不是素数 直接用isprime[i]是不是真就好了
sieve();
int cnt=0;//计数
scanf("%d%d",&l&r);//输入 l和r
for(int i=l;i<=r;i++){//遍历 l到r 判断就行了
if(isprime[i]){
cnt++;
}
}
printf("%d",cnt);
}
问题三 输入一个数n 判断他是不是素数(多组测试数据)
n的范围是 (n<1e6)
这个题很有意思 题上说有多组测试数据(没说几组) 每次给你一个n
那么其实意思就是说 有很多个n让你判断 他们是不是素数
而且n最大是1e6
那么你们想一下
如果我第一个数给你1000000 你用普通的判断素数的方法 需要sqrt(n)次才能判断出来
也就是3000次才能判断出来是不是素数 对吧
3000次也不多 那如果我输入一共一万组呢 就需要3000*10000次了吧
所以 这个时候 就体现了 预处理的重要性
我们先预处理出来 1e6以内的所有素数 这样不管你输入啥 我直接去看 是不是素数就好了
对吧
那么 预处理 有些同学也想到了 但是他用最普通的筛法去做预处理
那么你想一下 1e6每个你都需要去判断一下是不是素数 我刚才在上边算了 20以内的素数需要60次才能判断出来
那1e6以内 需要多少次呢 我告诉你 大概需要1e9次(也就是十亿次)
而用埃氏筛法需要几次呢 可能你们现在还不会算 我直接告诉你们 需要70万次
十亿 和 七十万 差别不用我说了吧 这就是这个算法的效率 也是算法的魅力所在。
#include<stdio.h>
#include<algorithm>
#include<math.h>
const int maxn=1e6+7;//总的范围规定在这里
using namespace std;
//我们将这个埃氏筛法写成一个函数
bool isprime[maxn];
void sieve(){
for(int i=0;i<=maxn;i++)isprime[i]=true;
isprime[0]=isprime[1]=false;
for(int i=2;i<=maxn;i++){//从2开始往后筛
if(isprime[i]){
for(int j=2*i;j<=maxn;j+=i){
isprime[j]=false;
}
}
}
}
int n;
int main(){
sieve();//预处理
//输入 n
while(scanf("%d",&n)!=EOF){
if(isprime[n]){
printf("Yes\n");
}
else{
printf("No\n");
}
}
}
米勒-拉宾素性检验(MillerRabbin)算法
如果对素数筛查的算法感兴趣,可以查看我的另一篇博文,更加高效的算法,复杂度可以达到log级别