1 素数
1.1 素数的判断
素数的判断较为简单,直接上代码,需要注意的几点:
bool isPrime(int n){
if(n==1) return false; //1需要特判
int sqr=(int)sqrt(n*1.0); //之所以要用这条语句而不是在for循环里使用sqrt(n),解释如下
for(int i=2;i<=sqr;i++){
if(n%i==0) return false;
}
return true;
}
在上面的代码中,其中需要用到一条 int sqr=(int)sqrt(n*1.0); 用来在循环中做一个循环边界呢。首先可以从 double sqrt(double) 可以看出,sqrt()的返回值和参数都是double类型的。在计算机中,小数都是不精确的,如,sqrt(9)是等于3.000000000001还是2.999999999999都是不好讲的。有的计算机可能给出前者,有的则可能是后者,这样对我们循环的边界的判断造成了影响。于是就用到了上面一行代码,转化参数的数据类型,保证sqrt得到我们想要的值。
1.2 素数表的获取
下面给出两个方法。
首先是比较常用的,对n不超过是可以的,复杂度包括了判断是否为素数O()和遍历从1到n的复杂度O(n),因此第一种方式(对n不超过)的时间复杂度为O(n) 。下面给出代码:
const int maxn=101;
int Prime[maxn],pNum=0; //数组Prime()存放所有的素数,pNum为素数的个数
bool p[maxn]={0};
void Find_Prime(){
for(int i=1;i<maxn;i++){
if(isPrime(i)==true){ //判断是否为素数
Prime[pNum++]=i;
p[i]=true;
}
}
}
其次是时间复杂度更小的一种方式,为O(),这是一种什么原理的,我们来看:
- 2是素数,因此去掉所有2的倍数,如4,6,8,10,12,14,16,18,20;
- 3没有被前面的步骤筛去,所以3是素数,因此去掉所有3的倍数,如6,9,12,15,18;
- 4被前面的步骤筛去了,所以4不是素数;
- 5没有被前面的步骤筛去,所以5是素数,因此去掉所有5的倍数,如5,10,15,20;
- 6被前面的步骤筛去了,所以6不是素数;
- ……
由上面可以看出,当从小到大达到达到某数n的时候,如果n没有被前面的步骤筛去,那么n就一定是素数。原理是:当n不是素数时,必然有小于n的质因数,这样在之前筛去质因数的倍数的时候就应该把n筛去了。所以,当n没有被前面步骤筛去的时候,就一定是一个素数。于是可以给出如下代码:
const int maxn=101;
int Prime[maxn],pNum; //同样的道理,建立一个数组Prime来存放素数,pNum为苏数的个数
bool p[maxn]={0}; //素数为0,非素数为1
void Find_Prime(int n){
for(int i=2;i<max;i++){
if(p[i]==0){
Prime[pNum++]=i;
for(int j=i;j<maxn;j += i){
p[j]=true;
}
}
}
}
2 质因数的分解
质因数分解就是将一个整数n分解成若干个素数乘积的形式,如8=222,18=2233等等。素数的寻找上面已经讲过了,而且值得注意的是2357111317192329的积就已经超过了int型的上限了。
我们对于质因数的分解,不妨先设一个结构体:
struct factor{
int x,cnt; //x代表了质因数,cnt代表了该质因数的个数
}fac[10];
那么对于180来说,就有
fac[0].x=2;
fac[0].cnt=2;
fac[1].x=3;
fac[1].cnt=2;
fac[2].x=5;
fac[2].cnt=1;
首先枚举1~sqrt(n)范围内所有质因子p,判断p是否为n的质因数。
const num=0;
void Find_Factor(int n){
for(int i=0;i<10;i++){
if(n%Prime[i]==0){
fac[num].x=Prime[i];
fac[num].cnt=0;
while(n%fac[num].x==0){
fac[num].cnt++;
n=n/fac[num].x;
}
num++;
}
}
}
如果在上边步骤结束后n人大于1,说明n有且仅有一个质因子(即n本身),这时需要将这个质因子加入fac数组,令其个数为1.
if(n!=1){
fac[num].x=n;
fac[num++].cnt=1;
}
解释完毕