因为最开始的计算机算法都是为了解决数学问题,因此我们首先介绍一些很基本的涉及数学运算的算法,其中会涉及一些初等数论知识。这一章的核心可以归结为以下两个问题,1.求一个数的素数分解 2.判断一个数是否是素数。
模运算:
基本的四则运算相信大家都应该比较熟悉,就不再赘述。这里主要用到的是整数的模运算,这是数论里最基本的一个运算,有很多的应用场景,本章提到的算法也都和模运算有关。如下:
定义: 设 x,N 均为整数,若x=q*N+r,其中0<=r<N,q和r均为整数,则r称为x对N的模,简写为x mod N 。
如果x mod N==y mod N,我们简写成x==y(mod N),接下来我们考虑在模运算下的基本运算法则,首先引入模运算的一个基本定理:
替换准则:
如果x==x’(mod N),y==y'(mod N),则x+y==x'+y'(mod N), xy==x'y'(mod N)
有了上述准则,我们可以很方便的进行大数的求模运算,如下
2345==(25)69==3269==169==1 (mod 31)
考虑对xy mod N,在x,y,N都很大的情况下,运算量非常大,考虑常规的计算顺序:
x mod N --> x2 mod N -->x3 mod N -->...... --> xy mod N ,
如果y有500 bits,那么上述计算总共需要2500次乘法运算。我们可以用一种更聪明的计算序列,
x mod N --> x2 mod N -->x4 mod N -->x8 mod N -->...... --> xy mod N ,
这样,只需要进行500次乘法运算即可求出结果。
但是上述准则只给出了在模运算下进行加法和乘法的运算法则,那减法和除法呢,首先考虑减法,其实和普通的四则运算一样,减去一个数相当于加上这个数的相反数,这样减法即可用加法来表示,如下:
5==25(mod 10),14==4(mod 10) ,
则5-14(mod 10)==-9(mod 10)==1(mod 10)==21(mod 10)
==25-4(mod 10)
值得注意的是,虽然加减法和乘法在模运算下都成立,但是除法在模运算下并不成立。
既然有了模运算这个工具 ,那肯定得给它找点用武之地了,接下来我们开始用这个工具解决几个问题,最著名的可能就是求两个整数的最大公约数(gcd)了:
欧几里德准则:
如果x和y 均是正整数并且x>=y,那么gcd(x,y)==gcd(y,x mod y)。
为什么呢,可以想一下,如果r是上述准则中x和y的最大公约数,那么x==ar,y=br,则x-y=(a-b)r,可作简单推倒如下:
gcd(x,y)=gcd(x-y,y)=gcd(x-2y,y)....... =gcd(x mod y,y)=gcd(y,x mod y);
有了上述准则,算法岂不是很容易了,为了简单,没有考虑负数等情况,只考虑最简单的a>=b>=0的情况,算法如下:
int gcd(int a,int b)
{
if(b==0) return a;
else ruturn gcd(b,a %b);
}
求出了最大公约数,我们在给出一个相关的定理,
如果d是a和b的公约数,并且对某个整数x和y满足d=ax+by,
那么d==gcd(a,b)
简单证明: gcd(a,b)是a,b的最大公约数 => gcd(a,b)>=d(1)
gcd(a,b)整除a和b =>
gcd(a,b)整除ax+by=d =>
gcd(a,b)<=d (2)
由(1)(2)可知,gcd(a,b)==d
那我们怎么才能把x和y求出来呢,很简单,我们对上述gcd算法进行一下扩展,由于要返回三个值,为书写方便,采用如下伪代码:
function extended-gcd( a ,b) //a,b均为正整数且a>b
{
if b==0) return (1,0,a);
(x,y,d)=extended-gcd(b,a mod b);
return (y,x - floor(a/b)*y,d); //floor 函数表示向下取整
}
上述是一个递归算法,基本情况是b==0时,此时gcd(a,b)=a*1+b*0;
若b不为0,则假设已求出extended-gcd(b,a mod b), 此时,
gcd(a,b) =gcd(b,a mod b)
=x*b +y *(a mod b)
=x*b+y*(a-floor(a/b)*b)=y*a+(x-floor(a/b)*y)*b.
接下来,对文章开始提出的第二个问题进行解答,即能否判断一个数是否是素数。首先引入一个相关的定理:
费马小定理: 如果p是素数,那么对任意的1<=a<p, 有 ap-1 ==1 (mod p)
上述定理给出了p为素数的一个必要条件,对于任意给定的数,我们可以根据上述定理证明它不是素数,但似乎无法证明它是素数,那怎么办呢,给出的解决方案很简单,多试几次,如果连着n次都符合素数的条件,那就可以以近似1的概率认为这个数是素数,n值一般取10左右即可,理论上,这种判断方法有可能会判断错误,但是这是小概率事件,可以认为不会发生,具体算法:
function primality(N)
Input: positive integer N
Output: yes/no
pick positive integers a1,a2,...ak<N at random
if aiN-1==1(mod N) for all i=1,2......k
return yes
else
return no
至于第一个问题,数学家们已经研究很久很久,不过至今没有什么有效的算法能够快速求解,当然,没法解这个问题不代表它没有意义,正因为这个问题在现有的计算机上无法求解,这反而给大家提供了一种保证信息安全的机制,我们接下来探讨一下著名的RSA加密算法,这也算是模运算和素数理论的一个重大贡献了。
为了解释这个算法,我们还需要了解一个概念,即对模运算,我们引入一个乘法‘逆’的概念:
定义:如果ax==1(mod N),那么我们把x称为a在模N运算下的乘法逆。
定理:对任意的a和N,a在模N运算下的乘法逆存在,当且仅当N和a互素,即gcd(a,N)==1
可以证明,如果一个数在模运算下的乘法逆存在,则它是唯一的。 例如,3*7(mod 10)==1,所以3是7在模10运算下的逆,那引入这 个‘逆’的概念有什么用呢,最直接的就是在RSA算法中的加解密运算了。我们来分析一下加解密的原理,
设原始数据是x,任意找两个素数p和q,设N=p*q,M=(p-1)*(q-1)),再任意找一个数e,满足gcd(e,M)==1,这样,根据上述定理,在模M运算下e的逆s必存在。我们用e对原始数据进行加密,y=encode(x)=xe(mod N),用s对收到的y进行解密,decode(y)=yd(mod N)=xed(mod N),接下来我们证明decode(y)==x (mod N) :
由于ed==1(mod M),即ed=1+k(p-1)*(q-1),其中k为某整数。
则有xed-x (mod p)
=x 1+k(p-1)*(q-1) - x (mod p)
=x*(x k(p-1)*(q-1) - 1) (mod p)
=0 (费马小定理)
所以xed-x 是p的倍数,同理可知,xed-x 是q的倍数
又因为p和q均是素数,所以xed-x 是N=p*q的倍数。
所以xed-x =0(mod N),即xed=x (mod N),这相当于解密出来原始数据。
RSA是一种公钥加密技术,什么意思呢,就是说,公钥是用来加密的,每个人都可以看见,私钥用来解密,只有我一个人可以看到,这样,如果有人给你发送消息,就用你的公钥进行加密,你可以用私钥对收到的密文进行解密,其他人也可以收到密文,但是由于没有私钥,强行破解算法是一个当前计算机无法解决的复杂度,这也就相当于保证了信息的安全。相信大家已经看出来了,上述证明中的e即是公钥,而d则是私钥,当然两者的角色也是可以互换的。
下面用Alice和Bob之间的通信说明RSA的工作过程:
Bob:接收方
1 随机挑选两个很大的素数(n-bit)p和q
2 他的公钥是(N,e),其中N=pq,e是一个和(p-1)(q-1)互素的数(2n-bit)
3 他的私钥是d,其中d是e在模(p-1)(q-1)运算下的乘法逆
5 收到密文y后,可通过yd mod N计算原始信息x
Alice:发送方
4 设发送的原始数据为x,首先找到Bob的公钥(N,e),加密y=(xe mod N)