算法之美(4)--数论算法

 

第一章 数论算法

本章所讨论的是对两个古典问题进行生动的对比。它们看上去非常相似:

l         因式分解(Factoring):给定一个数字N,将其表示成素数的乘积。

l         素属性(Primality):给定一个数字N,判断它是否是素数。

因式分解比较困难。尽管几个世纪以来世界上最聪明的数学家和科学家付出了很多努力,但分解一个数字N的最快的算法复杂度还是N位数的指数幂。

另一方面,我们可以很快地判断出N是否是素数!并且更有趣的是两个有密切联系的问题间的不对等:一个很难而另一个很容易。今天,两个问题的这种特性成为当今信息安全的核心基础。

在讲解这个问题的过程中,需要开发各种各样的数字计算的算法。从基本的算术开始是最为适合的,因为我们知道,“algorithms”这个单词最初只是解决这类问题的手段。

1.1 基本算术

1.1.1 加法

我们很小的时候,在学习加法运算时从来没有想过为什么要这样做。现在回过头来对它做进一步地了解。

十进制数字有一个基本属性:

任意三个个位数相加,其结果的最大值的位数不超过2位。

检验一下:9+9+9=27,结果为2位。这个规则不仅适用于十进制数,而且适用于所有进制(b>=2)。在二进制中,三个个位数的和的最大值为3,它是一个2位数字。(译者注:二进制的31相加结果为3,即二进制的11)。

底数和对数

自然地,这里没有提到10—我们正好有10个手指头,10也是数字计算中进入下一个级别的停顿点。玛雅人发明了一个类似位值系统,它是20进制的。当然,今天的计算机是用二进制来表现数字的。

基于b进制的正数N需要使用多少个位来表现呢?我们来看看---基于b进制的k位数最大值为b^k-1;例如,十进制中,三位数的最大值为999=10^3-1。使用[logb(N+1)]可以得出k的值(约等于logbN位,把1省略去),这样就可以使用以b为底的N的对数。

当改变底数时,数字的位数会改变多少呢?当底数由a变为b时,我们回顾关于对数的规则可以得出:logbN=(logaN)/(logab)。数字N的位数以a为底时相当于以b乘以一个常数因子logab。对于大O表示法来说,底数没有任何影响,我们只是简单地写为O(logN)。一般情况下,不需指定底数,这时我们把它当为log2N

顺便提一下,这个logN函数多次隐蔽地出现在本书中,下面是一此样本:

1.       logN is, of course, the power to which you need to raise 2 in order to obtain N.

2.       Going backward, it can also be seen as the number of times you must halve N to get down to 1. (More precisely: dlogNe.) This is useful when a number is halved at each iteration of an algorithm, as in several examples later in the chapter.

3.       It is the number of bits in the binary representation of N. (More precisely: [log(N+1)].)

4.       It is also the depth of a complete binary tree with N nodes. (More precisely: [logN].)

这个简单的规则给了我们一个方法来对两个数相加:让它们的右边对齐,并从左到右对数字逐个进行加法运算,当有溢出时则进位。因为个位数的和最多为两位数,所以进位始终是个位数,在一些特定步骤中,三个个位数被相加。下面的例子二进制演示了53+35

 

通常我们会给出算法的伪代码,但此例大家已经非常熟悉,我们就不再重复了,而是直接分析它的效率。

给定两个二进制数xy,对两个数相加的算法需要花费多少时间呢?这一类的问题在本书中将贯穿始终。我们希望用问题规模的函数来回答这个问题:数字xy的位数,也就是输入它们需要敲键的次数。

假设xy的长度都是n位;本章将全部使用n做为数字的长度。那么x + y的长度最多为n+1,并且每个位相加的运算时间为固定的。那么两数相加算法所耗费的时间为c0+c1nc0c1是常数。换句话说,这是线性的。我们站在更高的层面看问题,不需要考虑其精确性,舍去c0c1,从而得到运行时间为O(n)

现在已经有了一个算法并且知道了它的运行时间,但我们还是不可避免地会问:还能做得更好吗?

还会有更快的算法吗?(这是另外一个经常要提到的问题)其实,答案非常简单:为了把两个n位数字相加,我们至少要阅读它们并写下答案,这正好需要n次操作。所以相加算法是最优的,它是常数的倍数。

一些读者可能会对一个问题感到困惑:为什么是O(n)次运算?今天的计算机不是只使用一条指令进行二进制加法吗?这有两个答案。第一,对于今天的32位电脑来说,对一个字节长度的整数进行相加当然只是使用一条指令。但我们在本章后面将会看到,经常并且必须要处理更大的,甚至太到几千个位的数字。对这么大的数字进行加法或者乘法运算必须一点点地运算。第二,如果我们想理解算法,学习当今计算机的那些由硬件编码组成的基础算法是非常有意义的。为此,我们将聚集于算法的位复杂度(bit complexity译者注:不知道怎么翻译这个术语,只好按字面意思翻译了),数字中的单个位的初等运算,因为这个数字反映了实现算法所需要的硬件、晶体管和导线。

你可能感兴趣的:(算法之美(4)--数论算法)