质因数分解

经典算法

2 2 2开始,如果含有该质因子则一直除到不含为止,这样相当于含 2 2 2因子的合数都不会再被整除,类似素筛,那么每次就能保证整除的数一定是这个数的质因数

时间复杂度 O ( n ) O(\sqrt{n}) O(n ),也就是说可以对单个的小于 1 0 14 10^{14} 1014的数进行质因数分解

int fac[maxn],num[maxn];
int cnt;

void solve(int n){
    int m=sqrt(n+0.5);
    cnt=0;
    memset(num,0,sizeof num);
    for(int i=2;i<=m;i++){
        if(n%i==0){
            fac[cnt]=i;
            while(n%i==0){
                num[cnt]++;
                n/=i;
            }
            cnt++;
        }
        if(n==1) break;
    }
    if(n>1){
		fac[cnt]=n;  //这一步一定不要忘记
        num[cnt++]++;
    }
}
Pollard Rho质因数分解

1975年,John M.Pollard提出了第二种因数分解的方法,Pollard Rho快速因数分解。该算法时间复杂度为 O ( n 1 4 ) O(n^{\frac{1}{4}}) O(n41)

分解质因数过程:对 n n n进行分解质因数,应先找到一个最小的质数 k k k,然后按下述步骤完成:

  1. 如果这个质数恰等于 n n n,则说明分解质因数的过程已经结束,打印出即可
  2. 如果 n > k n>k n>k,但 n n n能被 k k k整除,则应打印出 k k k的值,并用 n n n除以 k k k的商作为新的正整数 n n n,重复执行第一步
  3. 如果 n n n不能被 k k k整除,则用 k + 1 k+1 k+1作为 k k k的值,重复执行第一步

原理:

快速对整数进行质因数分解的算法,需要与 M i l l e r − r a b i n Miller-rabin Millerrabin共同使用。求 n n n的质因子的基本过程是,先判断 n n n是否为素数,如果不是则按照一个伪随机数生成过程来生成随机数序列,对于每个生成的随机数判断与 n n n是否互质,如果互质则尝试下一个随机数。如果不互质则将其公因子记作 p p p,递归求解 p p p n / p n/p n/p的因子。如果 n n n是素数则直接返回 n n n为其素因子

至于这个随机数序列是如何生成的暂时还不能理解,而且也是有多种不同的方式。这个序列生成过程中会产生循环,遇到循环则立即退出

struct BigIntegerFactor {
    const static int maxm = 1e6 + 10;
    ll prime[maxm], p[maxm], sz, cnt;
    ll fac[10010], num[10010];

    inline ll mul(ll a, ll b, ll p) {   //wa了尝试下面的慢速乘或者改为__int128
        if (p <= 1000000000) return a * b % p;
        else if (p <= 1000000000000LL) return (((a * (b >> 20) % p) << 20) + (a * (b & ((1 << 20) - 1)))) % p;
        else {
            ll d = (ll) floor(a * (long double) b / p + 0.5);
            ll ret = (a * b - d * p) % p;
            if (ret < 0) ret += p;
            return ret;
        }
    }

    inline ll mul(ll a, ll b, ll Mod) {
        ll ans = 0;
        while (b) {
            if (b & 1) ans = (ans + a) % Mod;
            a = (a + a) % Mod;
            b >>= 1;
        }
        return ans;
    }

    void init(int up) {  //传入的参数不能超过maxm,根据数据范围来定,1e5wa了就改1e6试试
        int tot = 0;
        sz = up - 1;
        for (int i = 1; i <= sz; i++) p[i] = i;
        for (int i = 2; i <= sz; i++) {
            if (p[i] == i) prime[tot++] = i;
            for (int j = 0; j < tot && 1LL * i * prime[j] <= sz; j++) {
                p[i * prime[j]] = prime[j];
                if (i % prime[j] == 0) break;
            }
        }
    }

    inline ll qkp(ll x, ll n, ll p) {
        ll ans = 1;
        while (n) {
            if (n & 1) ans = mul(ans, x, p);
            x = mul(x, x, p);
            n >>= 1;
        }
        return ans;
    }


    inline bool check(ll a, ll n) {
        ll t = 0, u = n - 1;
        while (!(u & 1)) t++, u >>= 1;
        ll x = qkp(a, u, n), xx = 0;
        while (t--) {
            xx = mul(x, x, n);
            if (xx == 1 && x != 1 && x != n - 1) return false;
            x = xx;
        }
        return xx == 1;
    }

    inline bool miller(ll n, ll k) {  //检测一个数n是否为素数,一般k取20即可
        if (n == 2) return true;
        if (n < 2 || !(n & 1)) return false;
        if (n <= sz) return p[n] == n;
        for (int i = 0; i <= k; i++) {
            if (!check(rand() % (n - 1) + 1, n)) return false;
        }
        return true;
    }

    inline ll gcd(ll a, ll b) {
        return b == 0 ? a : gcd(b, a % b);
    }

    inline ll Abs(ll x) {
        return x < 0 ? -x : x;
    }

    inline ll Pollard_rho(ll n) {
        ll s = 0, t = 0, c = rand() % (n - 1) + 1, v = 1, ed = 1;
        while (1) {
            for (int i = 1; i <= ed; i++) {
                t = (mul(t, t, n) + c) % n;
                v = mul(v, Abs(t - s), n);
                if (i % 127 == 0) {
                    ll d = gcd(v, n);
                    if (d > 1) return d;
                }
            }
            ll d = gcd(v, n);
            if (d > 1) return d;
            s = t, v = 1, ed <<= 1;
        }
    }

    void getfactor(ll n) {  //得到有重复的质因子
        if (n <= sz) {
            while (n != 1) fac[cnt++] = p[n], n /= p[n];
            return;
        }
        if (miller(n, 6)) fac[cnt++] = n;
        else {
            ll d = n;
            while (d >= n) d = Pollard_rho(n);
            getfactor(d);
            getfactor(n / d);
        }
    }

    void print(ll x) {   //打印"质因子-个数"
        cnt = 0;
        getfactor(x);
        int k = 1;
        num[0] = 1;
        sort(fac, fac + cnt);
        for (int i = 1; i < cnt; i++) {
            if (fac[i] == fac[i - 1])
                num[k - 1]++;
            else {
                num[k] = 1;
                fac[k++] = fac[i];
            }
        }
        cnt = k;
        for (int i = 0; i < cnt; i++)
            printf("%lld %lld\n", fac[i], num[i]);
    }

    void solve(ll x, ll &tot) {  //进行其他操作
        
    }

} Q;

Q.init(100000); //一般初始化为1e5,wa了尝试1e6
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值