板子
1 #include <bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 LL n; vector <LL> z; 5 const LL mo=1e9+7; 6 LL gcd(LL x,LL y){ 7 return y?gcd(y,x%y):abs(x); 8 } 9 LL po(LL x,LL y){ 10 LL z=1; x%=mo; 11 for (;y;y>>=1,x=x*x%mo) 12 if (y&1) z=z*x%mo; 13 return z; 14 } 15 LL XX(LL x,LL y,LL z){ 16 /* 17 (long double)x/z*y求商可以有微小的误差,之后反正会+z%z模回来 18 但如果 没有x%=z,y%=z,商的误差可能会很大,这样作差时会±k*maxLL 而GG 19 */ 20 x%=z,y%=z; return ((x*y-(LL)((long double)x/z*y)*z)%z+z)%z; 21 // return (__int128)x*y%z; 22 } 23 int St(LL a,LL b){ //a^(b-1)!=1?&&二次探测 24 //二次探测定理:如果p是奇素数,则x^2≡1(mod p)的解为x=1||x=p-1(mod p); 25 if (a==b) return 0; 26 LL ans1=1,ans=1; 27 for (int i=64;i>=0;i--){ 28 ans=XX(ans1,ans1,b); 29 if (ans==1&&ans1!=1&&ans1!=b-1) return 1; 30 if ((b-1)&(1LL<<i)) ans=XX(ans,a,b); 31 ans1=ans; 32 } 33 return ans!=1; 34 } 35 int isP(LL x){ // x=1时为0; 36 return !(St(2,x)||St(3,x)||St(5,x)||St(7,x)||St(11,x)||St(13,x)||St(17,x)||St(19,x)||St(23,x)); 37 } 38 LL ra(LL x){ 39 return (rand()*2147483647LL+rand()*32767LL+rand())%x; 40 } 41 LL pro(LL x){ 42 LL x1=ra(x),x2=(XX(x1,x1,x)+1)%x,p=gcd(x1-x2,x); 43 while (p==1){ //不用判x=y,大不了返回x 44 x1=(XX(x1,x1,x)+1)%x; 45 x2=(XX(x2,x2,x)+1)%x; 46 x2=(XX(x2,x2,x)+1)%x; 47 p=gcd(x1-x2,x); 48 } 49 return p; 50 } 51 void divide(LL x){ 52 while (!(x&1)) z.push_back(2),x/=2; 53 if (x==1) return; 54 if (isP(x)) z.push_back(x);else{ 55 LL y=pro(x); 56 divide(x/y); divide(y); 57 } 58 } 59 int main(){ 60 scanf("%lld",&n); 61 divide(n); 62 printf("%lld=",n); 63 if (n==1){ 64 puts("1"); return 0; 65 } 66 sort(z.begin(),z.end()); 67 for (int q=0,h=0;q<z.size();q=h){ 68 while (h<z.size()&&z[h]==z[q]) ++h; 69 if (q!=h-1) printf("%lld^%d",z[q],h-q); else printf("%lld",z[q]); 70 if (h!=z.size()) printf("*"); 71 } 72 puts(""); 73 }
(没想到会在大学概统课写小论文时,回来补充高中的博客。。)
面对密码学中的大数问题,如大素数检验,大合数分解,精确的算法在现实的机器下难以实现,所以有了概率算法。
首先讲大素数检验
现有的概率检验算法,基本逻辑框架是,对于一个正奇数n,令集合S={1,…,n-1},存在一个S的子集W,W满足两个性质:
- 当给出一个a∈S ,能在较快的时间(多项式时间复杂度)内,判断是否有 a∈W;
- 若n为素数,则W=S ;若n为合数,则|W| <= k|S| = k(n-1), k是确定的常数且k∈(0,1)
简单理解:若a∈S而a∉W,则能够判定n不是素数。一个合数n,有k的概率不能被检验出来。
检验的流程:每次独立随机生成 a∈S,对n作上述判定。经过t轮判断后,结束检验。
此时,合数n依然被误认为是素数的概率<=kt,t足够大时,就几乎不会出错了。
目标就是构造这样“良好的W”:
①很弱的办法——公约数检验(我起的名字)
$W={a | gcd(a,n)=1,a in S}$ ,也就是说,随机生成的a必须和n有“非1公约数”,才能判定n不是素数。
这时候,$|W|=φ(n)$,(φ-欧拉函数),$|W| leq n-sqrt{n}$ ,(考虑$n=p^{2}$,p为质数,这种情况下左式取等号),显然不满足良好W的第二条性质。
②尝试改进 ——“费马素性检验”算法
费马小定理:对于质数$n$,若正整数$a in S$,且$gcd(a,n)=1$,则$a^{n-1}≡1$
但是,费马小定理只是素数的必要不充分条件,逆命题不成立。
有一些合数,对于部分a 满足费马小定理,这样的合数被称为“伪素数”(确切的说,是“a的伪素数”),最小的伪素数是341。
假设$W={ a|a in S 且gcd(a,n)=1 且 a^{n-1} ≡ 1 }$
下面证明一个定理:
若$W={a_{1},a_{2},...,a_{k}}$,如果$exists a in S 且 gcd(a,n)=1 , a^{n-1} eq 1 ,则 (a cdot a_{i})^{n-1} eq 1$
则,${a cdot a_{1},a cdot a_{2},...,a cdot a_{k}}$ 也是S的子集, 且与W无交集。
因此,$|W| leq frac{1}{2} |S|$。
由上定理知,对于一个n,只要存在$a in S$可以检验出n不是素数,则该检验方法就能满足“良好W”的性质。
实际上,这种算法确实已经很棒了,优先取2,3,5,7等小质数,可以很快淘汰掉绝大部分合数。
但是,在伪素数中,还有一类数——卡迈尔数。它对任何$a in S$都满足费马小定理,最小的卡迈尔数是561。
虽然,1亿以内只有255个卡迈尔数,可一旦遇到了,费马素性检验就退化成了①。
③再次改进——Miller_rabin算法
二次探测定理: 对于质数$n$,若正整数$a in S$,且$a^{2} equiv 1 (mod n)$,则 a=1或-1
算法原理:
对于正奇数n,$n-1=r2^{s}$,r为奇数。
若n为素数,则$a^{r2^{s}} equiv 1$, 则,$a^{r2^{s-1}} = 1或-1$ ;若为1,则,$a^{r2^{s-2}} = 1或-1$ ……
由此得到,素数n,应满足:$forall a in S,{ a^{r},a^{r2^{1}},a^{r2^{2}},...,a^{r2^{s-1}} }$,要么全为1,要么有一个-1(其后每一项都为1)。
令W={满足上述条件的a}
可以证明$|W| leq frac{1}{4} |S|$ ,且不像②那样依赖于n或者a。
这样,我们就得到了一个“良好的W”,能出色的完成大素数检验的任务了。
下面补充Miller_rabin一次误判概率$leq frac{1}{4}$的证明,不感兴趣的可以跳过。
定理1:
对正整数n,在mod n意义下的m阶有限群${x,x^{2},x^{3},cdots,x^{m}=1}$中,(x为生成元),有$gcd(m,k)$个元素,满足$(x^{i})^{k}=1$。
证明很简单。若$d=gcd(m,k)$,则满足条件的i的集合$={frac{m}{d},frac{2m}{d},cdots,m}$。
定理2:
对于奇素数n,$n-1=h2^{s}$,h为奇数,则对于正奇数t,满足$x^{t2^{r}}=-1$的x的个数$= egin{cases} 0,qquad qquad r geq s \ 2^{r}*gcd(h,t),quad r < s end{cases} $
证明:
Case1:$(x^{t2^{r}})^{h}=(x^{h2^{r}})^{t}=ig((x^{h2^{s}})^{2^{r-s}}ig)^{t}=1$, 又$ecause h$为奇数,$ herefore x^{t2^{r}} eq -1$
Case2:设奇素数n 原根是g, $S={1,g^{1},cdots,g^{n-2}} ,g^{frac{n-1}{2}}=-1$。
即求,满足$(g^{i})^{t2^{r}}=-1$的i的个数,$i in [0,n-2]$,
则,$(n-1) mid (it2^{r}-frac{n-1}{2}) qquad Rightarrow qquad (2^{s}h) mid (it2^{r}-h2^{s-1}) $
设$d=gcd(h,t)$ $Rightarrow (2^{s-r} cdot frac{h}{d}) mid (i cdot frac{t}{d}-2^{s-1-r} cdot frac{h}{d})$
应取$i=2^{s-1-r} cdot frac{h}{d} cdot (2k+1)$,k为整数,显然,在$i in [0,n-2]$中,分别对应$k in [0,2^{r}d-1]$,一共$2^{r}d$个。
另外,补充一个有一点点相关的知识:
素数定理(关于素数个数的定理): π(x)表示不大于x的素数个数, π(x) ~ $frac{x}{ln{x}}$。
这也说明,从$[1,n]$中随机选一个数,它是素数的概率大约是 $frac {1}{ ln{n}}$
所以,即使是近50位的十进制大整数,也有约1%的概率是素数。
================================================================
1、a^(b-1)!=1 加上 二次探测 误判率据说是 1/4
(而这个板子只判了9次 误判概率好像有点高啊)
2、生日悖论
期望 sqrt(n)个 [1,n]中随机的数 当中 有两个数是相同的
3、
选取xi= (xi-1* xi-1 +1)%n 可以认为是会有循环节的 [1,n]中的随机函数
根据生日悖论 ,循环节长度期望是 sqrt(n)
假设 ,p是n的最小质因子, 则p<=sqrt(n)
同理可得,xi%p的 循环节长度期望是sqrt(p)
然后,我们用两个指针 i1,i2 分别以一倍速和二倍速扫这些数
期望sqrt(p)步,i1与i2 在%p意义下是相同的(即在两个循环节的同一个位置,位置的差为一个循环节), 而它们在%n意义下是不同的。
此时,gcd(i1-i2,n) 是p的倍数,n的约数,于是我们成功分解了一次n,期望用 n^(1/4)次。
当然 这都是基于期望,也有可能循环节太短找不到,所以要做多次。