Loading

「学习笔记」素性测试

给定一个正整数 \(n\),判断它是否是质数。

Fermat 素性测试

根据费马小定理,如果 \(n\) 是质数,则会满足 \(a^{n - 1} \equiv 1 \pmod n\)
基本思想是不断地选取在 \(\left [2, n-1 \right ]\) 中的基 \(a\),并检验是否每次都有 \(a^{n-1} \equiv 1 \pmod n\)
如果 \(a^{n−1} \bmod n = 1\)\(n\) 不是素数,则 \(n\) 被称为以 \(a\) 为底的伪素数。我们在实践中观察到,如果 \(a^{n−1} \bmod n = 1\),那么 \(n\) 通常是素数。但这里也有个反例:如果 \(n = 341\)\(a = 2\),即使 \(341 = 11 \cdot 31\) 是合数,有 \(2^{340}\equiv 1 {\pmod {341}}\)。事实上,\(341\) 是最小的伪素数基数。
因此很遗憾,费马小定理的逆定理并不成立,换言之,满足了 \(a^{n-1} \equiv 1 \pmod n\)\(n\) 也不一定是素数。

bool millerRabin(int n) {
	if (n < 3) return n == 2;
	// test_time 为测试次数,建议设为不小于 8 的整数以保证正确率,但也不宜过大,否则会影响效率
	for (int i = 1; i <= test_time; ++i) {
		int a = rand() % (n - 2) + 2;
		if (qpow(a, n - 1, n) != 1) return 0;
	}
	return 1;
}

Miller-Rabin 素性测试

Miller-Rabin 素性测试(Miller–Rabin primality test)是进阶的素数判定方法。它是由 Miller 和 Rabin 二人根据费马小定理的逆定理(费马测试)优化得到的。

二次探测原理

如果 \(p\) 是奇素数,则 \(x^2 \equiv 1 \pmod p\) 的解为 \(x \equiv 1 \pmod p\) 或者 \(x \equiv p - 1 \pmod p\)
证明:

\[x^2 \equiv 1 \pmod p\\ x^2 - 1 \equiv 0 \pmod p\\ (x + 1) \cdot (x - 1) \equiv 0 \pmod p \]

又因为 \(p\) 是质数,所以只能有 \((x + 1) \equiv 0 \pmod p\) 或者 \((x - 1) \equiv 0 \pmod p\)
所以 \(x \equiv p - 1 \pmod p\) 或者 \(x \equiv 1 \pmod p\)


不妨将费马小定理和二次探测定理结合起来使用:
\(a^{n-1} \equiv 1 \pmod n\) 中的指数 \(n−1\) 分解为 \(n−1=u \times 2^t\),在每轮测试中对随机出来的 \(a\) 先求出 \(v = a^{u} \bmod n\),之后对这个值执行最多 \(t\) 次平方操作,若发现非平凡平方根时即可判断出其不是素数,否则再使用 Fermat 素性测试判断。

同余方程 \(x^2 \equiv 1 \pmod n\)\(x \equiv \pm 1 \pmod n\) 的任何根称为“以 \(n\) 为模的 \(1\) 的非平凡平方根”。 若 \(n\) 存在以 \(n\) 为模的 \(1\) 的非平凡平方根,则 \(n\) 是合数。
\(x \equiv -1 \pmod n\) 可以看作是 \(x \equiv -1 + p \pmod n\),即 \(x \equiv p - 1 \pmod n\)

bool millerRabin(int n) {
	if (n < 3 || n % 2 == 0) return n == 2;
	int u = n - 1, t = 0;
	while (u % 2 == 0) u /= 2, ++t;
	// test_time 为测试次数,建议设为不小于 8 的整数以保证正确率,但也不宜过大,否则会影响效率
	for (int i = 0; i < test_time; ++i) {
		int a = rand() % (n - 2) + 2, v = qpow(a, u, n);
		if (v == 1) continue;
		int s;
		for (s = 0; s < t; ++s) {
			if (v == n - 1) break; // 得到平凡平方根 n-1,通过此轮测试
			v = 1ll * v * v % n;
		}
		// 如果找到了非平凡平方根,则会由于无法提前 break; 而运行到 s == t
		// 如果 Fermat 素性测试无法通过,则一直运行到 s == t 前 v 都不会等于 -1
		if (s == t) return 0;
	}
	return 1;
}
posted @ 2023-06-04 21:07  yi_fan0305  阅读(184)  评论(0编辑  收藏  举报