算法杂记

2019-03-26  本文已影响0人  tristan_lee

起源

如今,人们早已习惯了以十进制来书写数字, 也早已忘记了古代欧洲将数字1448写成MCDXLVIII的情形。你是否知道应该如何将两个罗马数字相加?你又是否知道MCDXLVIII + DCCCXII会得到什么结果?(还可以试下将他们相乘)。面对罗马数字这种记数法,别说算,我想想都觉得是恶梦。

十进制系统是人类定量推理方面的一项重大变革,发源于约公元600年的印度。它仅仅使用了10个符号,甚至可以很简洁地写出很大的数字,它使得后面演示的算法基本步骤变得非常有效率。对十进制系统的传播产生重大影响的是一本教材,这本书由一个居住于巴格达的阿拉伯人Al Khwarizmi写于19世纪。Al Khwarizmi在书中展示了数字的加、减、乘、除的基本方法,甚至展示了如何求平方根和π。这些方法精准、明确、有法可寻、具有效率、正确而且简单,它们被称作算法algorithms。在很多世纪之后,十进制系统最终被欧州采用,而算法algorithms这个名词同时也表达了对作者Al Khwarizmi的敬意与纪念。

大O表示法

大O表示法的定义:

Let f(n) and g(n) be functions from positive integers to positive reals. We say f = O(g) (which means that “f grows no faster than g”) if there is a constant c > 0 such that f(n) ≤ cg(n).

大O表示法的核心是抓大放小,抓住主要矛盾。例如面对函数f(n)=3n^2+4n+5,这里的O(f(n))我们要做的就是抓住主要增长部分,所以可以写成O(n^2);因为相对于3n^2,其它项的增长都是次要的,而且可以去掉常数3。

大O的三种增长模式

数论

两个古老的数论问题:

这两个问题看上去似乎十分相似,但其中因子分解是非常困难的,直到现在,分解整数N的最快方法所耗费的时间仍然是对N位数的指数级函数。而另一个问题,我们却可以快捷地测试出一个数N是否为素数。这种奇怪的差异:一个十分困难,一个却异常简单;也正是这种差异奠定了安全通信技术的核心,从而保证了当今世界全球范围内通信环境的安全。

著名的数学家G. H. Hardy,也是数论方面的大家,曾这样描述过他自己的工作:“我这辈子所研究的东西都没有任何实用价值”。然而也正是这些人在几个世纪中的这些“没有价值的”工作,奠定了如今整个互联网,手机通信,当然还包括银行金融领域的安全基石。

欧几里德最大公因数算法

欧几里德规则:

gcd(a,b) = gcd(a mod b, b) (不妨设a>b 且r=a mod b ,r不为0)

其中gcd代表greatest common divisor

求最大公因数的欧几里德算法:

function Euclid(a, b)
Input: Two integers a and b with a ≥ b ≥ 0
Output: gcd(a; b)

if b = 0: return a
return Euclid(b, a mod b) 

模的除法

基本概念:

素性测试

费马小定理(Fermat's little theorem)

如果p是素数,那么对每个a (1 ≤ a < p),有a^{p-1} ≡ 1 (mod p)成立
If p is prime, then for every 1 ≤ a < p, a^{p-1} ≡ 1 (mod p)

图片.png

无处不在的概率:

如上图所示费马小定理为我们提供了一种对N的素性测试。但这里还存在问题,就是费马小定理并不是判断N是否为素数的充分必要条件;它并没有规定当N不是素数时会怎样。而事实上对a的某些取值,一些合数N可能能够通过费马小定理的测试(即a^{N-1} ≡ 1 mod N),例如对于341=11*31并不是一个素数,然而却有2^{340} ≡ 1 mod 341成立。但我们还是有希望的,又是概率来拯救。事实上对于合数N,大多数选取的a值都是不能通过费马小定理的测试,这就为我们提供了在实际应用中可行的算法。

以费马小定理作为测试我们可以得到一个素性测试算法:

function primality(N)
Input: Positive integer N
Output: yes/no

Pick a positive integer a < N at random
if a^(N-1) ≡ 1 (mod N):
    return yes
else:
    return no 

引理:如果对于某些与N互素的a,有a^{N-1} ≡ 1 mod N不成立,那么对于a<N的至少一半的可能取值,N将无法通过费马测试。(证明略)

由引理我们可以得到以上素性测试算法返回正确值的概率:

这样我们就可以得到一个改进版本算法,通过多次重复原先的过程来减少出错概率;可见出错概率将以指数级别快速降低,通过选择足够大的k值,能够使出错概率降低至任意小的水平。当k=100时,测试结果出错的概率最多只有2^{-100},这是一个极小的数:

改进版素性测试算法:

function primality2(N)
Input: Positive integer N
Output: yes/no

Pick positive integers a as a1, a2, ..., ak < N at random
if a^(N-1) ≡ 1 (mod N) for all a1, a2, ..., ak:
    return yes
else:
    return no 

素数的随机生成

我们离密码学应用所需要的所有工具就差最后一步了,一个快速生成随机素数的算法。该素数可能有几百位长,因为素数足够多,所以随机生成一个这样的素数就变得相对简单——一个随机的n位长的数字为素数的概率大约是1/n

随机生成一个n位长的素数:

该算法有多快?当随机选定一个N,N是素数的概率最少有1/n,所以在每次迭代中,该过程最少有1/n的概率停止。从而平均起来,该过程将在O(n)次迭代后终止。

图片.png

实际的素数生成算法测试,如上图所示,我们选取N ≤ 25 × 10^9,在这个范围内,我们得到大约10^9个素数,大约有20000个合数通过了素性测试,出错概率大约在20000/10^9 = 2 × 10^{-5}。而且随着参与计算的数的位数增加(达到几百位长),出错概率还将更迅速的降低。

我们慢慢会发现很多高效和精妙的实用算法背后都依赖于类似抛硬币般的随机性(chance),它们输出的正确概率可以相当高,但永远不可能100%。对所有可能的输入,错误概率的上限同样存在,它只依赖于算法自身做出的随机性选择,我们做不到完美,只能将出错限定在某种级别,但这些算法在实际使用中已经绰绰有余了。

RSA算法

RSA机制基本完全依赖于数论的知识,RSA算法可以是一个定义在集合{0,1,...N-1}上的双向映射函数(bijection),加密操作是从一端映射到另一端,而解密操作就是反向映射;如RSA有以下两个重要性质:

图片.png

RSA安全通信过程

Bob选择他的公钥和私钥

Alice想要向Bob发送消息x

破解RSA算法的两种可能途径

散列函数族

散列函数的最大特点是散射,并且是均匀的散射;这意味着即便输入数据在某个概率分布上是相当集中的,散列函数也可以将这些输入映射成随机的并且彼此唯一的输出。这主要还是依赖于以素数为模后得到的双向映射(bijection)特性。

上一篇下一篇

猜你喜欢

热点阅读