目录
1.质数
定义:质数又称素数,指在大于1的自然数中,除了1和该数自身外,无法被其他自然数整除的数(也可定义为只有1与该数本身两个正因数的数)。大于1的自然数若不是素数,则称之为合数(也称为合成数)。
算术基本定理,又称为正整数的唯一分解定理,即:每个大于1的自然数,要么本身就是质数,要么可以写为2个或以上的质数的积,而且这些质因子按大小排列之后,写法仅有一种方式。(即合数可以分解为两个或两个以上质数的乘积,此处和哥德巴赫猜想有点类似:任一大于2的偶数,都可表示成两个素数之和。)
算术基本定理的内容由两部分构成:
• 分解的存在性:
• 分解的唯一性,即若不考虑排列的顺序,正整数分解为素数乘积的方式是唯一的。
对于一个足够大的整数N,不超过N的质数大约由个,即每lnN个数中大约有1个质数。
先验知识就介绍到这里,接下来介绍质数的判定和筛法。
1.1质数的判定
1.1.1试除法
试除法就是扫描2~之间的所有整数,依次检查它们是否能整除N,若都不能整除,则N是质数,否则N是合数。试除法的时间复杂度是。需要特判0和1这两个整数,它们既不是质数,也不是合数。
模板如下:
bool is_prime(int n)
{
if(n<2) return false;
for(int i = 2; i <= sqrt(n); i++)
if( n % i == 0) return false;
return true;
}
1.2 质数的筛选
给定一个整数N,求出1~N之间的所有质数,称之为质数的筛选问题。
1.2.1埃拉托斯尼筛法
(第一次看见这个埃拉托斯尼这个名字是在初一的一本课外读物上,书上介绍他是古希腊的全才,他是第一个测量地球周长的人,那时觉得2200年前的人就能测出地球的周长真的是非常了不起,当然现在也还是觉得非常了不起。)
埃拉托斯尼筛法基于这样的想法:任意整数x的倍数2x,3x,...都不是质数。
另外,2和3都会把6标记为合数,事实上,小于的数在扫描更小的数时就已经被标记过了(因为任意一个合数N,其必存在一个质因子小于)。因此,对于每个数x,我们只需从开始,把标记N为合数即可。
时间复杂度约为
模板如下:
void primes(int n)
{
memset(v,0,sizeof(v)); //合数标记
for(int i = 2; i <= n; i++)
{
if(v[i]) continue;
cout << i << endl; //i是质数
for(int j = i; j <= n/i; j++) v[i*j] = 1;
}
}
1.2.2线性筛法
即使在优化后(从开始),埃拉托斯尼筛法仍然会重复标记合数。例如12会被2和3标记。其根本原因是我们没有确定出唯一的产生12的方式。
线性筛法通过“从小到大累积质因子”的方式标记每个合数。
设数组v来记录每个数的最小质因子,按照以下步骤来维护v:
1.依次考虑2~N之间的每一个数i
2.若v[i] = i,说明i是质数,添加到质数表中
3.扫描不大于v[i]的每个质数p,令v[i*p] = p. 也就是在i的基础上累积一个质因子p。因为p<=v[i],所有p是合数i*p的最小质因子。
每个合数i*p只会被它的最小质因子p筛选一次,时间复杂度为O(n).
模板:
const int MAX_N = 200010;
int v[MAX_N], prime[MAX_N];
void primes(int n)
{
memset(v,0,sizeof(v)); //存放每个数的最小质因子
int m = 0; //质数数量
for(int i=2;i<=n;i++)
{
if(v[i]==0) { v[i] = i; prime[++m] = i; }
//给当前的数i乘上一个质因子
for(int j=1;j<=m;j++)
{
//i有比prime[j]更小的质因子,或者超出n的范围,停止循环
if(prime[i]>v[i] || prime[j]>n/i) break;
v[i*prime[j]] = prime[j];
}
}
for(int i=1;i<=m;i++) cout<<prime[i]<<endl;
}
1.3 质因数分解
质因数分解就是前面提到的算术基本定理。
模板:
const int MAX_N = 200010;
int prime[MAX_N],c[MAX_N];
void divide(int n)
{
int m = 0;
for(int i=2;i*i<=n;i++)
{
if(n%i==0) //i为质数
{
prime[++m] = i, c[m] = 0;
while(n%i==0) n /= i, c[m]++; //除掉所有的i并计数
}
}
//n为质数
if(n>1) prime[++m] = n, c[m] = 1;
for(int i=1;i<=m;i++) cout<<prime[i]<<'^'<<c[i]<<endl;
}
2.约数
若整数n除以整数d的余数为0,即d能整除n,则称d是n的约数,n是d的倍数。
2.1求N的正约数集合
2.1.1 试除法
扫描1~,尝试d能否整除N,若能整除,则N/d也是N的约数。
int factor[1600],m=0;
void fac(int n)
{
for(int i=1;i*i<=n;i++)
{
if(n%i==0)
{
factor[++m]=i;
if(i!=n/i) factor[++m] = n/i;
}
}
for(int i=1;i<=m;i++) cout<<factor[i]<<endl;
}
推论:一个整数N的约数个数上界为2
2.2.2 倍数法
求出1~N的每个数的正约数集合
const int M = 500010;
vector<int> factor[M];
void div(int n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n/i;j++)
factor[i*j].push_back(i);
for(int i=1;i<=n;i++)
{
for(int j=0;j<factor[i].size();j++)
cout<<factor[i][j]<<' ';
cout<<endl;
}
}
3.最大公约数和最小公倍数
定理:最大公约数gcd(a,b)和最小公倍数关系:
3.1 九章算术--更相减损术
, 有
对于a,b的任意公约数d,有d|a,d|b,所以d|(a-b),即d也是b,a-b的公约数,反之亦成立。
有
3.2欧几里得算法
模板:
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}
由于高精度除法(取模)不容易实现,需要做高精度运算时,可考虑用更相减损术代替欧几里得算法。
4.互质与欧拉函数
4.1互质
定义:若a,b的最大公约数为1,则称a,b互质
4.2欧拉函数
1~N中与N互质的数的个数被称为欧拉函数,记为
4.2.1证明
设p是N的质因子,1~N中p的倍数有p,2p,3p,...,(N/p)*p,共N/p个。
同理,若q也是N的质因子,则1~N中q的倍数有N/q个。如果把这N/p+N/q个数去掉,那么p*q的倍数被排除了两次,需要加回来一次。因此,1~N中不与N含有共同质因子p或q的数的个数为:
类似地,可以在N的全部质因子上使用该方法,即可得到欧拉函数。
根据欧拉函数的计算式,我们只需分解质因数,即可求出欧拉函数。
int phi(int n)
{
int ans = n;
for(int i=2;i*i<=n;i++)
{
if(n%i==0)
{
ans = ans/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) ans = ans/n*(n-1);
return ans;
}
4.2.2性质
1.任意n>1, 1~n 中与 n 互质的数的和为 .
证明:因为gcd(n,x)=gcd(n,n-x),所以与n不互质的数x,n-x成对出现,平均值为n/2,因此,与n互质的数的平均值也是n/2.
2.若a,b互质,则
积性函数:如果a,b互质,f(ab)=f(a)*f(b),那么称 f 为积性函数。
3.若f是积性函数,且在算术基本定理中,则
4.设p为质数,若p|n且|n ,则
证明:若p|n且|n,则n,n/p包含相同的质因子,只是p的指数不同。直接把与按照欧拉公式的计算公式写出,二者相除商为p,所以性质4成立。
5.设p为质数,若p|n但不是n的约数,则
证明:推出p,n/p互质,有欧拉函数是积性函数得,而(p为质数),所以性质5成立。
6.
4.2.3线性筛法求欧拉函数
利用性质4和性质5
const int MAX_N = 200010;
int v[MAX_N], prime[MAX_N],phi[MAX_N];
void euler(int n)
{
memset(v,0,sizeof(v)); //存放每个数的最小质因子
int m = 0; //质数数量
for(int i=2;i<=n;i++)
{
if(v[i]==0) { v[i] = i; prime[++m] = i; phi[i] = i-1; }
//给当前的数i乘上一个质因子
for(int j=1;j<=m;j++)
{
//i有比prime[j]更小的质因子,或者超出n的范围,停止循环
if(prime[i]>v[i] || prime[j]>n/i) break;
v[i*prime[j]] = prime[j];
phi[i*prime[j]] = phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
}
}
}
4.12日更新
5.同余
定义:若整数a和整数b除以正整数