序言
- willson定理:https://blog.csdn.net/fengqiyuka/article/details/
- 普通篇:https://blog.csdn.net/fengqiyuka/article/details/
背景
- “还有没有判断素数的方法?”
- “当然有,那就是Miller_Rabin!”
- 这个算法能做到 O ( l o g n ) O(logn) O(logn)级别,但是相较于普通的 O ( n ) O(\sqrt n) O(n)以及willson定理所需要的神奇的快速求阶乘相比,它的优势也是十分明显的。
- 但,这一个算法有一个局限性。就是它不能做到判断所有的素数。
- 这时候就有人开始担心了——这么个正确性玄学的算法,用起来都觉得不安心!
- 可是,我们要知道,在一般的题目中,我们最多就只会判断大小在long long(大约在 [ 1 , 1 0 19 ] [1,10^{19}] [1,1019])范围内的素数。在一个这么小的范围内,它的正确性可以达到100%。
必备的定理
费马小定理
- 若 p p p是质数, a m o d p ≠ 0 a \mod p \neq 0 amodp=0,那么 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1(\mod p) ap−1≡1(modp)。
- 具体的证明可以考虑用剩余系,把 [ 1 , p − 1 ] [1,p-1] [1,p−1]的数用形如 k ∗ a k*a k∗a的数来代替。
- 而 k k k的集合本身也是 [ 1 , p − 1 ] [1,p-1] [1,p−1]。
二次探测定理
- 若存在 a a a,使 a 2 ≡ 1 ( m o d p ) a^2 \equiv 1 (\mod p) a2≡1(modp), a m o d p ≠ − 1 a \mod p \ne -1 amodp=−1且 a m o d p ≠ 1 a\mod p\ne 1 amodp=1,则 p p p是质数。
- 证明可以考虑反证法, ( a − 1 ) ( a + 1 ) ≡ 0 ( m o d p ) (a-1)(a+1) \equiv 0(\mod p) (a−1)(a+1)≡0(modp),发现这样是与 p p p为质数矛盾。故定理成立。
Miller_Rabin
- 我们可以用费马小定理和二次探测定理的“逆定理”来做。
- 这里之所以要打双引号,是因为它们的条件只具备必要性,是没有逆定理的。
- 但这样正确性不就玄学了吗?
- 上面已经说过了,在小范围内正确的概率就会升到100%。
- 关键是如何提升概率。
- 显而易见的,我们可以选取多个 a a a,但注意一定要是素数,不然的话有可能不会满足费马小定理中 a a a与 p p p不是倍数关系的条件。
- 对于每一个 a a a,我们就用“逆定理”来反过来推断该数是不是素数。
- 具体流程是这样的:
- 选取素数{2,3,5,7,…}
- 对于待测数 p p p,设 p − 1 p-1 p−1= 2 s ∗ t 2^s*t 2s∗t, t t t为奇数,先求出 d = a t d=a^t d=at。
- 不断平方,若平方到 − 1 -1 −1,则二次探测定理已经无效了,直接用费马小定理判断。
- 若在没有出现过 − 1 -1 −1的情况下平方到 1 1 1,则说明 p p p为合数。
- 若知道平方到 a p − 1 a^{p-1} ap−1也没有出现1,则不符费马小定理, p p p为合数。
- 其余情况,则说明 p p p有可能数素数,选取下一个 a a a判断。
- 若所有 a a a都试过,没有出现能说明 p p p一定为合数的情况是,认定 p p p为素数。
其它
- 一般来说,a选9个就绝对没有问题了。
- 如果待测数太大,要用快速乘,则时间复杂度退化到 O ( l o g 2 n ) O(log^2n) O(log2n),但跑的还是不错的。
代码
#include<cstdio> #include<cstring> using namespace std; typedef long long ll; int a[10]={2,3,5,7,11,13,17,19,23,29}; ll mi(ll x,ll t,ll mod){ ll d=1; while(t){ if(t%2) d=d*x%mod; x=x*x%mod;t/=2; } return d; } bool check(ll x){ if(x==1) return false; ll t=x-1;int p=0; while(t%2==0) t/=2,p++; for(int i=0;i<=9;i++){ if(a[i]==x) return true; ll t2=mi(a[i],t,x); for(int j=1;j<=p;j++){ if(t2==x-1) {t2=mi(a[i],x-1,x);break;} ll t3=mi(t2,2,x); if(t3==1&&t2!=1&&t2!=x-1) return false; t2=t3; } if(t2!=1) return false; } return true; } int main() { ll n;scanf("%lld",&n); if(check(n)) printf("YES\n"); else printf("NO\n"); return 0; }
讯享网
总结
- 素数的世界如此精彩,我们的探索将会一直继续!
(完)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/51793.html