1、组合算法的选择与应用【关键字】组合算法 评价依据 复杂性 选择 应用【摘要】本文提出了在组合算法设计和组合算法选择方面所应当遵循的三个原则,即通用性、可计算性和较少的信息冗余量,并初步分析了它们之间的相互关系。这三个原则是整个组合算法设计的主导思想,也是数学建模和算法优化的方向。通过对三个问题的分析,提示了组合算法的设计方法,改进方向和优化技术,是对一系列组合数学原理的实际应用,也是对组合算法设计的初步研究。【正文】一、引 论组合数学是一个古老的数学分支,也是当代数学中非常重要的数学分支。它发源于有趣的数学游戏,许多古典的组合数学问题,无论在理论数学或应用数学上都有很重要的研究价值。今天,一方
2、面,极为成熟的组合计数理论为物理学、化学、生物学的发展奠定了坚实的基础,另一方面,由于计算机软硬件的限制,组合计数理论的计算机实践又必然涉及到基于多项式时间内的算法优化问题。本文正是基于以上情况,对一系列组合问题的算法设计做了一些初步探索。二、组合算法的评价依据任何事物都有好坏之分,算法也不例外。众所周知,时间复杂度与空间复杂度是算法评价的主要依据。那么,除了这两点外,组合算法的设计还应遵循怎样的原则呢?1通用性通用性即可移植性。一个算法,是只适合于一个特殊问题,还是可以适用于一类问题,这是组合算法评价的一个主要依据,有些组合数学问题,许多信息学竞赛或数学建模竞赛选手一看到题目后往往使用模拟法
3、或构造产生式系统 1,然后用深度优先搜索(DFS) ,或广度优先搜索(BFS)求解,用这些方法设计的程序往往受到时间或空间的限制,而且由于在综合数据库中信息存储的数据结构不同,其算法实现时的规模 2也不同,这必然影响到算法的1 见参考文献6第一章2 在本论文中,我们将规模定义为在一定时间内程序可以运行完毕的情况下输入数据的最大量。通用性问题。解决问题的办法是对原问题进行数学抽象,取其精华,去其糟粕。只有对原问题的数学模型仔细分析,抓住本质,才能建立高效的组合算法,只有这种经过数学抽象的算法,才能具有较好的通用性,能够应付较大的规模。另外,在大规模组合算法的设计过程中,强调通用性的好处是显而易见
4、的,它便于算法的优化和升级。在实际应用中的某些条件改变时,可以重写较少的代码。从软件工程学的角度来说,通用性是必需的。2可计算性这里指的可计算性,是指能够在多项式时间内得出结果。一般来说,对于递归函数来说,由于计算机系统中的空间一定,因此对问题的规模有较严格的限制(例如在 Turbo Pascal 7.0系统中,栈的最大空间只有 65520 字节)因此说,对于大多数的递归函数具有较差的可计算性。通过组合方法,求递归函数的非递归形式是解决这类问题有主要方法,但并不是所有的递归函数都可转化为非递归形式,双递归函数(如 Ackermann 函数 3)便不能转化为非递归形式,这类函数具有较小的可计算性
5、。当然,对于某些递归函数,通过寻找函数本身的组合意义进而将其转化为非递归函数也是一种方法。这种方法的应用读者将在后文中见到。3、信息冗余量在组合数学的建模过程中,大量的信息冗余是制约组合算法效率的一个重要因素,例如在递归程序运行的过程中,实际上产生了一棵解答树 4,同一棵子树的结点间的信息不相互关联,这样便产生了大量的信息冗余,解决的方法之一便是引入记忆机制,将已得出的信息记录下来。这种机制实际上起到了剪枝的作用,但它严格受到空间的限制,实际上是时空矛盾在算法设计中的体现。这便是我们在组合算法设计中倡导函数非递归化的原因。它可以达到零信息冗余。当然,组合算法的通用性、可计算性与信息冗余程度在一
6、定程度上是对立的。例如双递归函数作为一种数学模型,能够反映一类问题,具有通用性,但却具有较差的可计算性和较大的信息冗余量,而有些问题虽具有较好的可计算性和较低的信息冗余量,却具有较差的通用性。总之,算法的时间复杂度,空间复杂度,通用性,可计算性和信息冗余量应是衡量组合算法的几个主要标准。3 Ackermann 函数可用递推关系如下定义A(m,0)=A(m-1,0) m=1,2,A(m,n)=A(m-1,A(m,n-1 ) ) m=1,2, n=1,2,初始条件为A(0,n)=n+1,n=0 ,1,4 见参考文献6第二章(产生式系统的搜索策略)三、组合算法的选择实例组合算法的评价依据同时也是建立
7、数学模型和优化算法的指导思想。那么应该如何设计高效,通用的组合算法呢?下面我们通过几个实际的组合问题来初步研究。例 1核反应堆中有 和 两种粒子,每秒钟内一个 粒子可以反应产生 3 个 粒子,而一个 粒子可以反应产生 1 个 粒子和 2 个 粒子。若在 t=0 时刻的反应堆中只有一个 粒子,求在 t 时刻的反应堆中 粒子和 粒子数。这是一个物理学中的简单问题。我们通过对两种算法的比较来说明组合算法的通用性。模型 I:本题中共涉及两个变量,设在 i 时刻 粒子数为 ni, 粒子数为 mi,则有:n0=1,m0=0,ni=mi1,m i=3ni1+2mi1 本题便转化为求数列 N 和 M 的第 t
8、 项,我们可用递推的方法求得 nt 和 mt,此模型的空间需求较小,时间复杂度为 O(n) ,但随着 n 的增大,所需时间越来越大,即:Tn此模型的算法如下:算法 1-1Prog Arithmtic1_1;BeginRead(t);n0:=1; /初始化操作m0:=0;for i:=1 to t do /进行 t 次递推beginni:=mi-1;mi:=3*ni-1+2*mi-1;end;write(nt); /输出结果write(mt);End. Arithmtic1_1模型 II:设在 t 时刻的 粒子数为 f(t) , 粒子数为 g(t),依题可知: g(t)=3f(t -1)+2g(
9、t -1) (1)f(t)=g(t -1) (2)g(0)=0,f(0)=1下面求解这个递归函数的非递归形式由(2)得 f(t -1)=g(t-2) ( 3)将(3)代入(1)得g(t)=3g(t -2)+2g(t-1) (t2) (4)g(0)=0,g(1)=3(4)式的特征根方程为:x22x3=0其特征根为 x1=3,x2= -1所以该式的递推关系的通解为g(t)=C13t+C2( -1)t代入初值 g(0)=0,g(1)=3 得C1+C2=03C1C2=3解此方程组得 43,21所以该递推关系的解为g(t)= tt)(43 11)(43)( ttgf即 tt)(算法 12Prog Ari
10、thmetic1_2;Beginread(t);n:=trunc(exp(t*ln(3);m:=trunc(exp(t+1)*ln(3);if odd(t) then begin /判断( -1) tn:=n-3;m:=m+3;endelse beginn:=n+3;m:=m-3;end;n:=trunc(n/4); / 4|n m:=trunc(m/4); / 4|mWrite(n);Write(m);End. Arithmetic1_2在模型 II 中,我们运用组合数学的方法建立了递归函数并转化为非递归函数。它的优点是算法的复杂性与问题的规模无关。针对某一具体数据,问题的规模对时间的影响微
11、乎其微。通过以上两个模型我们可以看出,模型 II 抓住了问题的本质,尤其成功地运用了组合数学中关于常系数线性齐次递推关系求解的有关知识,因而使算法本身既具有通用性和可计算性,同时达到了零信息冗余。例 2在平面直角坐标系中,有 n 个圆心坐标为整点的单位圆,求它们所覆盖区域的总面积。这是一道计算几何学的问题,关于图形并的问题,较为常用的方法是离散化,但是如果借助于组合数学的有关理论,是否可以设计出更好的算法呢?我们先来看几个简单的例子。(1)两个圆的交(交集不为 )设圆 i 的圆心坐标为(x i,yi) ,我们定义一个异型函数(dissmilaruty function)如下:|),(jiji
12、yj在讨论两个圆的交的问题时,设两圆为圆 1 与圆 2,它们的交有两种情况 1)2,(设阴影部分面积为 S,则)3213(2r= rS= 23 2),1(设阴影部分面积为 S,则S= )214(r= 2r= 1由于两个圆的非空交集问题是圆最简单的交问题。所以我们规定 的交为 型交,1),(ji的交为 型交,这个规定将在下文的讨论中用到。2),(ji2、三个圆的交(交集不为 ) :经过分析易证,若三个圆的交集不为空,则三个圆中任意两圆的交集一定不为空,反之亦成立。且在任意两圆相交所组成的三个交中,一定有 2 个 型交,1 个 型交。如图所示,阴影部分面积为 S,则有:S= 2221)4361(r
13、r= 533、四个圆的交(交集不为 )经分析可证,若四个圆的交集不为 。则四个圆的圆心一定围成一个边长为 1 的正方形,这四个圆心按照顺时针(或逆时针方向)一定形成 4 个 型交,四个圆的交集如图中阴影部分所示,设其阴影部分面积为 S,则:S= 15cos2sin12)5sin2( rr= 30ii4= 4125sin2可以证明 5 个或 5 个以上互不重合的单位圆的交集必为 。分析至此,我们可以知道,任意多个单位圆的交集都可以通过 2、3、4 个圆的交而获得,那么任意多个单位圆的并集呢?由交集到并集,这使我们想起了容斥原理,于是得出: njkkjijninijjini AAAA 121112
14、1 |131 |nijjknkmmkjii模型有了,但是平面上的位置关系如何来表示呢?我们用带权有向图来有表示单位圆间的关系,边上的权函数定义如下:0 AiA j=C(i,j)= 1 Ai 与 Aj 为 型交2 Ai 与 Aj 的 型交于是所有单位圆之间的信息便可由一个三角矩阵表示出来。两个圆、三个圆、四个圆相交的情况可由下图表示:i i i1 2 2 j j k 1(1) (2) (3) (4)(1)图表示两圆为 型交的圆;(2)图表示两圆为 型为圆;(3)图表示 3 个圆相交的图,在 3 边中有 边权为 2,其余两边权为 1;(4)图为四个圆相交时的图。题目标分析至此,我们便可轻松地设计出
15、算法。算法 2Func dissmilaruty_function(k1,k2:integer):integer;Beginl:=abs(xk1-xk2)+abs(yk1-yk2);/计算异型函数的值if l2 then return(0)j1 1ijkm111else return(l);End; dissmilaruty_function Proc Arithetic2;Begincount1:=0; /count1 为 型交的个数count2:=0; /count2 为 型交的个数area:=n*pi; /当所有圆都不相交时的面积值for i:=1 to n-1 dofor j:=i+1
16、 to n dobeginlisti,j:=dissmilaruty_function(i,j);if listi,j=1 then count1:=count1+1; /两圆为 型交else if listi,j=2 then count2:=count2+1;/两圆为 型交end;/判断两个圆的相交情况area:=area-count1*s1-count2*s2;count1:=0;for i:=1 to n-2 dofor j:=i+1 to n-1 dofor k:=j+1 to n dobegincheck:=true;p:=listi,j+listj,k+listi,k;if (l
17、isti,j=0) or (listj,k=0) or (listi,k=0)then check:=false;if (p=4) and check then /三边的权值都不为 0 且权值之和为 4begincount1:=count1+1; /三个圆的交不为空的个数if listi,j=2 then infoi,k:=2else if listj,k=2 then infoj,k:=2else if listi,k=2 then infoi,k:=2;end;/info 供判断四个圆的交的情况时使用end;/判断三个圆的交的情况area:=area+s3*count1;count1:=0
18、;for i:=1 to n-2 dofor j:=i+1 to n-1 dofor k:=j+1 to n doif (j0 thenbegink:=k-1;dfs(i+1);k:=k+1;end;end;endfor;End; dfs由于本算法的实质是模拟,因此算法实现起来时间复杂度较高,为指数级,这种算法严格限制了问题的规模,因而不是一个好的算法。II栈模型通过对问题的分析我们可以得出这样的结论:在任何时刻,若第 n 个人手持 100 元的钞票买票,则在此之前,定有 m 个人手持 50 元的钞票买票,使得 mn,我们通过分析还将得出:售票处收到的 50 元的钞票最终将全部找出,售票处收到
19、的 100 元的钞票最终将全部留下,且一旦收到一张面值为 100 元的钞票,则一定找出一张面值为 50 元的钞票。由此我们想到了用栈来表示这一过程:若某人手持一张 50 元的钱币购票,相当于一个元素进栈;若某人手持一张 100 元的钱币购票,相当于一个元素出栈。则问题转化为:若 1n 共 n 个元素依次进栈,问共有多少种出栈顺序。n 个 元素的全排列共有 n!个,那么这 n!种方案是否都是可能的出栈顺序呢?答案是否定的,我们可以证明,若 a1,a2,an 是可能的依次出栈顺序,则一定不存在这样的情况:使得iaj,那么当 aj 出栈时,如果 ak 已在栈中,则 ak 比 aj 先入栈,由输入 a
20、 序列知:a kj,若结点 i 是结点 k 的左儿子,结点 j 是结点k 的右儿子,则 i60 0.2747 0.0000 0.000015 9694845 1.1538 60 3.6813 0.0000 0.000016 35357670 4.2308 60 13.5165 0.0000 0.000017 129644790 15.3846 60 49.5055 0.0000 0.0000(时间单位:s)【源程序】1 算法 11 的源程序Program Arithmtic1_1;Var n,m:array0100 of longint;t,i:integer;Beginwrite(Pleas
21、e input t:);readln(t);n0:=1;m0:=0;for i:=1 to t dobeginni:=mi-1;mi:=3*ni-1+2*mi-1;end;writeln(N=,nt);writeln(M=,mt);End.2 算法 12 的源程序Program Arithmetic1_2;var t:integer;n,m:longint;beginwrite(Please input t:);readln(t);n:=trunc(exp(t*ln(3);m:=trunc(exp(t+1)*ln(3);if odd(t) then beginn:=n-3;m:=m+3;end
22、else beginn:=n+3;m:=m-3;end;n:=trunc(n/4);m:=trunc(m/4);writeln(N=,n);writeln(m=,m);end.3算法 2 的源程序Program Arithmetic2;Const InFile=input.txt;OutFile=output.txt;pi=3.1415926535;s1=2/3*pi-1.732/2;s2=pi/2-1;s3=5/12*pi-1.732/2;Var list,info:Array1100,1100 of shortint;x,y: Array1100 of integer;n: Integer
23、;area,s4: real;Procedure init;Var f:Text;a:integer;Beginassign(f,InFile);reset(f);readln(f,n);for a:=1 to n doread(f,xa,ya);close(f);s4:=4*sin(pi/12)*sin(pi/12)+pi/12-1/4;End;Function dissmilaruty_function(k1,k2:integer):integer;Var l:integer;Beginl:=abs(xk1-xk2)+abs(yk1-yk2);if l2 then dissmilaruty
24、_function:=0else dissmilaruty_function:=l;End;Procedure done;var i,j,k,p,count1,count2:integer;check: boolean;Begincount1:=0;count2:=0;area:=n*pi;for i:=1 to n-1 dofor j:=i+1 to n dobeginlisti,j:=dissmilaruty_function(i,j);if listi,j=1 then inc(count1)else if listi,j=2 then inc(count2);end;area:=are
25、a-count1*s1-count2*s2;count1:=0;for i:=1 to n-2 dofor j:=i+1 to n-1 dofor k:=j+1 to n dobegincheck:=true;p:=listi,j+listj,k+listi,k;if (listi,j=0) or (listj,k=0) or (listi,k=0)then check:=false;if (p=4) and check thenbegininc(count1);if listi,j=2 then infoi,k:=2else if listj,k=2 then infoj,k:=2else
26、if listi,k=2 then infoi,k:=2;end;end;area:=area+s3*count1;count1:=0;for i:=1 to n-2 dofor j:=i+1 to n-1 dofor k:=j+1 to n doif (j0 thenbegindec(k);dfs(i+1);inc(k);end;end;end;End;Beginread(n);m:=0;k:=0;dfs(1);writeln(total);End.5算法 32 的源程序$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V+,X+$M 65520,0
27、,655360program Arithmetic3_2;var n:integer;data:Array1100 of integer;flag:set of byte;total:longint;function check(s:integer):boolean;var a,b:integer;begincheck:=false;for a:=1 to s-2 dofor b:=a+1 to s-1 doif (databdatas) and (datasdataa) then exit;check:=true;end;procedure stack(i:integer);var j:in
28、teger;beginfor j:=1 to n doif not(j in flag) thenbegindatai:=j;if check(i) thenbeginflag:=flag+j;if i=n then inc(total)else stack(i+1);flag:=flag-j;end;end;end;beginread(n);stack(1);writeln(total);end.6算法 33 的源程序$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V+,X+$M 65520,0,655360program Arithmetic3_
29、3;var n:integer;function f(a,b:integer):longint;beginif ab then f:=0else if b=0 then f:=1else f:=f(a-1,b)+f(a,b-1);end;beginread(n);writeln(f(n,n);end.7算法 34 的源程序Program Arithmetic3_4;Var data :array120,120 of longint;a,b,n:integer;Beginreadln(n);for a:=1 to n do dataa,1:=a;for a:=2 to n dofor b:=2
30、to a do dataa,b:=dataa-1,b+dataa,b-1;writeln(datan,n);End.8算法 35 的源程序Program Arithmetic3_5;Var n,a,b:integer;total:longint;Beginreadln(n);total:=1;a:=n+2;b:=2;while (a=2*n) dobegintotal:=total*a;while (total mod b=0) and (b=n) dobegintotal:=total div b;inc(b);end;inc(a);end;while bn dobegintotal:=total div b;inc(b);end;writeln(total);End.