1、冗繁削尽留清瘦浅谈信息的充分利用第 1 页冗繁削尽留清瘦浅谈信息的充分利用【摘要】在算法设计中,人们往往不自觉地进行了大量多余的运算,这些累赘将大大降低算法的效率。作者认为,充分利用已知信息,是解决这一问题的一个有效方法。所谓充分利用信息,就是在算法设计中,把已知信息尽可能充分地利用起来,以避免冗余运算,降低算法的时空复杂度,从而提高算法的效率。本文对充分利用信息,在优化回溯法、动态规划和数值计算中的应用作了初步的探讨。【关键字】 信息,算法优化“冗繁削尽留清瘦” 1虽然讲的是画竹,却包含着深刻的哲理。算法设计同画竹一样,也需要削尽冗繁。但在解题实践中,人们往往不自觉地做了一些多余的运算,而忽
2、视了对已知信息的充分利用。所谓充分利用信息,就是在算法设计中,把已知信息尽可能充分地利用起来,以避免冗余运算,降低算法的时空复杂度,从而提高算法的效率。限于篇幅,本文仅对这种方法提高回溯法、动态规划和数值计算的效率进行探讨。一、 提高回溯法的效率我们知道,回溯法实质上是从树根出发,遍历一棵解答树的过程。如果解答树中存在一些性质相同的子树,那么,只要我们知道了其中一棵子树的性质,就可以根据这个信息,导出其它子树的性质。这就是自顶向下记忆化搜索 2的基本思想。记忆化搜索避免了一些多余的运算,因而比非记忆化搜索效率要高。但在有些记忆化搜索中,对信息的利用仍不够充分,还有进一步优化的余地。下面,我们看
3、一个例子:【序关系计数问题】用关系.枚举出所有的序关系表达式我们可以采用回溯法枚举出所有的序关系表达式。N 个数的序关系表达式,是通过 N 个大写字母和连接各字母的 N-1 个关系符号构成。依次枚举每个位置上的大写字母和关系符号,直到确定一个序关系表达式为止。冗繁削尽留清瘦浅谈信息的充分利用第 2 页由于类似于A=B和B=A的序关系表达式是等价的,为此,规定等号前面的大写字母在 ASCII 表中的序号,必须比等号后面的字母序号小。基于这个思想,我们很容易写出解这道题目的回溯算法。算法 1-1,计算 N 个数的序关系数。procedure Count(Step,First,Can);Step 表
4、示当前确定第 Step 个大写字母;First 表示当前大写字母可能取到的最小值;Can 是一个集合,集合中的元素是还可以使用的大写字母 beginif Step=N then begin确定最后一个字母 for i:=First to N do if i in Can then Inc(Total); Total 为统计的结果 Exitend;for i:=First to N do枚举当前的大写字母 if i in Can then begini 可以使用 Count(Step+1,i+1,Can-i);添等于号 Count(Step+1,1,Can-i)添小于号 endend;调用 Co
5、unt(1,1,1N)后,Total 的值就是结果。该算法的时间复杂度是O(N!).粗略利用信息,优化算法 1-1算法 1-1 中存在大量冗余运算。如图 1,三个方框内子树的形态完全一样。一旦我们知道了其中某一个方框内所产生的序关系数,就可以利用这个信息,直接得到另两个方框内将要产生的序关系数。显然,在枚举的过程中,若已经确定了前 k 个数,并且下一个关系符号是小于号,这时所能产生的序关系数就是剩下的 N-k 个数所能产生的序关系数。设 i 个数共有 Fi种不同的序关系,那么,由上面的讨论可知,在算法 1-1中,调用一次 Count(Step+1,1,Can-i)之后,Total 的增量应该是
6、 FN-Step。这个值可以在第一次调用 Count(Step+1,1,Can-i)时求出。而一旦知道了 FN-Step的值,就可以用 Total:=Total+FN-Step 代替调用 Count(Step+1,1,Can-i)。这样,图 1 N=3 时的解答树冗繁削尽留清瘦浅谈信息的充分利用第 3 页我们可以得到改进后的算法 1-2。算法 1-2,计算 N 个数的序关系数。procedure Count(Step,First,Can);Step,First,Can 的含义同算法 1-1beginif Step=N then begin 确定最后一个字母 for i:=First to N
7、do if i in Can then Inc(Total); Total 为统计的结果 Exitend;for i:=First to N do 枚举当前的大写字母 if i in Can then begin i 可以使用 Count(Step+1,i+1,Can-i); 添等于号 if FN-Step=-1 then begin 第一次调用 FN-Step:=Total; Count(Step+1,1,Can-i); 添小于号 FN-Step:=Total-FN-Step FN-Step=Total 的增量 end else Total:=Total+FN-Step FN-Step已经求
8、出 endend;开始,将 F0,F1,FN-1初始化为-1调用 Count(1,1,1N)之后,Total 的值就是结果算法 1-2 与算法 1-1 的差别仅限于程序中的粗体部分。算法 1-2 就是利用了 F0,F1,FN-1的值,使得在确定添小于号以后,能够避免多余的搜索,尽快地求出所需要的方案数。该算法实质上就是自顶向下记忆化方式的搜索,它的时间复杂度为 O(2N)3。同算法 1-1 相比,效率虽然有所提高,但仍不够理想。.充分利用信息,进一步优化算法 1-2在搜索的过程中,如果确定在第 k 个大写字母之后添加第一个小于号,则可得到下面两条信息:第一条信息:前 k 个大写字母都是用等号连
9、接的。第二条信息:在此基础上继续搜索,将产生 FN-k个序关系表达式。如图 2 所示,序关系表达式中第一个小于号将整个表达式分成了两个部分。图 2 充分利用信息,进一步优化算法 1-2冗繁削尽留清瘦浅谈信息的充分利用第 4 页由乘法原理易知,图 2 所示的序关系表达式的总数,就是图中前半部分所能产生的序关系数,乘以后半部分所能产生的序关系数。算法 1-2 实质上利用了第二条信息,直接得到图中后半部分将产生 Fn-k个序关系数,并通过搜索得到前半部分将产生的序关系数。但如果我们利用第一条信息,就可以推知图中前半部分将产生的序关系数,就是 N 个物体中取 k 个的组合数,即 。这样,knC我们可以
10、得到 Fn 的递推关系式: 1011 FnCFk, 其 中:公 式采用公式 1 计算 Fn的算法记为算法 1-34,它的时间复杂度是 O(N2)。.小结下面是三个算法的性能分析表 5:分析项目 算法 1-1 算法 1-2 算法 1-3时间复杂度 O(N!) O(2N) O(N2)理论分析 空间复杂度 O(1) O(N) O(N)N=7 1s 10s 0.5s 10s 2s .一种动态规划的解法解这道题目,很容易想到用动态规划。设 Fi表示在第 i 天收盘时能达到的最高收入,则有 Fi的递推关系式: 10*/max2)0( VFiVkjFiFikj , 其 中:公 式公式 2 的含义是:在第 i
11、 天收盘时能达到的最高的收入,是将第 j 天收盘后的收入,全部用于买入第 k 天的股票,再在第 i 天将所持的股票全部卖出所得的收入。采用公式 2,可以得到算法 2-1,其时间复杂度是 O(M3),空间复杂度冗繁削尽留清瘦浅谈信息的充分利用第 5 页是 O(M)。算法 2-1F0:=1;V0:=1;F1M:=0;for i:=1 to M dofor j:=0 to i-1 dofor k:=j to i-1 doFi:=MaxFi,Fj/Vk*Vi.改变状态表示的含义,优化算法 2-1改变动态规划中状态表示的含义,是优化动态规划的常用方法。例如此题,我们可以采用两种不同的状态表示方法优化算法
12、 2-1。方法 1:设 Pi表示前 i 天能获得的最多股票数,可列出如下状态转移方程: /*,1max3)0( iVjPiPij:公 式这是因为前 i 天所能获得的最多股票数,或者是前 i-1 天获得的最多股票数,或者是在第 j 天将前 j 天所能获得的最多的股票全部卖出,再买入第 i 天的股票。显然,前 i-1 天能获得的最多股票数乘以第 i 天的股价,就是第 i 天能达到的最大收入。方法 2:设 Qi表示前 i 天能达到的最大收入,可列出如下状态转移方程: */,1ax4)0( iVjQiQij:公 式就是说前 i 天所能达到的最大收入,或者是前 i-1 天所能达到的的最大收入,或者是在第
13、 j 天买入股票,再在第 i 天卖出,所能获得的最大收入。上述两种方法的时间复杂度都是 O(M2)。这表明,改变状态表示的含义,在一定程度上提高了算法的效率。但对于这道题目,仅仅改变状态表示的含义,很难进一步优化算法。.粗略利用信息,优化算法 2-1算法 2-1 粗体部分的功能是确定 Fi所能达到的最大值。由于 Vi不变,因此 Fi达到最大值,当且仅当 Fj/Vk达到最大值,其中 0j k.充分利用信息,进一步优化算法 2-2在算法 2-2 中,进一步利用信息,很容易得到时间复杂度为 O(M)的算法。算法 2-2 的粗体部分的功能是确定 MaxFVi所能达到的最大值。由于 Vi-1不变,因此
14、Fj/Vi-1达到最大值,当且仅当 Fj达到最大值,其中 0jMaxF then MaxF:=F;if MaxF/Vi-1MaxFV then MaxFV:=MaxF/Vi-1;F:=MaxFV*Viend;公式 6 中 MaxFi可以看作是前 i-1 天能达到的最大收入 7。虽然这种状态表示方法和公式 4 中 Qi是类似的,但算法的时间复杂度却从 O(M2)降到了O(M),空间复杂度也从 O(M)降到了 O(1)。这样,我们通过充分利用已知信息,达到了改变状态表示难以达到的优化效果。.小结下面是三个算法的性能分析表:分析项目 算法 2-1 算法 2-2 算法 2-3时间复杂度 O(M3) O
15、(M2) O(M)理论分析 空间复杂度 O(M) O(M) O(1)M=200 的随机数据 4s 0.05s 60s 1s 60s 5s .分析由于 k 可以达到 99,因此必须采用高精度计算。本题只要求出 Sm(n)的后 k位数,所以,每次计算只取结果的后 k 位数即可。显然,计算 Sm(n)将耗费大量的时间用于乘幂运算。下面,我们分析乘幂运算的算法。.朴素的乘幂算法根据乘幂的定义,我们将 i 自乘 m 次即可。算法 3-1,计算 imFunc Power(i,m);beginSetValue(x,1); x 是高精度数 for k:=1 to m do x:=Mul(x,i); Mul 是
16、高精度乘法,返回 x*y 的值 Power:=xend;采用算法 3-1 计算 im,需要进行 M 次高精度乘法。.粗略利用信息,优化算法 3-1算法 3-1 对信息的利用很不充分。例如,要计算 i5,现在已经求出了 i3 的值,算法 3-1 会利用 i3 和 i 的值,求出 i4,进而求出 i5。而实际上,在计算 i3 之前,我们已经求出了 i2 的值,将 i3 乘上 i2,就可以求出 i5。我们用数组 pk保存已经计算出的 ik 的结果,在计算过程中,尽可能的使用已经计算过的信息。例如,我们可以这样计算 i15:p1=ip2=p1*p1p3=p2*p1p6=p3*p3p7=p6*p1p14
17、=p7*p7冗繁削尽留清瘦浅谈信息的充分利用第 9 页p15=p14*p1这样,我们只用了 6 次高精度乘法,就求出了 i15。从上面的例子可以看出,将一个已知结果平方,就可以只用一次乘法,求出一个比较大的指数幂,根据这个思想就可得到算法 3-2:为 奇 数为 偶 数m)( 21mii算法 3-2,计算 imFunc Power(i,m);beginif m=1 then Power:=ielse beginTemp:=Power(i,m div 2);Temp:=Mul(Temp,Temp);if Odd(m) then Power:=Mul(Temp,i)else Power:=Tempe
18、ndend;算法 3-2 计算 im 需要 O(log2m)次高精度乘法。这样,我们通过对信息的粗略利用 8,将时间复杂度从 O(m)降到了 O(log2m)。算法 3-2 实质上就是二分法。采用二分法求乘幂,之所以比累乘的方法高效,就在于它更加充分的利用了已知信息。.充分利用信息,进一步优化算法 3-2注意到在计算 im 时,我们已经知道了 1m,2m,(i-1)m 的值,而这些值在算法3-2 计算 im 时没有起任何作用。如果能够建立 im 与 1m,2m,(i-1)m 的关系,就可更快计算 im。例如,求 120m。这时,我们已经计算了 5m 和 24m 的值。显然,5 m 和 24m相
19、乘,就可以得到 120m。当 i 是合数时,设 i=pq,其中 1.小结下面是三个算法的性能分析表:分析项目 算法 3-1 算法 3-2 算法 3-3时间复杂度 O(nm) O(nlog2m) O(nlog2m)理论分析 空间复杂度 O(k) O(k) O(nk)N=M=100,K=99 0.1 60s 55s 10s冗繁削尽留清瘦浅谈信息的充分利用第 10 页在这个例子中,我们首先通过对信息的粗略利用,优化朴素的乘幂算法 3-1,得到采用二分法的算法 3-2。进一步分析发现,二分法在计算 im 时,没有利用已经求出的 1m,2m,(i-1)m 等信息,充分利用这些信息,得到了更快的算法 3-
20、3。由于算法 3-3 需要保存一些已经求过的值 9,所以,该算法实质上是以空间为代价换取了时间。四、 结束语在搜索中加入记忆化信息提高回溯法的效率,改变动态规划中状态表示的含义提高动态规划的效率,采用二分法提高数值计算的效率,都不同程度的体现了对已知信息的充分利用。但仅使用这些常用技巧优化算法,仍可能存在多余的运算。如果能够更加充分的利用已知信息,就可以收到更好的效果。在算法中削去冗繁,关键在于找到可利用的已知信息,一旦把它们充分利用起来,就可以大大提高算法效率。【参考文献】1. 信息学奥林匹克. 1999(3)2. 吴文虎,王健德 .实用算法与程序设计. 电子工业出版社 . 1998.013
21、. 刘福生,王建德 .青少年国际信息学(计算机)奥林匹克竞赛指导人工智能搜索与程序设计. 电子工业出版社 . 1993.044. 郑板桥集冗繁削尽留清瘦浅谈信息的充分利用第 11 页【附录】1. 摘自郑板桥集第 206 页,全诗如下:题画竹四十年来画竹枝日间挥写夜间思冗繁削尽留清瘦画到生时是熟时2. 见参考文献2。3. 在后面的分析中可以看到,调用粗体部分程序段的总次数是 ni nijijC0102故算法的时间复杂度为 O(2n+1)=O(2n)。4. 也可以直接采用数学方法推导公式 1。但我认为,这比本文中使用的方法更难掌握。在参考文献1 中,给出了一个形式上更简单的计算公式,但这个公式的设
22、计也颇有难度。参考文献1中给出的公式是: niFntmttmF1),(0,)(),(),(个 数 的 序 关 系 数 是 :边 界 条 件 :5. 本文中所有程序的运行环境均为 Pentium 100MHz,BP7.0 编译。6. 如果我们边读数据边规划,就只要保存 Vi-1和 Vi,而没有必要定义长度为 M 的数组 V。这样,算法的空间复杂度降到了 O(1)。7. 公式 6 可进一步化简: 1*2/1,max/ /,11,/ax 1/*m,1/ 1,ax iViiMaxFiiMFiViixVi iaxFViFiViMi iiiaxFiii冗繁削尽留清瘦浅谈信息的充分利用第 12 页这样可以得
23、到如下递推关系式: 1*2/1,max iViiMaxFiiMF在源程序 Pro_2.pas 中,给出了这个公式的算法 2-4。算法 2-4 的时空复杂度同算法 2-3,只是形式上更加简单。8. 值得说明的是,采用二分法求乘幂,并不是最充分利用已知信息的方法。例如,采用二分法计算 i15 需要 6 次乘法,而实际上,我们只需 5 次乘法即可。p1=i;p2=p1*p1p3=p2*p1p6=p3*p3p9=p6*p3p15=p9*p6要得到求乘幂的最优方案,需要耗费大量的时间 (见参考文献3)。由于二分法的计算次数,与最优的次数非常接近,因此,本文选择了二分法。9. 我们只需保存已算出的 1m,2m,4999m 的值即可。当然,还可以进一步优化空间复杂度,详见程序 Pro_3.pas。