• LOJ #143. 质数判定 题解


    CSDN同步

    原题链接

    简要题意:

    给定 (T) 个数 (n),判素数。

    (T leq 10^5 , n leq 10^{18}).

    可能你判一个都有困难是不是 (cdots cdots).

    二次探测定理

    (x^2 equiv 1 pmod p , x < p),则 (x= 1)(x = p-1).

    简单证明:

    [x^2 equiv 1 pmod p ]

    [(x-1)(x+1) equiv 0 pmod p ]

    没了。

    ( ext{Miller - Rabin}) 算法

    如何快速测试一个数是否为素数?这是基于 二次探测定理 的。

    大家一定知道 伪素数 吧!就是那些 费马小定理逆定理的反例,以其中最小的 (341) 为例,先以 (2) 为底:

    (2^{340} equiv 1 pmod {341}),第一次通过。

    (2^{170} equiv 1 pmod {341}),第二次通过。

    (2^{85} equiv 32 pmod {341}),说明 (341) 不是素数。当然如果这一次通过则说明 (341) 通过了 (2) 的底数检测,因为 (85) 是奇数。

    当然同样的道理,我们可以以其它素数为底进行判断。这样的成功效率是多少呢?

    假设你判断了 (k) 个素数,那么错误的概率是 (4^{-k}),实际上和 (0) 也差不多。

    只用 (2,7,61) 进行测试尽可以保证 (4 759 123 141) 之内正确。

    如果你用 (2,3,5,7,11,13,17) 进行测试,可以保证 (341 550 071 728 320) 之内所有数正确。

    如果选用 (2, 3, 7, 61,24251) 作为底数,(10^{16}) 之内就存在一个反例:(46856248255981).(当然是 (10^{16}) 之内唯一的反例,说明了正确的概率之高)

    这题是 (10^{18}),本人采用 (2,3,5,7,11,61,24251) 可以通过。(当然你也可以用合数进行测试,但合数效率不高)另外本人还添加了 (10) 组随机测试(即随机生成底数进行测试),保证了正确性。

    时间复杂度:(mathcal{O}(T log^2 n)). 错误概率:(4^{-k}).

    实际得分:(100pts).

    细节:

    快速幂中的相乘会溢出,本人的编译器不知道怎么 typedef __int128 ll 会编译错误,所以用了 long double 进行乘法的转移。(不是龟速乘啊)

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    inline ll read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	ll x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}
    
    inline void write(int x) {
    	if(x<0) {putchar('-');write(-x);return;}
    	if(x<10) {putchar(char(x%10+'0'));return;}
    	write(x/10);putchar(char(x%10+'0'));
    }
    ll prime[8]={2,3,5,7,11,61,24251};
    inline ll times(ll x,ll y,ll MOD) {
    	x%=MOD; y%=MOD;
    	ll t=(long double) x/MOD*y;
    	ll p=x*y-t*MOD;
    	return ((p%MOD)+MOD)%MOD;
    } //计算 x*y
    
    inline ll pw(ll x,ll y,ll MOD) {
    	ll ans=1; while(y) {
    		if(y&1) ans=times(ans,x,MOD);
    		x=times(x,x,MOD); y>>=1;
    	} return ans;
    } //快速幂
    
    inline bool check(ll x,ll y,ll MOD) {
    	ll p=pw(x,y,MOD);
    	if(p!=1 && p!=MOD-1) return 0; //测试失败
    	if(p==MOD-1 || (p==1 && (y&1))) return 1; //测试成功
    	return check(x,y>>1,MOD); //继续测试
    }
    
    inline bool Miller_Rabin(ll n) {
    	if(n<=1) return 0;
    	for(int i=0;i<7;i++) {
    		if(n==prime[i]) return 1;
    		if(n%prime[i]==0) return 0;
    		if(!check(prime[i],n-1,n)) return 0; // 7 个素数轮流测试
    	} for(int i=1;i<=10;i++) {
    		int t=2+rand()%(n-2);
    		if(!check(t,n-1,n)) return 0; // 10 个随机
    	} return 1;
    }
    
    int main() {
    	ll x; while(~scanf("%lld",&x)) puts(Miller_Rabin(x)?"Y":"N"); //套路
    	return 0;
    }
    
    
    
  • 相关阅读:
    React组件的生命周期
    机器人api
    智能机器人
    已复制该虚拟机 之后需要的处理工作
    在虚拟机上安装CentOS6.5(minimal)
    fastdfs
    redis安装
    FtpClient上传文件异常:java.net.SocketException: Connection reset
    redis的学习笔记
    git命令2
  • 原文地址:https://www.cnblogs.com/bifanwen/p/13340513.html
Copyright © 2020-2023  润新知