有时候我们想快速的知道一个数是不是素数,而这个数又特别的大导致 $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$.

不妨将费马小定理和二次探测定理结合起来使用:

  1. 对偶数和0、1、2直接判断
  2. 设要测试的数为 $n$,设$m$、$k$,满足 $n-1 = m2^k$(使其中 $m$ 为奇数)
  3. 我们先算出 $a^t$,然后不断地平方且进行二次探测(进行 $k$ 次)
  4. 最后根据费马小定理探测一次
  5. 多次取不同的 $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. 百度百科——卡迈克尔数

2.OI WIKI——素数

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素性测试的更多相关文章

  1. POJ1811_Prime Test【Miller Rabin素数测试】【Pollar Rho整数分解】

    Prime Test Time Limit: 6000MS Memory Limit: 65536K Total Submissions: 29193 Accepted: 7392 Case Time ...

  2. HDU1164_Eddy&#39;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 ...

  3. 关于素数:求不超过n的素数,素数的判定(Miller Rabin 测试)

    关于素数的基本介绍请参考百度百科here和维基百科here的介绍 首先介绍几条关于素数的基本定理: 定理1:如果n不是素数,则n至少有一个( 1, sqrt(n) ]范围内的的因子 定理2:如果n不是 ...

  4. 米勒罗宾素性测试(Miller–Rabin primality test)

    如何判断一个素是素数 效率很高的筛法 打个表 (素数的倍数一定是合数) 就可以解决问题. 筛选法的效率很高,但是遇到大素数就无能为力了. 米勒罗宾素性测试是一个相当著名的判断是否是素数的算法 核心为费 ...

  5. 与数论的厮守01:素数的测试——Miller Rabin

    看一个数是否为质数,我们通常会用那个O(√N)的算法来做,那个算法叫试除法.然而当这个数非常大的时候,这个高增长率的时间复杂度就不够这个数跑了. 为了解决这个问题,我们先来看看费马小定理:若n为素数, ...

  6. 素数与素性测试(Miller-Rabin测试)

    转载自Matrix大牛的博客 把代码翻译成C++ http://www.matrix67.com/blog/archives/234 题目链接: http://hihocoder.com/proble ...

  7. 【数学】【筛素数】Miller-Rabin素性测试 学习笔记

        Miller-Rabin是一种高效的随机算法,用来检测一个数$p$是否是素数,最坏时间复杂度为$\log^3 p$,正确率约为$1-4^{-k}$,$k$是检验次数. 一.来源     Mil ...

  8. Miller-Rabin 素性测试 与 Pollard Rho 大整数分解

    \(\\\) Miller-Rabin 素性测试 考虑如何检验一个数字是否为素数. 经典的试除法复杂度 \(O(\sqrt N)\) 适用于询问 \(N\le 10^{16}\) 的时候. 如果我们要 ...

  9. miller_rabin_素性测试

    摘自:http://blog.csdn.net/pi9nc/article/details/27209455 看了好久没看懂,最后在这篇博客中看明白了. 费马定理的应用,加上二次探测定理. Ferma ...

  10. POJ1811- Prime Test(Miller–Rabin+Pollard's rho)

    题目大意 给你一个非常大的整数,判断它是不是素数,如果不是则输出它的最小的因子 题解 看了一整天<初等数论及其应用>相关部分,终于把Miller–Rabin和Pollard's rho这两 ...

随机推荐

  1. React学习,搭建项目,入门

    react项目基于node.js.用npm包管理工具; 在cmd命令行中输入: npm install -g create-react-app 等待其安装,意思是全局安装create-react-ap ...

  2. [转帖]Nginx为什么高效?一文搞明白Nginx核心原理

    Nginx为什么高效?一文搞明白Nginx核心原理 咔咔侃技术 2019-09-06 15:37:00 https://www.toutiao.com/a6733057587622707724/ Ng ...

  3. [转帖]linux常用命令大全(linux基础命令入门到精通+实例讲解+持续更新+命令备忘录+面试复习)

    linux常用命令大全(linux基础命令入门到精通+实例讲解+持续更新+命令备忘录+面试复习) https://www.cnblogs.com/caozy/p/9261224.html 总结的挺好的 ...

  4. java中内存泄露有几种?如何分析泄露原因

    一.Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Hea ...

  5. Python 【模块】

    A 什么是模块 最高级别的程序组织单元(模块什么都能封装) 模块中,我们不但可以直接存放变量,还能存放函数,还能存放类 定义变量需要用赋值语句,封装函数需要用def语句,封装类需要用class语句,但 ...

  6. TiDB部分

    TiDB部分 https://blog.csdn.net/D_Guco/article/details/80641236 https://www.v2ex.com/t/508094 https://u ...

  7. 使用Duilib开发Windows软件(3)——控件的样式

    摘抄下 https://www.cnblogs.com/Alberl/p/3344936.html 的一段代码 <?xml version="1.0" encoding=&q ...

  8. Qt中的常用容器类(解释比较全面,有插图)

    在Qt库中为我们提供了一系列的基于模板的容器类.这些类可以被用来存储特定类型的项.例如,如果你需要一个大小可以变得QString数组,那么可以使用QVector<QString>. 这些容 ...

  9. Python操作Redis(转)

    原文1:https://cloud.tencent.com/developer/article/1151834 原文2:https://www.cnblogs.com/zhaohuhu/p/91406 ...

  10. access 数据库创建表SQL语法

    create table R_CAIFA_B13 ( ID AUTOINCREMENT PRIMARY KEY, XB varchar(255), C1 varchar(50), C2 varchar ...