【学习笔记】Miller–Rabin素数测试

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/82317212

【算法简介】

  • M i l l e r R a b i n 素数测试是一种判断一个数是否是质数的方式。
  • 其单次测试的时间复杂度不会超过 O ( L o g 2 N ) ,期望为 O ( L o g N ) ,几乎不需要额外的空间。
  • M i l l e r R a b i n 素数测试不是一个确定算法,其单次测试有不超过 1 4 的概率会将一个合数误判为一个素数。但当被测试数在某一个范围内时,我们可以通过选取适当的测试底数让 M i l l e r R a b i n 素数测试对于这个范围内的每一个数都能够正确地得出结果。

【算法流程】

  • 一个质数具有许多合数不具有的性质,例如:

  • 定理 1 :若 p 是质数,则对于任意 0 < a < p ,有 a p 1 1   ( m o d     p )

  • 引理 2 :若 p 是质数,且 x 2 1   ( m o d     p ) ,那么 x 1   ( m o d     p ) x p 1   ( m o d     p ) 中的一个成立。

  • 其中定理 1 就是 费马小定理 ,引理 2 的证明的如下:

    由于 x 2 1   ( m o d     p ) ,有 ( x 1 ) ( x + 1 ) 0   ( m o d     p )

    ( x 1 ) ( x + 1 ) p 的倍数,因此引理 2 得证。

  • 那么,一个简单的想法就是我们对于被测试数 N ,分别测试定理 1 和引理 2 是否成立,若发现不成立,则说明被测试数 N 一定是一个合数。

  • 具体而言,我们首先要选取一个底数 a ( a < N ) ,计算 a N 1   m o d     N ,若 a N 1   m o d     N 1 ,那么 N 一定是一个合数。

    否则,即 a N 1 1   ( m o d     N ) ,若此时 N 1 为偶数,我们还可以计算 a N 1 2   m o d     N , 若 a N 1 2   m o d     N 1 a N 1 2   m o d     N N 1 ,那么 N 一定是一个合数。

    扫描二维码关注公众号,回复: 3029206 查看本文章

    否则,若 a N 1 2 1   ( m o d     N ) ,并且此时 N 1 2 为偶数,我们还可以计算 a N 1 4   m o d     N , 若 a N 1 4   m o d     N 1 a N 1 4   m o d     N N 1 ,那么 N 一定是一个合数。

    如此重复,直到 N 被确定是一个合数,或者 a x N 1   ( m o d     N ) ,或者 x 为奇数为止。可以发现,这个过程最多会进行 O ( L o g N ) 次,每一次我们需要计算一个乘幂,因此单次测试的最坏时间复杂度为 O ( L o g 2 N )

  • 可以发现,一些合数同样能够通过以某些 a 为底的 M i l l e r R a b i n 素数测试,一种可行的选择是随机底数 a ,进行 k M i l l e r R a b i n 素数测试,算法的错误几率将小于 1 4 k

  • 前面提到,当被测试数在某一个范围内时,我们可以通过选取适当的测试底数让 M i l l e r R a b i n 素数测试对于这个范围内的每一个数都能够正确地得出结果。下面是节选自 维基百科 的选取 a 的方式。

  • N < 4 , 759 , 123 , 141 ,选取 a = 2 , 7 , 61 即可确保算法得出正确结果。

  • N < 3 , 825 , 123 , 056 , 546 , 413 , 051 3 10 18 ,选取 a = 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 即可确保算法得出正确结果。

  • N < 18 , 446 , 744 , 073 , 709 , 551 , 616 = 2 64 ,选取 a = 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 即可确保算法得出正确结果。

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 100005;
const int p[9] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
typedef long long ll;
typedef long double ld;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
ll times(ll a, ll b, ll P) {
  ll tmp = (ld) a * b / P;
  return ((a * b - tmp * P) % P + P) % P;
}
ll power(ll a, ll b, ll P) {
  if (b == 0) return 1;
  ll tmp = power(a, b / 2, P);
  if (b % 2 == 0) return times(tmp, tmp, P);
  else return times(a, times(tmp, tmp, P), P);
}
bool prime(ll n) {
  for (int i = 0; i <= 8; i++) {
      if (p[i] == n) return true;
      else if (p[i] > n) return false;
      ll tmp = power(p[i], n - 1, n), tnp = n - 1;
      if (tmp != 1) return false;
      while (tmp == 1 && tnp % 2 == 0) {
          tnp /= 2;
          tmp = power(p[i], tnp, n);
          if (tmp != 1 && tmp != n - 1) return false;
      }
  }
  return true;
}
int main() {
  ll n;
  while (scanf("%lld", &n) != EOF) {
      if (n == 1) {
          puts("N");
          continue;
      }
      if (prime(n)) puts("Y");
      else puts("N");
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/82317212
今日推荐