简单数论

判 断 素 数 判断素数

一般来说判断素数我们会使用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。

G C D GCD GCD

gcd 没什么可说的,c语言都自带了gcd的函数调用方法是__gcd(a,b)。

//手写递归,也可一行写完,并预编译。
int gcd(int a,int b) {
	if (b == 0)
		return a;
	return gcd(b,a%b);
}

扩 展 G C D 扩展GCD GCD

扩展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;
	}
}

快 速 幂 快速幂

也没什么好讲的,倍增的思想,记录下代码和位运算。
常见二进制运算

  • a&1 a的二进制个位,奇偶性
  • a>>1 去掉二进制个位,a/2
  • a<<1 a*2
  • -x x各位取反后+1(补码特性)
  • x&(-x) lowbit(x)

/**快速幂**/
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);
}

素 数 筛 素数筛

  • 埃氏素数筛 O( n × log ⁡ ( log ⁡ n ) n \times \log(\log n) n×log(logn))
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;

}

你可能感兴趣的:(模板,数学学习)