素数的定义:一个正整数只能被1和自己整除。
关键字:正整数、只有2个因子。
----------------------------------------------------------------------------------------------------------------------------------------------------------------
知识点罗列:
入门题:判断一个数字是否是素数:
当判断一个数字是素数与否的时候,用适量的数学思维,可以大大减少枚举的次数。以下三个方法是层次递进地减少枚举的量。
1、暴力枚举:O(n)
2、折半枚举:O(n/2)
3、开方枚举:O(√n)
简单题:判断一堆数字是否是素数:
当判断的量上升,也就是同一个操作要执行多次,则需要再用数学思维进行优化,可以枚举的次数再降为。
4、开方枚举(函数版):O(n*√n)
5、线性筛选:O(n)
----------------------------------------------------------------------------------------------------------------------------------------------------------------
详解:
1、暴力枚举O(n):从2->(x-1)枚举判断,是否有因子。
//判断素数1:暴力枚举
//时间复杂度:O(n)
#include<cstdio>
int main()
{
int x;
scanf("%d",&x);
if(x>1)//素数一定大于 1(性质)
{
for(int i=2;i<x;i++)
{
if(x%i==0)
{
printf("0");//有因子,非素数
return 0;
}
}
}
printf("1");
return 0;
}
2、折半枚举O(n/2):
思维的优化:
例如:一个数字是48,能被2整除,就是2*24=48。
所以:只要找到2,就会找到对应的24;
只要枚举前面一半的数字,就等于同时枚举了另外一半。
//判断素数2:简单优化砍一半
//时间复杂度:O(n/2)
#include<cstdio>
int main()
{
int x,y;
scanf("%d",&x);
y=x/2;//y是x的一半
if(x>1)
{
for(int i=2;i<y;i++)//枚举的次数只有原来的一半
{
if(x%i==0)
{
printf("0");//有因子,非素数
return 0;
}
}
}
printf("1");
return 0;
}
3、开方枚举:O(√n)
思维的再优化:
例如:一个数字是48,能被2整除,就是2*24=48。
如果将48的因子分成2堆:2属于左边堆,24属于右边堆。那么这两堆因子的交汇点会在哪里呢?
答案是:开根号的地方。
所以:只要枚举到这个数字的开根号,就等于同时枚举了另外一半的因子。
//判断素数3:继续优化:降维
//时间复杂度:O(√n)
#include<cstdio>
#include<cmath>//sqrt开根函数要用
int main()
{
int x,y;
scanf("%d",&x);
y=sqrt(x+1);//y表示x的开根因子的取整,+1是保险
if(x>1)
{
for(int i=2;i<=y;i++)//枚举的次数只有原来的开根号次
{
if(x%i==0)
{
printf("0");//有因子,非素数
return 0;
}
}
}
printf("1");
return 0;
}
题目升级:求1-n的所有素数
4、开方枚举(函数版):O(n*√n)
//求1-n的素数:函数版
//时间复杂度:O(n√n)
#include<cstdio>
#include<cmath>//sqrt开根函数要用
int pd(int x)//当前判断的数字是x
{
if(x<2) return 0;//小于2,非素数
int t=sqrt(x+1);
for(int i=2;i<=t;i++)
{
if(x%i==0) return 0;//有因子的话,非素数
}
return 1;//是素数
}
int main()
{
int n;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
if(pd(i)==1)//如果函数返回值是1,就是素数
{
printf("%d ",i);
}
}
return 0;
}
5、线性筛选:O(n)
当n的值很大的时候,对于每个n枚举因子,还是很大的运算量。
所以需要用到一个打表的思维,用数组f[i]来表示i是否素数:
1、设全部的数字都是素数;
2、小的奇数应该是素数;
3、素数的倍数一定不是素数,剔除;
//求1-n的素数:线性筛选
//时间复杂度:O(n)
#include<cstdio>
#include<cstring>//memset函数用
int f[100005];//10W以内的素数表
int main()
{
int n=100000;
memset(f,0,sizeof(f));//将f数组初始化为0:素数
f[1]=1;//1不是素数
for(int i=2;i<=n;i++)
{
if(f[i]==0)//i是素数,
{
for(int j=i*2;j<=n;j+=i)
{
f[j]=1;//i的倍数都不是素数
}
}
}
//以上是打表部分
scanf("%d",&n);
for(int i=1;i<=n;i++)//直接查表,完美!
{
if(f[i]==0) printf("%d ",i);
}
return 0;
}
到这里,素数的入门介绍就差不多了。如果有兴趣了解更多数论知识的同学,
请移步到 NOIP学习大纲整理 里面有2个章节,从入门开始讲数论。加油o~