2 条题解

  • 0
    @ 2022-5-29 0:54:06

    本题需要掌握两个算法:

    米勒拉宾算法(至少能快速做到int以内单点素性判断)

    Pollard Rho算法详解(题解主要是要对它进行修改)

    首先,Pollard rho算法的本质是以Rho形通过生日悖论概率原理提高随机枚举的可靠性。 它不止能够处理素因子分解,这一步只是粗略的功能。更准确地说,它能提高每次随机到的数尽可能地满足素数的性质。

    int pollard_rho(int n) {
        // 对n特殊处理,有些初始值是不适合执行pollard rho的,要提前剔除
        // 对于素分解来说,n本身是素数,或者n是2这种就不好。
        // 要知道,一个数如果不是质数,就必然在[2,sqrt(n)]内有因子,或者说有其他素因子。
        loop { // 等价于for(;;)比while true更好的死循环
            int c = Random::randint(1, n - 1);
            auto f = [=](ll x)->int { return (x * x + c) % n; };
            int t = f(0), r = f(f(0));
            while (t != r) {
                int p = g(abs(t - r)); // g是一个将枚举到的一个数映射到解集上的处理。
                if (isprime(p) && isprime(n - p)) return p;
                t = f(t), r = f(f(r));
            }
        }
    }
    

    对于素分解来说,特殊处理和g分别是处理非奇素数和素性,然后g取跟n的gcd:

    int pollard_rho(int n) {
        if (n == 4) return 2;
        if (isprime(n)) return n;
        loop {
            int c = Random::randint(1, n - 1);
            auto f = [=](ll x)->int { return (x * x + c) % n; };
            int t = f(0), r = f(f(0));
            while (t != r) {
                int p = gcd(abs(t - r), n);
                if (p > 1) return p;
                t = f(t), r = f(f(r));
            }
        }
    }
    

    而对于本题来说,两者的差别就是另外的处理了:

    int pollard_rho(int n) {
        int mid = n / 2;
        if (isprime(mid)) return mid;
        loop {
            int c = Random::randint(1, n - 1);
            auto f = [=](ll x)->int { return (x * x + c) % n; };
            int t = f(0), r = f(f(0));
            while (t != r) {
                int p = abs(t - r) % mid * 2 + 1;
                if (isprime(p) && isprime(n - p)) return p;
                t = f(t), r = f(f(r));
            }
        }
    }
    

    完整代码参考:

    #include <bits/stdc++.h>
    using namespace std;
    typedef __uint128_t ulll;
    typedef __int128_t lll;
    typedef unsigned long long ull;
    typedef long long ll;
    #define Unlock
    #ifdef DEBUG
    #define main_end cout << endl;system("pause");return 0
    #else
    #define main_end cout << flush;return 0
    #endif
    #ifdef Unlock
    #define main_first ios::sync_with_stdio(false);cin.tie(nullptr)
    #else
    #define main_first cin.tie(nullptr)
    #endif
    #define gcd __gcd
    #define count_bits __builtin_popcountll
    #define lowbit __builtin_ctzll
    #define log2n __builtin_clzll
    #define loop for(;;)
    constexpr ll mpow(ll z, ll y, ll m) {
        ll ans = 1;
        for (;y;y >>= 1, z = z * z % m)
            if (y & 1)ans = ans * z % m;
        return (ll)ans % m;
    }
    namespace Random {
        mt19937_64 r(chrono::system_clock::now().time_since_epoch().count());
        int randint(int L, int R) {
            return uniform_int_distribution<int>(L, R)(r);
        }
    }
    const int witness[] = { 2, 7, 61 };
    bool isprime(int n) {
        if (n < 2) return false;
        int r = n / ((n - 1) & (1 - n));
        for (int w : witness) {
            if (n % w == 0) return n == w;
            if (mpow(w, r, n) != 1) {
                for (;r < n;r <<= 1) if (mpow(w, r, n) == n - 1) break;
                if (r >= n) return false;
            }
        }
        return true;
    }
    int pollard_rho(int n) {
        int mid = n / 2;
        if (isprime(mid)) return mid;
        loop {
            int c = Random::randint(1, n - 1);
            auto f = [=](ll x)->int { return (x * x + c) % n; };
            int t = f(0), r = f(f(0));
            while (t != r) {
                int p = abs(t - r) % mid * 2 + 1;
                if (isprime(p) && isprime(n - p)) return p;
                t = f(t), r = f(f(r));
            }
        }
    }
    int main() {
        main_first;
        int t; cin >> t;
        for (;t;--t) {
            int n; cin >> n;
            int p = pollard_rho(n);
            cout << p << ' ' << n - p << '\n';
        }
        main_end;
    }
    

    pollard_rho 是优秀的随机数论分解算法,还有带Floyd判环算法的再优化版本,具体参考:Pollard Rho算法详解

    pollard_rho一般都依赖于isprime的性能,这里采用前面提及的米勒拉宾算法来快速计算,因此可以实现1e9甚至更大范围的哥德巴赫分解。

    信息

    ID
    419
    时间
    3000ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    7
    已通过
    2
    上传者