弱菜开始学数论了,不定时更新。。。
一.素数定理
素数分布:小于x的素数大约有 x/ln(x)个
推论:如果Pn为第n个素数 那个Pn约等于n*ln(n);
二.素数测试
1.sqrt(n)的朴素测试。这个就不多说了,数据范围小的时候比较方便
2.nlogn的筛法
void setprime() { memset(prime, 0, sizeof(prime)); //为了方便赋值。令prime[i]=0 表示 i是素数 for (int i=2; i<N; i++) if (!prime[i]) { for (int k=i*i; k<N; k+=i) prime[k]=1; } return; }
3.线性筛
2中筛法会重复筛掉部分合数,因此复杂度还可以优化,得到线性筛
void setprime() { for(long i = 2 ; i < N ; i ++) { if(! isNotPrime[i]) prime[num_prime ++]=i; for(long j = 0 ; j < num_prime && i * prime[j] < N ; j ++) { isNotPrime[i * prime[j]] = 1; if( !(i % prime[j] ) ) break; //关键优化 } } return ; }
此筛法的关键在于注释中的break。prime[]数组记录素数。当i可以整除prime[j]的时候break,原理见 http://blog.csdn.net/leolin_/article/details/6642126
2,3中的筛法单独使用都只能筛出较小范围的素数,范围较大,如1e9时,数组开不了,时间也是不允许的
4.改进的筛法素数测试
此方法是2.3中方法的应用和延伸,用来筛超大范围的质数。。由1中朴素判断法我们可以知道,判断素数只需要判断sqrt范围内有没有因子即可,所以在范围变大的时候,我们可以先用筛法筛出
sqrt(n)中的素数并保存在数组中。范围1e9的话只需要筛出30000多内的素数即可,再用保存下来的素数去对更大范围的数进行素数测试。例题poj2689(最大所测试数达到了2147483647)
就是这种方法的直接应用 题解链接:http://www.cnblogs.com/lnever/p/3963288.html
5.miller-robin大素数测试
理论基础,费马小定理: 若p为素数,则对于正整数a,有a^p%p==a%p;若存在a不满足上式,则p为合数
同时我们规定 若n为合数且满足 a^n %n=a%n,称n为以a为底的伪素数。
mille robin素数测试的算法原理:随机取多个a进行测试,若都满足a^p%p==a%p,则可以近似认为 p为素数。
例题 poj3641
模板:
long long random(long long n) { srand(time(0)); return (long long)(rand()%(n-1)+1); } long long multi(long long a,long long b,long long m)//a*b%m { long long res=0; while(b>0) { if(b&1) res=(res+a)%m; b>>=1; a=(a<<1)%m; } return res; } long long quickmod(long long a,long long b,long long m) //a^b%m { long long res=1; while(b) { if(b&1) res=multi(res,a,m); b>>=1; a=multi(a,a,m); } return res; } int primetest(long long n) { for(int i=1;i<=100;i++) { long long a=random(n-1); if(quickmod(a,n,n)!=a) return 0; } return 1; }
//这个是一次判断,会受到卡迈克尔数的影响,下面的是二次判断版 int check(long long a,long long n,long long x,long long t) { long long res=quickmod(a,x,n); long long last=res; for(int i=1;i<=t;i++) { res=multi(res,res,n); if(res==1&&last!=1&&last!=n-1) return 1; last=res; } if(res!=1) return 1; return 0; } int primetest(long long n) { if(n<2)return 0; if(n==2)return 1; if((n&1)==0) return 0; long long x=n-1; long long t=0; while((x&1)==0){x>>=1;t++;} for(int i=0;i<20;i++) { long long a=random(n); if(check(a,n,x,t)) return 0; } return 1; }
三.算术基本定理
任何一个大于1的正整数都可以表示为素数的积的形式。
令a=p1^a1*p2^a2......*pn^an b=p1^b1*p2^b2......*pn^bn
(1)设d(a)为a的正因子的个数,s(n)为所有因子之和,则
d(n)=(a1+1)(a2+1).....*(an+1);
s(n)=乘积(i=1...n):(pi^(ai+1)-1)/(pi-1);
(2)gcd(a,b)=p1^min(a1,b1)...*pn^(min(an,bn));
lcm则为 max..这两个很好理解
(3)n!的素因子分解中的素数P的幂为
sum(i=1.....∞)[n/(p^i)]; 例题: 求n!末尾0的个数。只需要用此公式算出因子中5的个数。
四.反素数
(1)定义:对于任何正整数x,其约数的个数记做g(x).例如g(1)=1,g(6)=4.如果某个正整数x满足:对于任意i(0<i<x),都有g(i)<g(x),则称x为反素数.
(2)性质:性质一:一个反素数的质因子必然是从2开始连续的质数.
性质二:p=2^t1*3^t2*5^t3*7^t4.....必然t1>=t2>=t3>=....
结合算数基本定理(1)对性质2进行证明:如果ti<tj,其中i<j,由于pi小于pj,那么pi^tj*pj^ti<pi^ti*pj^tj,这样就出现了
因子数目相同,但x更小的情况,与反素数的定义矛盾。
(3)例题:zoj2562,给定n,求不大于n的正整数中因子最多的数 题解
五.素因子分解
基于miller-robin的随机数算法:模板如下 原理及延伸 例题:poj1881题解 poj2429题解
long long pollardrho(long long n,long long c) { long long x,y,d,i,k; i=1;k=2; x=random(n); y=x; while(1) { i++; x=(multi(x,x,n)+c)%n; long long tmp=y-x>=0?y-x:x-y; d=gcd(tmp,n); if(d>1&&d<n) return d; if(y==x) return n; if(i==k) { y=x; k+=k; } } } void findfac(long long n) { if(n==1) return; if(primetest(n)) { fac[nfac++]=n;return; } long long p=n; while(p>=n) p=pollardrho(n,random(n-1)); findfac(p); findfac(n/p); }