质数
一:试除法判断一个数是否为正数
bool prime(int n)
{
if(n<2) return false;//小于2的数一定不是质数
for(int i=2;i<=n/i;i++)
if(n%i==0) return false;//如果i能整除n,则n一定不是质数
return true;
}
注意:循环判断内最好写为i<=n/i,若写为i<=sqrt(n),会降低程序效率,写为i*i<=n则会导致i过大而溢出。
二:分解质因数
根据算术基本定理,不考虑排列顺序的情况下,每个正整数都能够以唯一的方式表示成它的质因数的乘积。
n=p1^a1 * p2^a2 *p3^a3.....pn^an
void divide(int n)
{
for(int i=2;i<=n/i;i++)
{
if(n%i==0)
{
int s=0;
while(n%i==0)//让i这个质因数完全除干净
{
n/=i;
s++;
}
cout<<i<<' '<<s<<endl; //i为底数,s为指数
}
}
if(n>1) cout<<n<<' '<<1<<endl;//目前的n仍然为质因数
cout<<endl;
}
笔记:n中只含有一个大于sqrt(n)的质因子。
证明:如果有两个质因子大于sqrt(n),则sqrt(n)*sqrt(n)>n,与题设矛盾,证毕。
三:筛质数(从一到n 的质数)
原理:一个素数的n倍数一定为合数(n>1)
(1)朴素筛O(n√n):
void shai(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i]) primes[cnt++]=i;//把素数存起来
for(int j=i;j<=n;j+=i)
{ //不管是合数还是质数,都用来筛掉后面它的倍数
st[j]=true;
}
}
}
(2)线性筛法O(n):
void shai(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i]) prime[cnt++]=i;
for(int j=0;j<=n;j++)
{
st[prime[j]*i]=true;//让素数p[j]的i倍标记为合数
if(i%prime[j]==0) break;//退出循环防止与下一次重复,降低时间复杂度
}
}
}
笔记:当i%prime==0时,证明prime不是当前i的最小质因数,为了防止与下一次最小质因数筛选重复,应该在这是退出循环以降低时间复杂度。(这就是与朴素筛法不同之处)
(3)埃氏筛法O(nloglogn):
void shai(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])
{
prime[cnt++]=i;
for(int j=2*i;j<=n;j+=i) st[j]=true;//从2i开始,让素数i的倍数都标记为合数
}
}
}
注意:内层for中要从2i开始循环,若从i开始循环会导致素数i被标记为合数。