《算法技术手册》一2.4.7 性能不明显的计算

2.4.7 性能不明显的计算

在很多情况下,仅仅通过算法的描述(如加法和乘法)就可以分辨出算法的性能是线性级还是平方级的。例如,平方级的主要特征是嵌入的循环结构。但是,这样的直接分析对某些算法却无法使用。例2-5给出了GCD算法,该算法是由欧几里德设计,用于计算两个整数的最大公约数。
例2-5:欧几里得GCD 算法
public static void gcd (int a[], int b[], int gcd[]) {
if (isZero (a)) { assign (gcd, a); return; }
if (isZero (b)) { assign (gcd, b); return; }
a = copy (a); // 确保a和b都没有被修改
b = copy (b);
while (!isZero (b)) {
// 最后一个参数表示结果符号,这里可以忽略它,
// 因为我们总是用大数减去小数
// 注意如果a > b,compareTo(a, b)的结果为正数
if (compareTo (a, b) > 0) {
subtract (a, b, gcd, new int[1]);
assign (a, gcd);
} else{
subtract (b, a, gcd, new int[1]);
assign (b, gcd);
}
}
// a的值就是原先a和b的最大公约数
assign (gcd, a);
}
这个算法不断地比较a和b,然后用大数减去小数直至得到0为止。其中的辅助函数(isZero、assign、compareTo和subtract)都可以在代码库中找到。
虽然这个算法可以计算出两个数的最大公约数,但是对于给定的输入规模,要经过多少次迭代,这个答案并不明显。由于每一次循环之后,a或者b都不会变为负数,因此能够保证的是这个算法一定会结束,但是某些GCD的计算可能会需要较长的时间,例如,该算法计算gcd(1000, 1)时会执行999步!很明显,这个算法的性能对于输入数据的敏感程度比乘法和加法算法要大得多。对于相同规模的输入来说,GCD算法的时间是随着输入数据的变化而变化的。GCD算法在计算(10k-1, 1) 的最大公约数时性能最差,它需要处理n=10k-1次循环!由于加法和减法在数据规模为n时的时间复杂度均为O(n),因此GCD算法可以被归类为O(n2)。
例2-6中ModGCD算法的性能要比例2-5的GCD实现好得多,这个算法使用取模计算a除以b的余数。
例2-6:ModGCD 算法计算最大公约数
public static void modgcd (int a[], int b[], int gcd[]) {
if (isZero(a)) { assign (gcd, a); return; }
if (isZero(b)) { assign (gcd, b); return; }
// 将a、b调整到相同位数,然后在其副本上进行计算
a = copy(normalize(a, b.length));
b = copy(normalize(b, a.length));
// 确保a比b大,同时返回最大公约数
int rc = compareTo(a,b);
if (rc == 0) { assign (gcd, a); return; }
if(rc < 0){
int t[]=b;
b=a;
a=t;
}
int quot[] = new int[a.length];
int remainder[] = new int[a.length];
while (!isZero(b)) {
int t[] = copy (b);
divide (a, b, quot, remainder);
assign (b, remainder);
assign (a, t);
}
// a 的值就是原始的(a,b)的最大公约数
assign (gcd, a);
}
ModGCD能够更快地得到解,因为它不会在while循环中不断地从大数中减去小数。这个差异并不仅仅体现在实现细节上,也反映了在如何使用算法解决问题方面的一个根本性改变。
图2-5和表2-5展示了针对随机产生的142个n位数,求解所有10 011对整数对最大公约数的计算结果。

图2-5:比较gcd和modgcd算法

对于随机数据,即便ModGCD实现比对应的GCD实现约快3倍,它的性能依旧是平方级,也就是O(n2)。分析ModGCD算法的最坏情况有些难度,研究表明当ModGCD处理两个连续的Fibonacci数字时性能最差。从图2-5上也可以推断出算法是平方级的,因为算法性能在数据规模翻倍后变为原先的4倍。
计算最大公约数还有着更为优秀的算法,不过这些算法主要针对特别大的整数,因而在实际场景中并不实用。

时间: 2024-09-10 17:17:09

《算法技术手册》一2.4.7 性能不明显的计算的相关文章

《算法技术手册》一导读

前言 修订一本书向来都是一项艰巨的任务.我们既希望保留第1版(于2009年出版)中的精华,也希望弥补其中的一些不足并增加一些新的篇幅.在第2版中,我们延续了第1版中列出的原则,包括: 使用实际代码而非伪代码来描述算法. 将算法独立于解决的问题之外. 恰到好处地介绍数学知识. 以经验主导支撑数学分析. 在更新修订过程中,我们精简了文字描述,简化了一些布局,从而有助于补充新的算法和其他内容.我们相信,从概括的角度介绍计算机科学的一个重要领域,会对实用软件系统有着深远影响. 第2版的变动 在修订过程中

《算法技术手册》一2.2 函数的增长率

2.2 函数的增长率 我们将算法执行时间的增长率描述为一个与输入问题样本规模相关的函数.使用这种方法描述算法性能会比较抽象,因为它忽略了大量的细节问题.所以,在使用这种方法时,需要对抽象背后的细节有一个清醒的认识.程序都必须运行在某个平台上,在这里,广义的平台定义包括: 运行程序的计算机,包括CPU.数据缓存.浮点运算单元(FPU)以及其他芯片特性. 程序编写所使用的编程语言.编译器/解释器以及用于生成代码的优化设置. 操作系统. 其他后台进程. 改变上述组成平台的任何条件都只会对程序的执行时间

《算法技术手册》一3.1 算法模板的格式

3.1 算法模板的格式 使用模板来描述算法的好处在于可以很方便地对比各种算法的相似和不同之处.本书中的每种算法都会遵照模板格式使用固定的小节进行展示.如果某个小节不适用于当前算法或者没有什么价值,就会略去. 3.1.1 名称 算法的描述性名称,用来方便区分其他算法.例如,当我们讨论顺序搜索时,这个名称可准确表达所讨论的是哪种搜索算法.算法的名称永远用粗体表示. 3.1.2 输入/输出 描述输入/输出数据的格式和结构. 3.1.3 使用环境 使用环境一节描述了算法的最佳使用时机和场所,还有成功实现

《算法技术手册》一1.3.2 分治算法

1.3.2 分治算法 我们也可以将点按x坐标从左到右排序(如果x坐标相同,就按照y坐标排序),就能将这个问题分成两个稍微小一点的子问题.首先可以从点p0到pn-1,按照从左到右.顺时针的顺序计算出一个上半部分凸包,然后用同样的方法从pn-1到p0,按照从右到左.同样是顺时针的顺序计算出下半部分凸包.凸包扫描算法(将在第9章中介绍)可以计算出这些半凸包(见图1-4),然后将结果合并在一起生成最终的凸包. 图1-4:合并上.下部分凸包组成完整凸包

《算法技术手册》一2.4.3 次线性级算法O(nd)(d&lt;1)的性能

2.4.3 次线性级算法O(nd)(d<1)的性能 在某些情况下,次线性算法的性能要好于线性算法,但还是不如对数算法高效.第10章将会讨论多维k-d树,它能够高效地划分n个d维的点.如果k-d树是平衡树,那么区间查询的性能为,在二维的情况下,最终性能为O(sqrt(n)).

《算法技术手册》一2.4.6 二次方的算法性能

2.4.6 二次方的算法性能 现在考虑一个类似的问题:两个n位的整数相乘.例2-4展示了使用小学课堂上学过的算法实现的乘法运算,其中n位数字的表示方法与之前的加法一样. 例2-4:mult乘法的Java实现 public static void mult (int[] n1, int[] n2, int[] result) { int pos = result.length-1; // 清除所有的值 for (int i = 0; i < result.length; i++) { result

《算法技术手册》一2.4.1 常数级算法的性能

2.4.1 常数级算法的性能 在分析算法性能时,本书常常会强调原生操作都具有常数级的性能.很明显,这个声明并不能完全准确地描述实际操作的性能,因为它没有考虑到硬件问题.例如,比较两个32 位的数x和y的大小,这个操作耗费的时间与x.y的大小无关.常数级的操作被认为具有O(1)的性能.那么在比较两个256 位的数字时,性能又如何呢?比较两个1024位的数字又如何呢?我们认为,在长度k固定的前提下,比较两个k位数字的操作可以在常数时间内完成.这么认为的前提是问题的规模(即x.y的值)不得超过k.由k

《算法技术手册》一2.4.2 对数级算法的性能

2.4.2 对数级算法的性能 酒保和顾客打了一个10 000 美元的赌.酒保说:"我会从1~1 000 000中挑选一个秘密数字,你有20次的机会来猜这个数字.每次猜完,我会告诉你结果是高了.低了,还是猜中了.如果你能在20个问题之内猜到我的秘密数字,我给你10 000美元,否则你给我10 000美元."你会打这个赌吗?回答当然是应该打,因为你能够稳赢.表2-1给出了范围为1~8的示例场景.表中展示了如何通过一系列的询问,每次将候选数据缩减一半.表2-1:在1~8之间猜数字的示例行为酒

《算法技术手册》一2.4.5 线性对数算法的性能

2.4.5 线性对数算法的性能性能指标很好地描述了同类算法的共同行为.为了更好地阐述算法在实践中的行为,我们定义了一个函数t(n),用于表示算法解决样本规模为n的问题所需要的时间.分治法是解决问题的一个高效方法,它将规模为n的问题划分成(大致相等的)两个规模为n/2的子问题,并通过递归解决问题.这些子问题会通过线性时间方式合并在一起来解决原先规模为n的问题.使用数学表达式可以表示为:也就是说,t(n)包括了解决两个子问题和归并结果的费用,其中归并结果的费用不超过线性时间(即c*n).在等式的右边