GCD&素数打表&快速判断质数

时间:2022-04-19 05:18:41

GCD(求两数的最大公约数)

#include <cstdio>
__int64 GCD(__int64 a,__int64 b)//GCD
{
if (a % b == 0)
return b;
else
return GCD(b,a%b);
}
int main()
{
__int64 a,b;
__int64 ans;
while (~scanf ("%I64d %I64d",&a,&b))
{
ans=GCD(a,b);
printf ("%I64d\n",ans);
}
return 0;
}


素数打表


int su[1000005]={1,1};
for(int i=2;i<=1000000;i++)
{
if(su[i]==1)
continue;
for(int j=i*2;j<=1000000;j+=i)
su[j]=1;
}


快速判断质数

下面的转自[菜鸟专供|高手勿入] 快速判断质数


1、任何一个合数(都指正整数,下同),都是几个质数的乘积。例如:18 = 2 * 3 * 3


2、如果一个数N不能整除M,则N也不能整除M*i,i=1, 2, 3....。即,N%M != 0,则 N%(M*i)  != 0。例如,N不能整除2,那么N也不能整除6。因为,如果N能整除6,即,N%6 = 0,也就是 N%(2*3) = 0,也就是N能整除2,与前面矛盾。


3、如果N不是质数,那么N有满足1<d<=sqrt(N)(N的平方根)的一个质数因子 d 存在。因为如果N不是质数,根据条例1,N肯定可以分解为一堆质数的和,即肯定存在一个质数因子。如果找不到,那就说明N是质数,只能被1和自己本身整除。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


A、依照判断素数的概念,我们可以从2~N-1,依次去除,看余数是否为0,复杂度为O(N)


B、改进一下,先判断 N%2 是否为0,如果不为0,即 N%(2*i) != 0(前面的条例2),所以只要再判断 3~N-1 之间的奇数即可,复杂度减半!为O(N/2)

同理,可以再判断 N%3 是否为0,如果不为0,则可以去掉后面的 3*i


C、其实,并不需要一直整除到N-1,而只要整除到 sqrt(N) 即可。如果 a < sqrt(N), b<sqrt(N),那么 a * b < sqrt(N)*sqrt(N) = N。如果b = N/a,那么b肯定是>=sqrt(N)。所以,只要判断小于等于sqrt(N)的数即可,再判断大于sqrt(N)的数就是在浪费时间了。


D、结合前面的 B 和 C,我们就可以先判断 N%2 是否为0,然后再判断 3~sqrt(N) 之间的奇数即可,复杂度为O(sqrt(N)/2)


E、根据条例3,如果N是质数,则N只能被1和N整除,如果N不是质数,那一定存在1<d<=sqrt(N)的一个质数因子。那么我们就可以取 2~sqrt(N) 之间的所有质数d,依次判断N%d 是否为0。如果判断均不等于0,就说明不存在这样的一个质数,也就是说N只能被1和本身整除,即N是质数。


注意: 如果没有特殊说明, 以下讨论的都是针对n为素数时的时间复杂度

1. 根据概念判断:

如果一个正整数只有两个因子, 1和p,则称p为素数.

bool isPrime(int n)
{
if(n < 2) return false;

for(int i = 2; i < n; ++i)
if(n%i == 0) return false;

return true;
}

时间复杂度O(n).


2. 改进, 去掉偶数的判断

bool isPrime(int n)
{
if(n < 2) return false;
if(n == 2) return true;

for(int i = 3; i < n; i += 2)
if(n%i == 0) return false;

return true;
}

时间复杂度O(n/2), 速度提高一倍.


3. 进一步减少判断的范围

定理: 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
证明: 如果n不是素数, 则由定义n有一个因子d满足1<d<n.
如果d大于sqrt(n), 则n/d是满足1<n/d<=sqrt(n)的一个因子.

bool isPrime(int n)
{
if(n < 2) return false;
if(n == 2) return true;

for(int i = 3; i*i <= n; i += 2)
if(n%i == 0) return false;

return true;
}

时间复杂度O(sqrt(n)/2), 速度提高O((n-sqrt(n))/2).


4. 剔除因子中的重复判断.
例如: 11%3 != 0 可以确定 11%(3*i) != 0.

定理: 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个"素数"因子d.
证明: I1. 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
I2. 如果d是素数, 则定理得证, 算法终止.
I3. 令n=d, 并转到步骤I1.

由于不可能无限分解n的因子, 因此上述证明的算法最终会停止.

// primes[i]是递增的素数序列: 2, 3, 5, 7, ...
// 更准确地说primes[i]序列包含1->sqrt(n)范围内的所有素数

bool isPrime(int primes[], int n)
{
if(n < 2) return false;

for(int i = 0; primes[i]*primes[i] <= n; ++i)
if(n%primes[i] == 0) return false;

return true;
}

假设n范围内的素数个数为PI(n), 则时间复杂度O(PI(sqrt(n))).

函数PI(x)满足素数定理: ln(x)-3/2 < x/PI(x) < ln(x)-1/2, 当x >= 67时.

因此O(PI(sqrt(n)))可以表示为O(sqrt(x)/(ln(sqrt(x))-3/2)),

O(sqrt(x)/(ln(sqrt(x))-3/2))也是这个算法的空间复杂度.


5. 构造素数序列primes[i]: 2, 3, 5, 7, ...

由4的算法我们知道, 在素数序列已经被构造的情况下, 判断n是否为素数效率很高;

但是, 在构造素数序列本身的时候, 是否也可是达到最好的效率呢?

事实上这是可以的! -- 我们在构造的时候完全可以利用已经被构造的素数序列!

假设我们已经我素数序列: p1, p2, .. pn

现在要判断pn+1是否是素数, 则需要(1, sqrt(pn+1)]范围内的所有素数序列,

而这个素数序列显然已经作为p1, p2, .. pn的一个子集被包含了!

// 构造素数序列primes[]

void makePrimes(int primes[], int num)
{
int i, j, cnt;

primes[0] = 2;
primes[1] = 3;

for(i = 5, cnt = 2; cnt < num; i += 2)
{
int flag = true;
for(j = 1; primes[j]*primes[j] <= i; ++j)
{
if(i%primes[j] == 0)
{
flag = false; break;
}
}
if(flag) primes[cnt++] = i;
}
}

makePrimes的时间复杂度比较复杂, 而且它只有在初始化的时候才被调用一次.在一定的应用范围内, 我们可以把近似认为makePrimes需要常数时间.在后面的讨论中, 我们将探讨一种对计算机而言更好的makePrimes方法.

下面的不敢兴趣可以不看