真的Miller_Rabin和真的Pollard_Rho小讲解

真的是很水的小讲解


以前写的假的 Miller_Rabin 都是多用了几个数去判费马,虽然正确几率变大了,但是面对 lld 的数据还是无能为力,所以只好去学了真的 Miller_Rabin


Miller Rabin的搞法应该是多次费马小定理判断,然而很多时候是不行的,因此我们融合二次探测和费马小定理来进行判断


费马小定理: ap1=1(modp) 如果p为素数一定满足,当然p为合数也有可能满足
二次探测: x2=1(modp) 的解在mod p的剩余系中只有 x=1 x=p1


所以我们先把要判断的数的所有的2去除,然后判断去除了之后的平方mod p的结果,如果为1并且x mod p 的结果不为1或者p - 1,则说明这是一个合数,最后我们再判断一下费马小定理即可,貌似也不是特别难


如下是假的miller_rabin,其实已经表现得比较优秀了

long long fast_pow( long long BASE, long long pr, long long mod ) {
    long long x = 1, y = BASE;
    while( pr ) {
        if( pr & 1 ) x = x * y % mod;
        pr >>= 1;
        y = y * y % mod;
    }
    return x % mod;
}
bool miller_rabin( int n ) {
    if( n == 2 || n == 3 || n == 5 || n == 7 ) return true;
    if( n % 2 == 0 ) return false;
    for( register int i = 1; i <= 4; i++ ){
        long long y = a[i];
        long long x = fast_pow( y, n-1, n );
        if( x != 1 ) return false;
    }
    return true;
}

如下是真的Miller Rabin

long long fast_mul( long long a, long long b, long long mod ) {
    long long ret = 0;
    a %= mod;
    while( b ){
        if ( b & 1 ) ret = ( ret + a ) % mod;
        b >>= 1;
        a = ( a + a ) % mod;
    }
    return ret;
}

long long fast_pow( long long a, long long b, long long mod ) {
    long long BASE = a, tmp = 1;
    while( b ) {
        if( b & 1 ) tmp = fast_mul( BASE, tmp, mod );
        BASE = fast_mul( BASE, BASE, mod );
        b >>= 1;
    }
    return tmp;
}

bool miller_rabin(long long n){
    if ( n == 2 || n == 3 ) return true;
    if ( n % 2 == 0 ) return false;

    long long d = n - 1LL;
    int s = 0;
    while( d % 2 == 0 ) s++, d >>= 1;

    for(int i = 0;i < 10; i++ ) {
        long long a = rand() % ( n - 3 ) + 2;
        long long x = fast_pow( a, d, n );
        long long y = 0;
        for(int j = 0; j < s; j++ ) {
            y = fast_mul( x, x, n );
            if ( y == 1 && x != 1 && x != n - 1 ) return false;
            x = y;
        }
        if ( y != 1 ) return false;
    }
    return true;
}

瞬间感觉好害怕啊,如果数据是int的,我还是选择前者


Pollard Rho大数质因子分解法其实是源于一个神奇的生日悖论,大概是讲如果一个班有23个人,那么几乎是一定会有两个人的生日相同,当时就被震惊到了,这么奇葩的言论竟然用在计算机科学上,这么玄学的随机数算法竟然会用来搞long long范围的素数判断
如果我们对于一个大数n,猜测a是其因数,猜中的几率很小,但是如果枚举两个数a,b,看gcd((a-b+n)% n,n)是不是为1,如果不是的话那说明这个gcd值一定为n的一个因子,我们rand k 个在n的剩余系中的数,然后两两判断,找到因子的概率会非常高,不要问我为什么,如果你搞懂了为什么一个班有23个人以上的话,几乎一定是有两个人是同一天生日之后给我讲讲,所以这就是我们的算法,然后不断地分解就可以了,配合miller rabin判断当前的数是不是素数,效率大大增加
貌似没有讲得太清楚,如果有不懂的朋友请联系我的邮箱


PoJ 1811

#include <cstdio>
#include <algorithm>
#include <ctime>
#include <cstdlib>
using namespace std;

long long fast_mul( long long a, long long b, long long mod ) {
    long long ret = 0;
    a %= mod;
    while( b ){
        if ( b & 1 ) ret = ( ret + a ) % mod;
        b >>= 1;
        a = ( a + a ) % mod;
    }
    return ret;
}

long long fast_pow( long long a, long long b, long long mod ) {
    long long BASE = a, tmp = 1;
    while( b ) {
        if( b & 1 ) tmp = fast_mul( BASE, tmp, mod );
        BASE = fast_mul( BASE, BASE, mod );
        b >>= 1;
    }
    return tmp;
}

bool miller_rabin(long long n){
    if ( n == 2 || n == 3 ) return true;
    if ( n % 2 == 0 ) return false;
    //将n分解为2^s*d
    long long d = n - 1LL;
    int s = 0;
    while( d % 2 == 0 ) s++, d >>= 1;

    for(int i = 0;i < 10; i++ ) {
        long long a = rand() % ( n - 3 ) + 2;
        long long x = fast_pow( a, d, n );
        long long y = 0;
        for(int j = 0; j < s; j++ ) {
            y = fast_mul( x, x, n );
            if ( y == 1 && x != 1 && x != n - 1 ) return false;
            x = y;
        }
        if ( y != 1 ) return false;
    }
    return true;
}

long long prime[100];
int cnt;

long long gcd(long long a,long long b){ return b == 0 ? a : gcd( b, a % b ); }
long long pollard_rho( long long n, long long c ) {
    long long i = 1, k = 2;
    long long x = rand() % n;
    long long y = x;
    while( 1 ) {
        i++; long long d;
        x = ( fast_mul( x, x, n ) + c ) % n;
        d = gcd( ( y - x + n ) % n , n );
        if( d != 1LL && d != n ) return d;
        if( y == x ) return n;
        if( i == k ) y = x, k <<= 1;
    }
}

void find( long long n ) {
    if( 4LL == n ) {
        prime[0] = prime[1] = 2LL;
        cnt = 2;
        return; 
    }
    if( miller_rabin( n ) ) {
        prime[ cnt++ ] = n;
        return;
    }
    long long p;
    while( ( p = pollard_rho( n, rand() % ( n - 3 ) + 3 ) ) == n );

    find( p );
    find( n / p );
}
int main() { int T;
    scanf( "%d", &T );
    while( T-- ) { long long n;
        scanf( "%lld", &n );

        cnt = 0;
        find(n);

        if( prime[0] == n ) printf( "Prime\n" );
        else { long long ans = 21474836474747; 
            for( register int i = 0; i < cnt; i++ ) ans = min( ans, prime[i] );
            printf( "%lld\n", ans );
        }
    }
    return 0;
} 

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值