Pollard-Rho 素性测试

概念讲解

Miller-Rabin 素性测试

用于判断一个大数是否是素数。

Pollard-Rho 算法

用于分解大整数的因数。

实现

Miller-Rabin 素性测试

12345678910111213141516171819202122232425262728293031323334353637383940414243long long quickPowerMod(long long a, long long n, long long mod) {
    long long result = 1;
    a %= mod;
    while (n > 0) {
        if (n % 2 == 1) result = result * a % mod;
        a = a * a % mod;
        n /= 2;
    }
    return result;
}

bool millerRabin(long long n) {
    if (n < 2) return false;
    if (n == 2 || n == 3) return true;
    if (n % 2 == 0) return false;
    
    long long d = n - 1;
    int r = 0;
    while (d % 2 == 0) {
        d /= 2;
        r++;
    }
    
    for (int a : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}) {
        if (a >= n) continue;
        
        long long x = quickPowerMod(a, d, n);
        if (x == 1 || x == n - 1) continue;
        
        bool found = false;
        for (int i = 0; i < r - 1; i++) {
            x = x * x % n;
            if (x == n - 1) {
                found = true;
                break;
            }
        }
        
        if (!found) return false;
    }
    
    return true;
}

Pollard-Rho 算法

12345678910111213141516171819202122232425262728293031long long pollardRho(long long n) {
    if (n % 2 == 0) return 2;
    
    long long x = rand() % (n - 2) + 2;
    long long y = x;
    long long c = rand() % (n - 1) + 1;
    long long d = 1;
    
    while (d == 1) {
        x = (x * x % n + c) % n;
        y = (y * y % n + c) % n;
        y = (y * y % n + c) % n;
        d = __gcd(abs(x - y), n);
        
        if (d == n) return pollardRho(n);
    }
    
    return d;
}

void factorize(long long n, vector<long long>& factors) {
    if (n == 1) return;
    if (millerRabin(n)) {
        factors.push_back(n);
        return;
    }
    
    long long d = pollardRho(n);
    factorize(d, factors);
    factorize(n / d, factors);
}

考点分析

初赛考查方式

  1. 选择题: 素性测试的概念
  2. 程序阅读: 理解 Miller-Rabin
  3. 编程题: 大数分解

常见题型

  • 大数素性判断
  • 大数因数分解

例题精讲

例题 1:大数素性判断

题目: 判断一个大数是否是素数。

解答:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960#include <iostream>
#include <cstdlib>
using namespace std;

long long quickPowerMod(long long a, long long n, long long mod) {
    long long result = 1;
    a %= mod;
    while (n > 0) {
        if (n % 2 == 1) result = result * a % mod;
        a = a * a % mod;
        n /= 2;
    }
    return result;
}

bool millerRabin(long long n) {
    if (n < 2) return false;
    if (n == 2 || n == 3) return true;
    if (n % 2 == 0) return false;
    
    long long d = n - 1;
    int r = 0;
    while (d % 2 == 0) {
        d /= 2;
        r++;
    }
    
    for (int a : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}) {
        if (a >= n) continue;
        
        long long x = quickPowerMod(a, d, n);
        if (x == 1 || x == n - 1) continue;
        
        bool found = false;
        for (int i = 0; i < r - 1; i++) {
            x = x * x % n;
            if (x == n - 1) {
                found = true;
                break;
            }
        }
        
        if (!found) return false;
    }
    
    return true;
}

int main() {
    long long n;
    cin >> n;
    
    if (millerRabin(n)) {
        cout << n << " 是素数" << endl;
    } else {
        cout << n << " 不是素数" << endl;
    }
    
    return 0;
}

练习题

  1. 洛谷 P3383 Miller-Rabin 素性测试
查看答案

思路:使用 Miller-Rabin 素性测试判断大数是否为素数。

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859#include <iostream>
using namespace std;

long long quickPowerMod(long long a, long long n, long long mod) {
    long long result = 1;
    a %= mod;
    while (n > 0) {
        if (n % 2 == 1) result = result * a % mod;
        a = a * a % mod;
        n /= 2;
    }
    return result;
}

bool millerRabin(long long n) {
    if (n < 2) return false;
    if (n == 2 || n == 3) return true;
    if (n % 2 == 0) return false;
    
    long long d = n - 1;
    int r = 0;
    while (d % 2 == 0) {
        d /= 2;
        r++;
    }
    
    for (int a : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}) {
        if (a >= n) continue;
        
        long long x = quickPowerMod(a, d, n);
        if (x == 1 || x == n - 1) continue;
        
        bool found = false;
        for (int i = 0; i < r - 1; i++) {
            x = x * x % n;
            if (x == n - 1) {
                found = true;
                break;
            }
        }
        
        if (!found) return false;
    }
    
    return true;
}

int main() {
    int n, m;
    cin >> n >> m;
    
    while (m--) {
        long long x;
        cin >> x;
        cout << (millerRabin(x) ? "Yes" : "No") << endl;
    }
    
    return 0;
}
  1. 洛谷 P4718 Pollard-Rho
查看答案

思路:使用 Pollard-Rho 算法分解大整数。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;

long long quickPowerMod(long long a, long long n, long long mod) {
    long long result = 1;
    a %= mod;
    while (n > 0) {
        if (n % 2 == 1) result = (__int128)result * a % mod;
        a = (__int128)a * a % mod;
        n /= 2;
    }
    return result;
}

bool millerRabin(long long n) {
    if (n < 2) return false;
    if (n == 2 || n == 3) return true;
    if (n % 2 == 0) return false;
    
    long long d = n - 1;
    int r = 0;
    while (d % 2 == 0) {
        d /= 2;
        r++;
    }
    
    for (int a : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}) {
        if (a >= n) continue;
        
        long long x = quickPowerMod(a, d, n);
        if (x == 1 || x == n - 1) continue;
        
        bool found = false;
        for (int i = 0; i < r - 1; i++) {
            x = (__int128)x * x % n;
            if (x == n - 1) {
                found = true;
                break;
            }
        }
        
        if (!found) return false;
    }
    
    return true;
}

long long pollardRho(long long n) {
    if (n % 2 == 0) return 2;
    
    long long x = rand() % (n - 2) + 2;
    long long y = x;
    long long c = rand() % (n - 1) + 1;
    long long d = 1;
    
    while (d == 1) {
        x = ((__int128)x * x % n + c) % n;
        y = ((__int128)y * y % n + c) % n;
        y = ((__int128)y * y % n + c) % n;
        d = __gcd(abs(x - y), n);
        
        if (d == n) return pollardRho(n);
    }
    
    return d;
}

long long maxFactor = 0;

void factorize(long long n) {
    if (n == 1) return;
    if (millerRabin(n)) {
        maxFactor = max(maxFactor, n);
        return;
    }
    
    long long d = pollardRho(n);
    factorize(d);
    factorize(n / d);
}

int main() {
    int n;
    cin >> n;
    
    while (n--) {
        long long x;
        cin >> x;
        
        if (millerRabin(x)) {
            cout << "Prime" << endl;
        } else {
            maxFactor = 0;
            factorize(x);
            cout << maxFactor << endl;
        }
    }
    
    return 0;
}
  1. 洛谷 P1075 质因数分解
查看答案

思路:试除法分解质因数。

12345678910111213141516#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            cout << n / i << endl;
            break;
        }
    }
    
    return 0;
}

易错点

⚠️ 注意:

  1. Miller-Rabin 要选择合适的测试基数
  2. Pollard-Rho 要注意随机化
  3. 大数分解要注意溢出
  4. Pollard-Rho 是数论的高级内容