米勒拉宾素数测试 - >大素数判断 + 大整数的因数分解 + 例题 POJ1811

费马小定理:

a为整数,n是素数,且a,n互质,则有a^(n-1)≡1(mod n) ,即:a^(n-1)模n得1。

快速判定一个数是否为素数的方法:

如果存在一个整数a,使得a^(n-1)≡1(mod n)
,则称n为基于a的伪素数,当有多个满足关系的a时,则n为素数的概率趋向于1。所以取多个a测试一下即可。

二次探测定理:

如果n是一个素数,且n>x>0,则方程x²%n=1的解为:x=1或 x=n-1.

证明:
除x=1这个解外,剩下的x满足 n>x>√n , n²>x²>n;
那么则有x²=n+1 2n+1 3n+1……(n-2)n+1 …(n-1)n+1
由于n是素数,所以n不可分解,所以只有(n-2)n+1可化为平方形式,即x=n-1;

pollard rho算法

带有一定的概率性,错判率大概只有4^(-s),s值是自定的; 该算法主要是将大数不断分解为小数;
由费马小定理及伪质数推断可知,如果n是一个正整数,如果存在和n互素的正整数a满足 a^(n-1)≡ 1(mod
n),我们说n是基于a的伪素数。如果一个数是伪素数,那么它几乎肯定是素数,多次查询,则误判几率能降到极低;
所以不断构造随机数x1,找到这样一个因子p,使p=gcd(x1-x2,n);若p==1则构造失败,基于x1不断调整x2,否则p就是a的一个因数,递归继续找p,及n/p;

大素数判断:
Code:

LL mult( LL a , LL b , LL c ) {  //快速乘法
    a %= c ;
    b %= c ;
    LL ret = 0;
    while( b ){
        if( b & 1 ){
            ret = ( ret + a ) % c ; 
        }
        b >>= 1 ;
        a <<= 1 ;
        if( a >= c ) a %= c ;
    }
    return ret;
}

LL quick( LL a , LL b , LL m ){  // 快速幂
    LL res = 1;
    while( b ){
        if( b & 1 ){
            res = mult( res , a , m );
        }
        b >>= 1 ;
        a = mult( a , a , m ) % m ;
    }
    return res ;
}

bool check( LL a, LL n , LL x , LL t ){
    LL ret = quick( a , x , n );
    LL last = ret ;
    for( int i = 1 ; i <= t ; i++ ){
        ret = mult( ret , ret , n ) ;
        if( ret == 1 && last != 1 && last != n - 1 ) return true;
        last = ret ;
    }
    if( ret != 1 ) return true ;
    return false;
}

bool Miller_Rabin( LL n ){
    if( n < 2  ) return false;
    if( n == 2 ) return true; 
    if( !( n & 1 ) ) return false;
    LL x = n - 1;
    LL t = 0 ;
    while( !(x & 1) ){
        x >>= 1;
        t ++ ;
    }
    srand(time(NULL));
    for( int i = 0 ; i < S ; i++ ){  // S为自己设定的值,一般8-10次即可
        LL a = rand() % ( n - 1 ) + 1;
        if( check( a , n , x , t ) ) return false;
    }
    return true;
}

大整数的因数分解: (可以分解小于2 ^ 63的大整数)
Code:

const int S = 10;

LL mult( LL a , LL b , LL c ) {
    a %= c ;
    b %= c ;
    LL ret = 0;
    while( b ){
        if( b & 1 ){
            ret = ( ret + a ) % c ; 
        }
        b >>= 1 ;
        a <<= 1 ;
        if( a >= c ) a %= c ;
    }
    return ret;
}

LL quick( LL a , LL b , LL m ){
    LL res = 1;
    while( b ){
        if( b & 1 ){
            res = mult( res , a , m );
        }
        b >>= 1 ;
        a = mult( a , a , m ) % m ;
    }
    return res ;
}

bool check( LL a, LL n , LL x , LL t ){
    LL ret = quick( a , x , n );
    LL last = ret ;
    for( int i = 1 ; i <= t ; i++ ){
        ret = mult( ret , ret , n ) ;
        if( ret == 1 && last != 1 && last != n - 1 ) return true;
        last = ret ;
    }
    if( ret != 1 ) return true ;
    return false;
}

bool Miller_Rabin( LL n ){
    if( n < 2  ) return false;
    if( n == 2 ) return true; 
    if( !( n & 1 ) ) return false;
    LL x = n - 1;
    LL t = 0 ;
    while( !(x & 1) ){
        x >>= 1;
        t ++ ;
    }
    srand(time(NULL));
    for( int i = 0 ; i < S ; i++ ){
        LL a = rand() % ( n - 1 ) + 1;
        if( check( a , n , x , t ) ) return false;
    }
    return true;
}

LL factor[100];
int tot ;
LL gcd( LL a , LL b){  //这样gcd可以求负数
    if( !a ) return 1;
    if( a < 0 ) return gcd( -a , b );
    while( b ){
        LL t = a % b ;
        a = b ;
        b = t ;
    }
    return a;
}

LL pollard_rho( LL x , LL c ){
    LL i = 1 , k = 2 ;
    srand(time(NULL));
    LL x0 = rand() % (x-1) + 1 ;
    LL y = x0;
    while(1){

        i ++;
        x0 = ( mult( x0 , x0 , x ) + c ) % x;
        LL d = gcd( y - x0 , x );
        if( d != 1 && d != x ) return d;
        if( y == x0 ) return x;
        if( i == k ){
            y = x0 ;
            k += k;
        }
    }
}

void findfac( LL n , int k ){
    if( n == 1 ) return;
    if( Miller_Rabin(n) ){
        factor[tot++] = n ;
        return ;
    }
    LL p = n ;
    int c = k ;
    while( p >= n ) p = pollard_rho( p , c-- );

    findfac( p , k );
    findfac( n / p , k );
}

例题: POJ1811
题意:判断一个数是否是素数,不是就输出它的最小的素因数
注意:这题交G++ RE的话交C++。
Code:

#include 
#include 
#include 
#include 
#include 
#define LL long long 
using namespace std;
const int S = 10;

LL mult( LL a , LL b , LL c ) {
    a %= c ;
    b %= c ;
    LL ret = 0;
    while( b ){
        if( b & 1 ){
            ret = ( ret + a ) % c ; 
        }
        b >>= 1 ;
        a <<= 1 ;
        if( a >= c ) a %= c ;
    }
    return ret;
}

LL quick( LL a , LL b , LL m ){
    LL res = 1;
    while( b ){
        if( b & 1 ){
            res = mult( res , a , m );
        }
        b >>= 1 ;
        a = mult( a , a , m ) % m ;
    }
    return res ;
}

bool check( LL a, LL n , LL x , LL t ){
    LL ret = quick( a , x , n );
    LL last = ret ;
    for( int i = 1 ; i <= t ; i++ ){
        ret = mult( ret , ret , n ) ;
        if( ret == 1 && last != 1 && last != n - 1 ) return true;
        last = ret ;
    }
    if( ret != 1 ) return true ;
    return false;
}

bool Miller_Rabin( LL n ){
    if( n < 2  ) return false;
    if( n == 2 ) return true; 
    if( !( n & 1 ) ) return false;
    LL x = n - 1;
    LL t = 0 ;
    while( !(x & 1) ){
        x >>= 1;
        t ++ ;
    }
    srand(time(NULL));
    for( int i = 0 ; i < S ; i++ ){
        LL a = rand() % ( n - 1 ) + 1;
        if( check( a , n , x , t ) ) return false;
    }
    return true;
}

LL factor[100];  // 存储因数,无序
int tot ;
LL gcd( LL a , LL b){         //这样gcd可以求负数
    if( !a ) return 1;
    if( a < 0 ) return gcd( -a , b );
    while( b ){
        LL t = a % b ;
        a = b ;
        b = t ;
    }
    return a;
}

LL pollard_rho( LL x , LL c ){
    LL i = 1 , k = 2 ;
    srand(time(NULL));
    LL x0 = rand() % (x-1) + 1 ;
    LL y = x0;
    while(1){

        i ++;
        x0 = ( mult( x0 , x0 , x ) + c ) % x;
        LL d = gcd( y - x0 , x );
        if( d != 1 && d != x ) return d;
        if( y == x0 ) return x;
        if( i == k ){
            y = x0 ;
            k += k;
        }
    }
}

void findfac( LL n , int k ){
    if( n == 1 ) return;
    if( Miller_Rabin(n) ){
        factor[tot++] = n ;
        return ;
    }
    LL p = n ;
    int c = k ;
    while( p >= n ) p = pollard_rho( p , c-- );

    findfac( p , k );
    findfac( n / p , k );
}

int main(){
    int T;
    scanf("%d",&T);
    LL n ;
    while( T-- ){
        scanf("%lld",&n);
        if( Miller_Rabin(n) ){
            printf("Prime\n");
        }else{
            tot = 0 ;
            findfac( n , 107 ) ;
            LL ans = factor[0];
            for( int i = 1 ; i < tot ; i++ ){
                ans = min( ans , factor[i] );
            }
            printf("%lld\n",ans);
        }
    }
    return 0 ;
}

你可能感兴趣的:(快速幂,数论,数学-公式,大数)