一、素数定义
素数又称为质数,是指大于1的自然数中除了1和它本身没有其他因子的数字。
二、素数的朴素判断方法
对于一个数字N来说,判断他是否是素数,朴素的算法则需要枚举他的所有可能的因子,判断是否能够整除,如果能够整除则不是素数。
bool isprime(int n)
{
bool flag = true;
for(int i = 2;i < n;i++)
if(n % i)
{
flag = false;
break;
}
return flag;
}
我们注意到,一个数字的所有因子都是都是成对存在的,一个因子分布在区间 [ 1 , sqrt(n) ]另一个分布在[ sqrt(n) , n],所以只需要枚举到根号n就可以判断一个数字是不是素数.
bool isprime(int n)
{
bool flag = true;
for(int i = 2;i <= sqrt(n) + 1;i++)//一般会枚举多一个,防止因为精度问题造成差错
if(n % i == 0)
{
flag = false;
break;
}
return flag;
}
三、素数筛
考虑这样一个问题,找出区间[1 , n]内的所有素数。
根据我们之前的算法,复杂度为O(n * sqrt(n)),对于n比较小的时候(n < 1e5),可以在1s之内完成,
但是当n比较大的时候,时间复杂度就已经爆炸了,于是素数筛横空出世。
素数筛的基本思想是用素数筛去非素数,也就是如果一个数字不是素数,那么他一定可以是某个素数的整数倍
1、Eratosthenes(埃氏筛法)
bool vis[MAXN];//
int prime[MAXN];//素数表
void prime_table(int n)
{
int cnt = 0;
memset(vis,false,sizeof(false));
for(int i = 2;i <= sqrt(n);i++)
if(!vis[i])
{
//对于一个素数来说,第一个没被筛掉的他的倍数是 i * i
//(比 i * i小的数字i * k已被之前的素数作为倍数筛掉)
for(int j = i * i;j <= n;j += i)//j 有可能会超出int的范围
vis[j] = true;
prime[++cnt] = i;
}
}
2、素数筛
素数筛的方法是对于一个新的数字来说,用已经打好的素数表去判断整除。
每一个素数都只被记录一次,
每一个合数都只会被一个唯一的最小素因子标记一次(例如6只被2标记了,没有被3标记)。
因此是O(n)线性复杂度的。
bool notprime[MAXN];//标记合数
int prime[MAXN],pn,n;//素数表
void init()
{
pn = 0;//初始化素数表为空
notprime[1] = true;
for(int i = 2;i < n;i++)
{
if(notprime[i] == false)//找到第一个素数
prime[pn++] = i;
for(int j = 0;j < pn && prime[j] * i < n;j++)
{
notprime[prime[j] * i] = true;
if(i % prime[j] == 0)//如果是i的约数,则跳出循环,保证个数字都只被它的最小素因子筛去
break;
}
}
}
四、后续
对于一个比较大的数字来说,判断它是否是素数,一般采用随机素数测试的方法。
以后有时间再写相关的。(感觉给自己挖了很大的坑)