1、什么是Miller-Rabin
Miller - Rabin 算法是一种用于判断一个数是否为素数的概率性算法。在密码学等领域,经常需要快速判断一个大整数是否为素数。传统的试除法对于大整数效率极低,而 Miller - Rabin 算法能够在较短时间内以较高的概率判断一个数是否为素数。
2、相关数学基础
费马小定理
如果p是素数,a是整数且a与p互质(即 gcd(a,b)=1 ),那么。不过费马小定理的逆定理不成立,即满足的p不一定是素数。这就是为什么单纯依赖费马小定理来判断素数会有问题,Miller-Rabin算法就是在此处进行改进。
反例:卡迈克尔数(伪质数)
卡迈克尔数
定义:对于合数n,如果对于所有与n互质的正整数b,都有同余式成立,则称合数n为卡迈克尔数。
性质:
- 至少是三个不同素数的乘积:每个卡迈克尔数至少是三个不同素数的乘积,例如最小的卡迈克尔数
- 满足 Korselt 准则:卡迈克尔数的等效定义,当正合数n满足以下三个性质时,它就是一个卡迈克尔数:必须包含不止一个质因数;质因数均不重复;对于每一个能被n整除的质数p, 也可以被 整除。
因为对于卡迈克尔数n,都有成立,但n不是素数(费马小定理的逆定理不成立)
二次探测定理
如果p是奇素数,x是小于p的正整数,且,那么或者。
3、算法步骤
如果先不考虑卡迈克尔数,常规判断n是否是素数(概率):枚举底数,,判断等于多少,如果,那么n大概率是素数。
但卡迈克尔数不是素数,却满足,所以需要筛掉卡迈克尔数,使用Miller-Rabin算法能进行有效筛掉卡迈克尔数。
如何筛掉卡迈克尔数呢?
第一次是进行费马小定理,第二次通过二次探测原理来筛掉卡迈克尔数。
二次探测定理:
假设 p 是一个素数,那么 p 显然是奇数,所以 p-1 显然是偶数。
假设有,p-1 是偶数,令,则有。
由费马小定理得,,
那么,平方差变形可得,则说明或。
也就是说,第一次是进行了费马小定理,但此时还没筛去卡迈克尔数,就需要通过判断或是否成立,如果成立则说明不是卡迈克尔数且大概率是素数,否则不是素数。如果将 ,可得,令,可得序列,通过同余定理,可得序列每一项mod p后的序列为,由同余定理得知,mod p后的序列里,如果是x ->...-> -1 -> 1...序列中间有一个-1的,那么-1之后就只能是1;如果中间没有-1,那么之后也就不会是1;所以有:
- p是质数:要么起始就是1,要么中间出现-1
- p是合数:中间没有出现-1
4、代码实现
python
import random def miller_rabin(n, k): if n == 2: return True if n % 2 == 0: return False # 分解n - 1为2^s * d的形式 s = 0 d = n - 1 while d % 2 == 0: s += 1 d //= 2 for _ in range(k): a = random.randint(2, n - 2) x = pow(a, d, n) if x == 1 or x == n - 1: continue for _ in range(s - 1): x = pow(x, 2, n) if x == n - 1: break else: return False return True
cpp
#include <iostream> #include <cstdlib> #include <ctime> // 快速幂取模运算,用于计算 (a^b) % m int pow_mod(int a, int b, int m) { int result = 1; a %= m; while (b > 0) { if (b & 1) { result = (result * a) % m; } a = (a * a) % m; b >>= 1; } return result; } // Miller-Rabin素性测试算法 bool miller_rabin(int n, int k) { if (n == 2) return true; if (n % 2 == 0 || n < 2) return false; // 分解 n - 1 为 2^s * d 的形式 int s = 0; int d = n - 1; while (d % 2 == 0) { s++; d /= 2; } // 进行k轮测试 for (int i = 0; i < k; i++) { int a = rand() % (n - 3) + 2; // 选取 [2, n - 2] 之间的随机数作为底数 int x = pow_mod(a, d, n); if (x == 1 || x == n - 1) continue; bool composite = true; for (int r = 1; r < s; r++) { x = pow_mod(x, 2, n); if (x == n - 1) { composite = false; break; } } if (composite) return false; } return true; } //测试 int main() { srand(static_cast<unsigned int>(time(nullptr))); int num = 121; // 这里可以替换成你想要测试的数 int round = 5; // 测试轮数,可以根据需求调整 if (miller_rabin(num, round)) { std::cout << num << " 可能是素数" << std::endl; } else { std::cout << num << " 是合数" << std::endl; } return 0; }