真的是很水的小讲解
以前写的假的 Miller_Rabin 都是多用了几个数去判费马,虽然正确几率变大了,但是面对 lld 的数据还是无能为力,所以只好去学了真的 Miller_Rabin
Miller Rabin的搞法应该是多次费马小定理判断,然而很多时候是不行的,因此我们融合二次探测和费马小定理来进行判断
费马小定理:
ap−1=1(modp)
如果p为素数一定满足,当然p为合数也有可能满足
二次探测:
x2=1(modp)
的解在mod p的剩余系中只有
x=1
和
x=p−1
所以我们先把要判断的数的所有的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;
}