初见安~本章讲一点儿数学知识最近讲的东西好像越来越基础了……
这里是基础数论专题(1)
1.质数
质数——若一个正整数除了它自己和1没有别的因数了,则称这个数为质数(or素数),否则合数。
比如:3=1*3,3就是一个质数;8=1*8=2*4,8就是个合数。
在c++里我们要判断质数or筛选质数,可以运用到以下几种方法:
(1) 试除法
若一个正整数N为合数,则在2~N-1里必定可以找到N的因数M。而我们可以知道:N的合数成双出现,不成双则为√N,如9=3*3(这里算3为一个合数),所以N若为合数,则在2~√N里一定可以找到一个N的因数。因此我们可以直接用for循环除以N来验证N是否为质数。
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int n;
cin>>n;
for(int i=2;i<=sqrt(n);i++)
{
if(n%i==0)//取余
{
cout<<"No"<<endl;
return 0;//找到了一个因数,则不必继续了
}
}
cout<<"Yes"<<endl;
return 0;
}
(2)Eratosthenes算法
看起来是个很高大上的名字(我都不会读),其实也是很简单的——但这一算法比较适用于要打出2~N之内的质数or统计有多少个,也就是全局性的问题。同样基于我们对质数的定义——一个正整数,除了1和它本身没有别的因数。所以有别的因数的都不是质数。
例——输出1~N以内所有的质数。
#include<iostream>
using namespace std;
int n;
bool v[110];
int main()
{
cin>>n;
for(int i=2;i<=n;i++)//2~n
{
if(v[i]) continue;//已经标记为合数了,继续循环不做操作
cout<<i<<" ";
for(int j=i;j<=n/i;j++)
{
v[i*j]=1;//找可以被i整除的n以内的数并标记
}
}
return 0;
}
这一算法的时间复杂度为O(NloglogN),效率是十分高的,也十分常用。
(3)线性筛法
Eratosthenes算法就算优化也会重复标记合数,如12会被2和3重复标记,所以我们试试线性筛法。
这一筛法运用到了一个数学结论:任何一个合数都可以被分解为几个素数的乘积。如44=2*2*11等等。
考虑到上述的重复标记问题(加一个v的判断也可以,但为了更优化还是介绍一下)
【p.s】最小质因数,顾名思义就是某合数的一个最小的为质数的因数。
#include<iostream>
using namespace std;
int n;
int v[1100];//在i下标处放i的最小质因数
int prime[1100];//存放质数
int m=0;
int main()
{
cin>>n;
for(int i=2;i<=n;i++)
{
if(v[i]==0)//不曾被标记上最小质因数,则i为质数。
{
v[i]=i;
//它本身作为一个因数,去筛将它作为最小质因数的数
prime[++m]=i;
}
for(int j=1;j<=m;j++)
{
if(prime[j]>v[i]||prime[j]>n/i) break;
//i有比prime[j]更小的质因数,也就是已被标记
v[i*prime[j]]=prime[j];//没有则继续标记
}
}
for(int i=1;i<=m;i++)
{
cout<<prime[i]<<" ";
}
return 0;
}
这一算法中每个合数只会被它的最小质因数标记一次,时间复杂度为O(N)。
(4)质因数分解
例题:输入一大于一的整数n,将n分解为几个质数的乘积。
由上文所提到的:每个合数都可以被分解为几个质数的乘积,同时任意一个大于1的正整数都能唯一分解为有限个质数的乘积。所以我们可以尝试一下将试除法和Eratosthenes算法结合起来——扫描2~√N中的每个数d,若d能整除N,则存起来,并统计需要多少个d相乘。这里d天然保证了为质数,因为每个合数都可以分解为几个质数的乘积,在到这个合数之前n就应当已经被除尽了。
#include<iostream>
#include<cmath>
using namespace std;
int n;
int p[1100];//从小到大存放有哪些因数
int c[1100];//存放第m个因数需要乘的次数
int m=0;//计数
int main()
{
cin>>n;
cout<<n;//格式而已,不用在意。毕竟操作过后n变了
for(int i=2;i<=sqrt(n);i++)
{
if(n%i==0)//找到因数
{
p[++m]=i;
c[m]=0;
while(n%i==0)//可以分解i则继续分解
{
n/=i;
c[m]++;//统计次数
}
}
}
if(n>1)//最后n还是无法分解完,则为质数
{
p[++m]=n;c[m]=1;
}
cout<<"=";//作为一个强迫症,为了格式而已……
int num=0;//为了格式+1
for(int i=1;i<=m;i++)
{
for(int j=1;j<=c[i];j++)
{
if(num==0) cout<<p[i];
else cout<<"×"<<p[i];
num++;
}
}
return 0;
}
毕竟基本上是纯数学知识,还是很好理解的~
后面的这个应用就比较多了。
2.欧拉函数
求1~n中与n互质(即最大公约数为1)的元素的个数,我们就要用到欧拉函数了。
这里我们可以用到容斥的思想,一共有n个数,有多少元素是n的约数(传送门:约数专题),那么减去就好了,剩下的就是与n互质的元素,,也就是:
其中p为n的质因数,共有t个不同的质因数。
这个式子可以化简(太愚钝了化简不来直接上结论)得到的就是欧拉函数了:
这个函数在基础数论的各大公式推导中会用到,所以还是很重要的,也有一些常用性质,比如:
1)若n本身为素数,则n以内与它互质的数的个数为n - 1(也就是除了它自己)。
2)如果n为某一素数p的a次方,则有
3)欧拉函数为积性函数,即。(2)的结论就是基于这条性质。
大约就是这些内容啦!
迎评:)
——End——