素数测试

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言:两种高效的求mod方法


一、(a*b)%n

代码如下:

public long multiple(long a,long b,long n){
        long ans = 0;
        while(b > 0){
            if((b & 1) == 1){
                ans = (ans+a)%n;
            }
            a = (a + a)%n;
            b>>=1;
        }
        return ans;
    }

其实主要就是把b化成二进制表示,遇到为1的位就对结果加a,否则丢弃当前的最低位。因为右移了一位,下一次加的a就变成2*a了,用加法会更快一些。但是为什么变成2a之后可以取模,是我不理解的地方。

二、(a*b)%n

代码如下:

//幂运算要借助到之前的乘法运算
    public long power(long a,long b,long n){//b个a相乘,把个数b化成2的多项式的形式,
        // 每次递归求出二项式的每一项,再通过位是否为1判断需不需要这一项
        long ans = 1;
        while(b > 0){
            if((b & 1) == 1){
                ans = multiple(ans,a,n);//(ans * a)mod n
            }
            a = multiple(a,a,n);//(a * a)mod n
            b >>= 1;
        }
        return ans;
    }

multiple(a,a,n)就是求多项式的某一项的值,multiple(ans,a,n)就是把这一项加到总和里。

Miller-Rabin测试

先摆三条定理

费马小定理:对于素数p和任意整数a,有a^p ≡ a (mod p)(同余)。
反过来,满足a^p ≡ a(mod p),p也几乎一定是素数。

伪素数:如果n是一个正整数,如果存在和n互素的正整数a满足 a^(n-1) ≡ 1(mod n),
我们说n是基于a的伪素数。如果一个数是伪素数,那么它几乎肯定是素数。

Miller-Rabin测试:不断选取不超过n-1的基b(s次),计算是否每次都有b^(n-1) ≡ 1(mod n),
若每次都成立则n是素数,否则为合数。

	    之所以说几乎,因为MIller-Rabin测试是概率型的,不是确定型的,不过由于多次运行后
	出错的概率非常小,所以实际应用还是可行的。(一次Miller-Rabin测试其成功的概率为
	3/4)。

再来一条定理

二次探测定理:如果p是奇素数,则 x^2 ≡ 1(mod p)的解为 x = 1 || x = p - 1(mod p);

代码如下:

public boolean miller_rabin(int n){
        if(n==2 || n==3 || n==5 || n==7 || n==11) return true;
        if(n==1 || (n%2)==0 ||  (n%3)==0 ||  (n%5)==0 ||  (n%7)==0 ||  (n%11)==0) return false;
        long x,pre,u;
        int offset=0;
        u=n-1;
        while((n&1) != 0){
            offset++;
            n>>=1;
        }
        //去掉最低位1后面的0,为什么?减少x^n-1的计算量
        //x的32次方要算32次,而去掉0算再乘回去只要算5次
        //那x的41次方呢?101001
        Random random = new Random();
        //不断选取不超过n-1的x(s次),计算是否每次都有x^(n-1) ≡ 1(mod n),
        //若每次都成立则n是素数,否则为合数
        for(int i=0;i<8;i++){
            x = random.nextInt() % (n-2) + 2;//2到n的随机数
            if(x % n == 0) continue;
            x = power(x,u,n);//计算x的n-1次方
            pre = x;//存下来
            for(int j = 0; j < offset; ++j) {    //把移位减掉的量补上,并在这地方加上二次探测
                x = multiple(x, x, n);
                //pre=x,x=x*x
                if(x == 1 && pre != 1 && pre != n-1)
                    return false;
                //二次探测定理,这里如果x = 1则pre 必须等于 1或 n-1,否则可以判断不是素数
                pre = x;
            }
            if(x != 1)    return false;    //费马小定理
        }
        return true;
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星辰的野望

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值