判断是否是素数

几个常用的sqrt(n)复杂度的就不说了。
对于一个 longlong 范围或者更大的数,怎么快速判断一个数是不是素数,就要用到Miller_Rabin算法.
立用a^(n-1)=1(mod n) 怎么来的就不解释了,有兴趣的同学可以看看算法导论P566有详细推导。
在这个的基础上用 随机数进行测试(直接用的话会有一些伪素数)。
里面 a用随机数随机, (n-1) 写成 2^r*s 为啥这么写我也不知道,反正大家都是这么写的 qaq
然后就根据上面那个判断就行了
这个算法有时候会出错,出错的概率差不多是 2^-循环次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
inline LL ksc(LL x,LL n,LL mod) {
    LL res=0;
    while(n>0) {
        if(n&1)res=(res+x)%mod;
        x=(x+x)%mod;
        n>>=1;
    }
    return res%mod;
}

inline LL ksm(LL x,LL n,LL mod) {
    LL res=1;
    while(n>0) {
        if(n&1)res=ksc(res,x,mod);
        x=ksc(x,x,mod);
        n>>=1;
    }
    return res%mod;
}
bool check(LL x) { //Miller_Rabin算法,判断n是否为素数
    for(int i=0; i<50; i++) { 
        int a=rand()%(x-1)+1,k=0;
        LL t=x-1;
        if(ksm(a,x-1,x)!=1)return 0;
        while(t&1==0) {
            ++k;
            t>>=1;
        }
        LL u=ksm(a,t,x),l=u;
        for(int i=1; i<=k; i++) {
            u=ksc(u,u,x);
            if(u==1&&l!=1&&l!=x-1)return 0;
            l=u;
        }
    }
    return 1;
}

 

质因子分解

pollard_rho算法,这个算法十分玄学,找到一个数x0 ,然后用一个玄学递推得到 x1=x0*x0 + 一个随机数 然后用两个的差值去和 n做 gcd 然后得出来如果是1就继续找,如果不是1 不就是个因子吗? (个人理解有点像直接随机一个数和他做GCD有没有公约数。。。。)

补充说明一下 x=(x*x +c)%n 是一个滚循环 像ρ所以称为pollard_rho算法,所以要判断循环结 用 floyd 判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
LL pollard_rho(LL n, LL c) {
    LL i = 1, k = 2;
    LL x = rand() % (n - 1) + 1;
    LL y = x;
    while (1) {
        i++;
        x = (ksc(x, x, n) + c) % n; //玄学递推
        LL d = __gcd((y - x + n) % n, n);
        if (1LL < d && d < n) {  //如果有因子就直接返回
            return d;
        }
        if (y == x) {   //如果找到了循环节就跳出
            return n;
        }
        if (i == k) {       //空间 o1 判断循环节用的 ,看不懂你没救了
            y = x;
            k <<= 1;
        }
    }
}

LL fac[100],ct;
void find(LL n, int c) {
    if (n == 1) {
        return;
    }
    if (check(n)) {
        fac[ct++] = n; //是个质因子
        return;
    }
    LL p = n;
    LL k = c;
    while (p >= n) {
        p = pollard_rho(p, c--);    //如果是合数总会找到一个因子
    }
    find(p, k); //继续找
    find(n / p, k);
}