miller-rabin matlab,区块链中的数学-用Miller Rabin算法判断大素数实例

本节从"凭证"的角度来扩展说明了Miller Rabin算法

## 写在前面

上一节说了[抽象的椭圆曲线密钥协商和素数判断法](https://learnblockchain.cn/article/1500), 对于Miller Rabin素数判定法做了简要的概述, 本文再展开说明一下,并附带具体实现如果篇幅允许的话。

## Miller Rabin算法之凭证

结合上一文的说明,该算法利用**费马小定理和二次探测定理**的逆否性质:

把$p-1$分解为$ 2^k *t $ 的形式,即:$ p-1=2^k *t$

如果我们能找到这样一个a,使得对任意 0 < r < = k, 以下两个式子均满足:

![](https://img.learnblockchain.cn/2020/09/27/16011692528549.jpg)

![](https://img.learnblockchain.cn/2020/09/27/16011692742780.jpg)

那么p肯定就不是一个素数。这样的a称为p是合数的一个**凭证(witness)**,否则a可能是一个证明p是素数的“**强伪证”(strong liar)**。

也就是说,当p是一个合数的时候,由于a是一定范围内随机选取的,对某次选取的a来说,上述两个式子不同时满足,这时称为p是基于a的大概率素数【注:1-1/4 = 3/4 (单次素数概率),上文提及】。

每个奇合数n都有很多个对应的凭证a,但并不容易直接找出这些a。当前解决的办法是使用概率性的测试。我们从集合中随机选择数a,然后检测当前的a是否是p为合数的一个凭证。

如果p本身确实是一个合数,那么大部分被选择的a都应该是n的凭证,也即通过这个测试能检测出n是合数的可能性很大[**注:也有极小概率我们找到的a是一个“强伪证”(反而表明了n可能是一个素数]见举例说明**]。

通过多次独立测试不同的a,我们能减少这种出错的概率,这就是**Miller Rabin算法思想的核心**。

对于测试一个大数是否是素数,通用的做法随机选取基数a,毕竟我们并不知道凭证和伪证在 [1, p-1]这个区间如何分布。

典型的例子是 Arnault 曾经给出了一个397位的合数n,但是所有小于307的基数a都是n是素数的“强伪证”。很不幸,如果函数通过检测a = 2,3,5,7,11这几种情况来进行素性检验,会被误判为素数。

所以使用Miller Rabin算法库,错误概率参数尽量设置非常低,即测试次数足够多。

## 实例说明

通过例子,更能清楚理解“强伪证”。

假设需要检验p = 221是否是一个素数。

首先$p - 1 = 220 =2^2 * 55$,于是我们得到了 k = 2和t = 55。我们随机从[1,p-1]中选取a,假设a = 174,于是有:

![](https://img.learnblockchain.cn/2020/09/27/16011695457842.jpg)

因为我们得到了一个-1,所以要么p = 221确实是一个素数,要么a = 174是一个“强伪证”。再尝试选取a=137,于是有:

![](https://img.learnblockchain.cn/2020/09/27/16011696432250.jpg)

可得,a = 137是 n = 221为合数的一个凭证,而a = 174是一个“强伪证”, 综合可得p = 221不是一个素数。

### 代码示例

具体实现代码容易在GitHub上找到,这里贴简单示例,方便直接查看(PC端阅读效果佳)

```

#include

#include

#include

#include

#include

#include

using namespace std;

int number = 0;

mapm;

long long Random( long long n )

//生成[ 0 , n ]的随机数

{

return ((double)rand( ) / RAND_MAX*n + 0.5);

}

long long q_mul( long long a, long long b, long long mod )

{//快速计算 (a*b) % mod

long long ans = 0;

while(b)

{

if(b & 1)

{

b--;

ans =(ans+ a)%mod;

}

b /= 2;

a = (a + a) % mod;

}

return ans;

}

long long q_pow( long long a, long long b, long long mod )

{ //快速幂模运算 (a^b) % mod

long long ans = 1;

while(b)

{

if(b & 1)

{

ans = q_mul( ans, a, mod );

}

b /= 2;

a = q_mul( a, a, mod );

}

return ans;

}

bool witness( long long a, long long n )//miller_rabin算法的核心

{ //用检验算子a来检验n是不是素数

long long tem = n - 1;

int j = 0;

while(tem % 2 == 0)

{

tem /= 2;

j++;

}

//将n-1拆分为a^r * s

long long x = q_pow( a, tem, n );

//得到a^r mod n

if(x == 1 || x == n - 1) return true;

//余数为1则为素数

while(j--) //否则试验条件2看是否有满足的 j

{

x = q_mul( x, x, n );

if(x == n - 1) return true;

}

return false;

}

bool miller_rabin( long long n, int times )

{ //检验n是否是素数

if(n == 2)

return true;

if(n < 2 || n % 2 == 0)

return false; //注:特别大的数使用BigInt

for(int i = 1; i <= times; i++) //做times次随机检验

{

long long a = Random( n - 2 ) + 1;

//得到随机检验算子 a

if(!witness( a, n ))

//用a检验n是否是素数

return false;

}

return true;

}

```

可以看到用到了[快速幂模运算](https://learnblockchain.cn/article/1556)等, 注意对于特别大的数使用BigInt,示例代码仅帮助理解。

## 小结

本节从"凭证"的角度来扩展说明了Miller Rabin算法,结合前文会有更好的理解。判断大素数的方法除了Miller Rabin这种概率性的算法,也有确定的算法,但是效率不高,未能广泛使用,故不再介绍。

有了数论,RSA,椭圆曲线密码学等基础,从下一篇起打算介绍[VRF(随机可验证函数)的原理和在区块链中的应用](https://learnblockchain.cn/article/1495)。

欢迎关注公众号:blocksight

写在前面

上一节说了抽象的椭圆曲线密钥协商和素数判断法, 对于Miller Rabin素数判定法做了简要的概述, 本文再展开说明一下,并附带具体实现如果篇幅允许的话。

Miller Rabin算法之凭证

结合上一文的说明,该算法利用费马小定理和二次探测定理的逆否性质:

把$p-1$分解为$ 2^k t $ 的形式,即:$ p-1=2^kt$

如果我们能找到这样一个a,使得对任意 0 < r < = k, 以下两个式子均满足:

bcf0dfb7c20dc6bea57281516e9bf4f7.png

9ac5e0d43ed7f6a76f34bac7fbad9564.png

那么p肯定就不是一个素数。这样的a称为p是合数的一个凭证(witness),否则a可能是一个证明p是素数的“强伪证”(strong liar)。

也就是说,当p是一个合数的时候,由于a是一定范围内随机选取的,对某次选取的a来说,上述两个式子不同时满足,这时称为p是基于a的大概率素数【注:1-1/4 = 3/4 (单次素数概率),上文提及】。

每个奇合数n都有很多个对应的凭证a,但并不容易直接找出这些a。当前解决的办法是使用概率性的测试。我们从集合中随机选择数a,然后检测当前的a是否是p为合数的一个凭证。

如果p本身确实是一个合数,那么大部分被选择的a都应该是n的凭证,也即通过这个测试能检测出n是合数的可能性很大[注:也有极小概率我们找到的a是一个“强伪证”(反而表明了n可能是一个素数]见举例说明]。

通过多次独立测试不同的a,我们能减少这种出错的概率,这就是Miller Rabin算法思想的核心。

对于测试一个大数是否是素数,通用的做法随机选取基数a,毕竟我们并不知道凭证和伪证在 [1, p-1]这个区间如何分布。

典型的例子是 Arnault 曾经给出了一个397位的合数n,但是所有小于307的基数a都是n是素数的“强伪证”。很不幸,如果函数通过检测a = 2,3,5,7,11这几种情况来进行素性检验,会被误判为素数。

所以使用Miller Rabin算法库,错误概率参数尽量设置非常低,即测试次数足够多。

实例说明

通过例子,更能清楚理解“强伪证”。

假设需要检验p = 221是否是一个素数。

首先$p - 1 = 220 =2^2 * 55$,于是我们得到了 k = 2和t = 55。我们随机从[1,p-1]中选取a,假设a = 174,于是有:

c1fa33e126c26a899813aca84da9846c.png

因为我们得到了一个-1,所以要么p = 221确实是一个素数,要么a = 174是一个“强伪证”。再尝试选取a=137,于是有:

5bcd0cec371b4b039126e03f9be6b115.png

可得,a = 137是 n = 221为合数的一个凭证,而a = 174是一个“强伪证”, 综合可得p = 221不是一个素数。

代码示例

具体实现代码容易在GitHub上找到,这里贴简单示例,方便直接查看(PC端阅读效果佳)

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cmath>

#include <cstring>

#include <map>

using namespace std;

int number = 0;

map<long long, int>m;

long long Random( long long n )

//生成[ 0 , n ]的随机数

{

return ((double)rand( ) / RAND_MAX*n + 0.5);

}

long long q_mul( long long a, long long b, long long mod )

{//快速计算 (a*b) % mod

long long ans = 0;

while(b)

{

if(b & 1)

{

b--;

ans =(ans+ a)%mod;

}

b /= 2;

a = (a + a) % mod;

}

return ans;

}

long long q_pow( long long a, long long b, long long mod )

{ //快速幂模运算 (a^b) % mod

long long ans = 1;

while(b)

{

if(b & 1)

{

ans = q_mul( ans, a, mod );

}

b /= 2;

a = q_mul( a, a, mod );

}

return ans;

}

bool witness( long long a, long long n )//miller_rabin算法的核心

{ //用检验算子a来检验n是不是素数

long long tem = n - 1;

int j = 0;

while(tem % 2 == 0)

{

tem /= 2;

j++;

}

//将n-1拆分为a^r * s

long long x = q_pow( a, tem, n );

//得到a^r mod n

if(x == 1 || x == n - 1) return true;

//余数为1则为素数

while(j--) //否则试验条件2看是否有满足的 j

{

x = q_mul( x, x, n );

if(x == n - 1) return true;

}

return false;

}

bool miller_rabin( long long n, int times )

{ //检验n是否是素数

if(n == 2)

return true;

if(n < 2 || n % 2 == 0)

return false; //注:特别大的数使用BigInt

for(int i = 1; i <= times; i++) //做times次随机检验

{

long long a = Random( n - 2 ) + 1;

//得到随机检验算子 a

if(!witness( a, n ))

//用a检验n是否是素数

return false;

}

return true;

}

可以看到用到了快速幂模运算等, 注意对于特别大的数使用BigInt,示例代码仅帮助理解。

小结

本节从"凭证"的角度来扩展说明了Miller Rabin算法,结合前文会有更好的理解。判断大素数的方法除了Miller Rabin这种概率性的算法,也有确定的算法,但是效率不高,未能广泛使用,故不再介绍。

有了数论,RSA,椭圆曲线密码学等基础,从下一篇起打算介绍VRF(随机可验证函数)的原理和在区块链中的应用。

欢迎关注公众号:blocksight

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

发表于 2020-09-07 18:47

阅读 ( 334 )

学分 ( 9 )

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值