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这两 ...
随机推荐
- Java线程状态的改变
一.线程状态 线程的状态转换是线程控制的基础.线程状态总的可分为五大状态:分别是生.死.可运行.运行.等待/阻塞.用一个图来描述如下: 1.新状态:线程对象已经创建,还没有在其上调用start()方法 ...
- 系统获取 IP 工具类
系统获取 IP 工具类 import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterf ...
- Yii2 设计模式——单例模式
单例模式 模式定义 单例模式确保一个类只有一个实例,并提供一个全局访问点.当现实中只需要一个对象,或者为了节省系统资源,又或者是为了共享数据的时候可以使用单例模式. 代码实现 我们先来看看单例模式的标 ...
- 编写并提取通用 ShellCode
简易 ShellCode 虽然可以正常被执行,但是还存在很多的问题,因为上次所编写的 ShellCode 采用了硬编址的方式来调用相应API函数的,那么就会存在一个很大的缺陷,如果操作系统的版本不统一 ...
- Qt中的常用容器类(解释比较全面,有插图)
在Qt库中为我们提供了一系列的基于模板的容器类.这些类可以被用来存储特定类型的项.例如,如果你需要一个大小可以变得QString数组,那么可以使用QVector<QString>. 这些容 ...
- HDU4641 K-string(后缀自动机+线段树合并)
先考虑没有动态加字符怎么做.计算每个节点的贡献,当|right|>=k时将len-lenfa计入即可. 动态加字符后,这个东西难以用LCT维护.于是考虑离线.建完SAM后,容易发现每个节点在时间 ...
- Mysql分表和分区的区别、分库和分表区别
一,什么是mysql分表,分区 什么是分表,从表面意思上看呢,就是把一张表分成N多个小表,具体请看:mysql分表的3种方法. 什么是分区,分区呢就是把一张表的数据分成N多个区块,这些区块可以在同一个 ...
- Keras 训练 inceptionV3 并移植到OpenCV4.0 in C++
1. 训练 # --coding:utf--- import os import sys import glob import argparse import matplotlib.pyplot as ...
- SVN 问题解决之 The XML response contains invalid XML
公司几个同事的SVN更新时出现了The XML response contains invalid XML报错 经Google得到一个线索,可能和Http请求有关. 想起之前项目改过一次网络请求方式, ...
- jQuery异步请求ajax()之complete参数详解
请求完成后回调函数 (请求success 和 error之后均调用).这个回调函数得到2个参数:XMLHTTPRequest) 对象和一个描述请求状态的字符串("success", ...