Miller-Rabin素性测试
有时候我们想快速的知道一个数是不是素数,而这个数又特别的大导致 $O(\sqrt n)$ 的算法也难以通过,这时候我们可以对其进行 Miller-Rabin 素数测试,可以很大概率测出其是否为素数。
两个理论基础
(1)费马小定理:当 $p$ 为质数,有 $a^{p-1}\equiv 1(mod \ p)$,反过来不一定成立,也就是说,如果 $a, \ p$ 互质,且 $a^{p-1}\equiv 1(mod \ p)$,不能推出 $p$ 是质数,比如 $Carmichael$ 数
(2)二次探测:如果 $p$ 是素数,$0 < x < p$,则方程 $x^2 \equiv 1(mod \ p)$ 的解为 $x=1$ 或 $x=p-1$.
证一下二次探测定理:
易知 $ x^2-1 \equiv 0(mod \ p)$
$\because(x+1)(x-1) \equiv 0\ (mod \ p)$
$\because$ $p$ 为素数,只能分解为
$\because x+1 \equiv 0\ (mod \ p)$ 或 $x-1 \equiv 0\ (mod \ p)$
根据 $x$ 的取值范围,$x=1$ 或 $x=p-1$.
Fermat 素性测试
只根据费马小定理,易得一种检测质数的思路:
它的基本思想就是多次从 $\left [ 2, n-1 \right ]$ 中选取基 $a$,并检验是否每次都有 $a^{n-1} \equiv 1\ (mod \ n)$
bool millerRabin(int n) {
if (n < ) return n == ;
// test_time 为测试次数,建议设为不小于 8
// 的整数以保证正确率,但也不宜过大,否则会影响效率
int test_time = ;
for (int i = ; i <= test_time; ++i) {
int a = rand() % (n - ) + ;
if (qpow(a, n - , n) != )
{
printf("a:%d\n", a);
return false;
}
}
return true;
}
遗憾的是,由于费马小定理的逆定理并不成立 ,即满足 $a^{n-1} \equiv 1\ (mod \ n)$,$n$ 也不一定是素数。
卡迈克尔数
上面的做法中随机地选择 $a$,很大程度上降低了犯错概率,但是仍有一类数,上面的做法并不能准确地判断。
对于合数,如果对于所有与 $n$ 互素的正整数 $a$,都有同余式 $a^{n-1} \equiv 1\ (mod \ n)$ 成立,则合数 $n$ 为卡迈克尔数(Carmichael Number),又称费马伪素数。
而且我们知道,若 $n$ 为卡迈克尔数,则 $2^n-1$ 也是卡迈克尔数,从而卡迈克尔数的个数是无穷的。
在1~100000000范围内的整数中,只有255个Carmichael数。前3个Carmichael数是561,1105,1729。
Miller-Rabin测试
根据卡迈克尔数的性质,可知其一定不是 $p^e$.
不妨将费马小定理和二次探测定理结合起来使用:
- 对偶数和0、1、2直接判断
- 设要测试的数为 $n$,设$m$、$k$,满足 $n-1 = m2^k$(使其中 $m$ 为奇数)
- 我们先算出 $a^t$,然后不断地平方且进行二次探测(进行 $k$ 次)
- 最后根据费马小定理探测一次
- 多次取不同的 $a$ 进行上面3步
可以证明Miller-Rabbin算法单次出错的概率小于等于 $\frac{1}{4}$,若重复 $k$ 次,则出错概率降低至 ${(\frac{1}{4})}^k$。这是一个很保守的估计,实际效果要好得多。
#include<bits/stdc++.h>
using namespace std; typedef long long int ll; ll mod_mul(ll a, ll b, ll mod)
{
ll res = ;
while (b)
{
if (b & )
res = (res + a) % mod;
a = (a + a) % mod;
b >>= ;
}
return res;
} ll mod_pow(ll a, ll n, ll mod)
{
ll res = ;
while (n)
{
if (n & )
res = mod_mul(res, a, mod);
a = mod_mul(a, a, mod);
n >>= ;
}
return res;
} // Miller-Rabin随机算法检测n是否为素数
bool Miller_Rabin(ll n)
{
if (n == )
return true;
if (n < || !(n & ))
return false;
ll m = n - , k = ;
while (!(m & ))
{
k++;
m >>= ;
}
for (int i = ; i <= ; i++) // 20为Miller-Rabin测试的迭代次数
{
ll a = rand() % (n - ) + ;
ll x = mod_pow(a, m, n);
ll y;
for (int j = ; j <= k; j++)
{
y = mod_mul(x, x, n);
if (y == && x != && x != n - )
return false;
x = y;
}
if (y != )
return false;
}
return true;
} int main()
{
ll n;
while(scanf("%lld", &n) == )
{
printf("%d\n", Miller_Rabin(n));
}
}
注:
(1)时间复杂度 $O(Slog^3)$($S$ 为测试次数)
(2)long long相乘可能会爆long long,所以使用快速乘
(3)当 $a$ 取遍30内的素数,$int$ 都不会出错
(4)记住几个素数19260817、23456789、2092876369、11111111111111111111111(23个1)
(5)还可以去 hihocodeCoder 1287 测一下板子
参考链接:
1. 百度百科——卡迈克尔数
3. https://blog.csdn.net/forever_dreams/article/details/82314237?tdsourcetag=s_pctim_aiomsg
4. https://blog.csdn.net/ECNU_LZJ/article/details/72675595
5. http://www.matrix67.com/blog/archives/234
Miller-Rabin素性测试的更多相关文章
- POJ1811_Prime Test【Miller Rabin素数测试】【Pollar Rho整数分解】
Prime Test Time Limit: 6000MS Memory Limit: 65536K Total Submissions: 29193 Accepted: 7392 Case Time ...
- HDU1164_Eddy's research I【Miller Rabin素数测试】【Pollar Rho整数分解】
Eddy's research I Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...
- 关于素数:求不超过n的素数,素数的判定(Miller Rabin 测试)
关于素数的基本介绍请参考百度百科here和维基百科here的介绍 首先介绍几条关于素数的基本定理: 定理1:如果n不是素数,则n至少有一个( 1, sqrt(n) ]范围内的的因子 定理2:如果n不是 ...
- 米勒罗宾素性测试(Miller–Rabin primality test)
如何判断一个素是素数 效率很高的筛法 打个表 (素数的倍数一定是合数) 就可以解决问题. 筛选法的效率很高,但是遇到大素数就无能为力了. 米勒罗宾素性测试是一个相当著名的判断是否是素数的算法 核心为费 ...
- 与数论的厮守01:素数的测试——Miller Rabin
看一个数是否为质数,我们通常会用那个O(√N)的算法来做,那个算法叫试除法.然而当这个数非常大的时候,这个高增长率的时间复杂度就不够这个数跑了. 为了解决这个问题,我们先来看看费马小定理:若n为素数, ...
- 素数与素性测试(Miller-Rabin测试)
转载自Matrix大牛的博客 把代码翻译成C++ http://www.matrix67.com/blog/archives/234 题目链接: http://hihocoder.com/proble ...
- 【数学】【筛素数】Miller-Rabin素性测试 学习笔记
Miller-Rabin是一种高效的随机算法,用来检测一个数$p$是否是素数,最坏时间复杂度为$\log^3 p$,正确率约为$1-4^{-k}$,$k$是检验次数. 一.来源 Mil ...
- Miller-Rabin 素性测试 与 Pollard Rho 大整数分解
\(\\\) Miller-Rabin 素性测试 考虑如何检验一个数字是否为素数. 经典的试除法复杂度 \(O(\sqrt N)\) 适用于询问 \(N\le 10^{16}\) 的时候. 如果我们要 ...
- miller_rabin_素性测试
摘自:http://blog.csdn.net/pi9nc/article/details/27209455 看了好久没看懂,最后在这篇博客中看明白了. 费马定理的应用,加上二次探测定理. Ferma ...
- POJ1811- Prime Test(Miller–Rabin+Pollard's rho)
题目大意 给你一个非常大的整数,判断它是不是素数,如果不是则输出它的最小的因子 题解 看了一整天<初等数论及其应用>相关部分,终于把Miller–Rabin和Pollard's rho这两 ...
随机推荐
- React学习,搭建项目,入门
react项目基于node.js.用npm包管理工具; 在cmd命令行中输入: npm install -g create-react-app 等待其安装,意思是全局安装create-react-ap ...
- [转帖]Nginx为什么高效?一文搞明白Nginx核心原理
Nginx为什么高效?一文搞明白Nginx核心原理 咔咔侃技术 2019-09-06 15:37:00 https://www.toutiao.com/a6733057587622707724/ Ng ...
- [转帖]linux常用命令大全(linux基础命令入门到精通+实例讲解+持续更新+命令备忘录+面试复习)
linux常用命令大全(linux基础命令入门到精通+实例讲解+持续更新+命令备忘录+面试复习) https://www.cnblogs.com/caozy/p/9261224.html 总结的挺好的 ...
- java中内存泄露有几种?如何分析泄露原因
一.Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Hea ...
- Python 【模块】
A 什么是模块 最高级别的程序组织单元(模块什么都能封装) 模块中,我们不但可以直接存放变量,还能存放函数,还能存放类 定义变量需要用赋值语句,封装函数需要用def语句,封装类需要用class语句,但 ...
- TiDB部分
TiDB部分 https://blog.csdn.net/D_Guco/article/details/80641236 https://www.v2ex.com/t/508094 https://u ...
- 使用Duilib开发Windows软件(3)——控件的样式
摘抄下 https://www.cnblogs.com/Alberl/p/3344936.html 的一段代码 <?xml version="1.0" encoding=&q ...
- Qt中的常用容器类(解释比较全面,有插图)
在Qt库中为我们提供了一系列的基于模板的容器类.这些类可以被用来存储特定类型的项.例如,如果你需要一个大小可以变得QString数组,那么可以使用QVector<QString>. 这些容 ...
- Python操作Redis(转)
原文1:https://cloud.tencent.com/developer/article/1151834 原文2:https://www.cnblogs.com/zhaohuhu/p/91406 ...
- access 数据库创建表SQL语法
create table R_CAIFA_B13 ( ID AUTOINCREMENT PRIMARY KEY, XB varchar(255), C1 varchar(50), C2 varchar ...