一般来说判断素数我们会使用O( n \sqrt n n)的判断方法,就像下面一样
if (n <= 3) {
return n > 1;
}
int Sqrt = (int)sqrt(n);
for (int i = 2; i <= Sqrt; i++) {
if(n % i == 0) {
return false;
}
}
return true;
这样的方法足够我们进行数据较小时的使用,但当数据过大,就不实用了。
下面介绍一种O( n 3 \frac{\sqrt n}{3} 3n)的算法
bool isPrime(int x) {
if (x == 2||x == 3) return true;
float nSqrt = floor(sqrt((float)x));
if (x%6!=1 && x%6!=5) return false;
for (int i = 5; i <= nSqrt; i+=6) {
if (x%(i)==0 || x%(i+2)==0) return false;
}
return true;
}
做法的原因是这样的。任何一个数可以看做是6x,6x+1,6x+2,6x+3,6x+4,6x+5其中的一个。但这6个数中,6x不是质数,6x+2不是质数,6x+3不是质数,6x+4不是质数。只有6x+1和6x+5这两个数可能是质数,所以我们只需要判断这两个数是否为质数即可。其中6x+5等于6x-1。
gcd 没什么可说的,c语言都自带了gcd的函数调用方法是__gcd(a,b)。
//手写递归,也可一行写完,并预编译。
int gcd(int a,int b) {
if (b == 0)
return a;
return gcd(b,a%b);
}
扩展gcd讲的是这么一回事
Ax + By = C
Ax + By = gcd(A,B)
有这么两个式子,求x,y的整数解。
C ≡ 0 (mod gcd(A,B))----①
找到一组 x,y 使得①式成立
那个推到过程我忘了,找到了再粘上去。重点是推导过程,知道过程就会写代码了。
int x,y; //(x,y定义为全局变量)
inline void exgcd(int a,int b) {
if (b) {
exgcd(b,a%b);
int k = x;
x = y;
y = k-a/b*y;
}
else y = (x=1)-1;
}
或者
int exgcd(int a,int b) {
if (b == 0) {
y = (x=1)-1;
return a;
}
else {
int gcd = exgcd(b,a%b);
int xx = x;
int yy = y;
x = yy;
y = xx-a/b*yy;
return gcd;
}
}
也没什么好讲的,倍增的思想,记录下代码和位运算。
常见二进制运算
/**快速幂**/
long long Quickpow(long long a,long long x,long long mod) {
long long ans(1);
while (x) {
if (x&1) ans = (ans*a)%mod;
a = (a*a)%mod;
x >>=1;
}
return ans;
}
补充上快速乘法
#define ll long long
ll qx(ll a,ll b,ll p)//a*b
{
ll res=0;
while(b)
{
if(b&1) res=(res+a)%p;
b>>=1;
a=(a+a)%p;
}
return res%p;
}
ll qmi(ll a,ll b,ll p)//a^b
{
ll res=1%p;
while(b)
{
if(b&1) res=qx(res,a,p)%p;
b>>=1;
a=qx(a,a,p);
}
return res%p;
}
这里的逆元是在模运算下的。
//p是素数,模运算模p的意义下的逆元。
int inv(int x,int p) {
return Quickpow(x,p-2);
}
int prime[MAX];
bool is_Prime[MAX];
int sieve(int n) {
memset(is_Prime,true,sizeof(is_Prime));
int cnt = 0;
is_Prime[0] = is_Prime[1] = false;
for (int i = 2; i <= n; i++) {
if (is_Prime[i]) {
prime[cnt++] = i;
for (int j = 2*i; j <= n; j+=i)
is_Prime[j] = false;
}
}
return cnt; //返回1-n区间的素数个数
}
欧几里得筛 O(n)
#include
#define maxn 1000010
#define ll long long
using namespace std;
int prime[maxn];
int factor[maxn];//用来记录最小素因子
int sieve(int n)
{
int p=0;
for(int i=2;i<=n;i++)
{
if(!factor[i])//如果没有找到素因子,当然就是素数啦
{
prime[p++]=i;
factor[i]=i;
}
for(int j=0;j<p&&prime[j]*i<=n;j++)
{
factor[prime[j]*i]=prime[j];
if(!(i%prime[j]))//筛的数已经被前面的数筛过了
break;
}
}
return p;
}
int main()
{
int n;
cin>>n;
cout<<sieve(n)<<endl;
}