递归分析方法

递归分析方法
递归分析方法

当一个算法(如二分查找)中包含对自己的递归调用时,关于这个算法时间复杂性的分析最终都转化为一个递归方程的求解问题,而这样的算法不在少数。实际上这是数学领域的问题,但是计算机科学又怎么能脱离数学而存在呢?^_^ 数学是好东西呀,可惜自己在这方面造诣颇浅,今生之遗憾亚。^_^

还好,解决递归方程涉及的数学知识我还是能应付的了的^_^。在MIT算法导论中介绍了3种方法,我们这里就说说这三种方法!这些是基础,如果以后要深入研究算法的话,这些知识是必须要精通的;如果并不想在算法方面有所深入的话,多学些知识也没错。我本身也是在学习,像这类的知识一般都比较死性,有些记住了,就可以掌握了。

1、Substitution Method

这是一种使用数学归纳法推导证明的方法,其步骤为先假设一个解,然后带入到递归方程中,利用数学归纳法推导,以验证假设的解是否合理。我们拿ITA(Introduction to Algorithm)中的例子说明吧,比较保险^_^。

[Ex1.]

T(n) = 4T(n/2) + n,解这个递归等式,分析T(n)的渐近性。

解:(这里我们只来找上界)

我们假设T(1) = θ(1),猜测一个解T(n) = O(n^3),根据O符号的定义,我们得到对k < n, 有T(k) <= ck^3,把这个解代入到T(n) = 4T(n/2) + n,并进行推导得出:

T(n) = 4T(n/2) + n

<= 4c((n/2)^3) + n

= (c/2)n^3 + n

= cn^3 - ((c/2)n^3 - n)

当c >= 2, n >= 1时,((c/2)n^3 - n) >= 0,这时T(n) <= cn^3,即T(n) = O(n^3);

我们再回过头来看看当n = 1时这个解是否成立,即证明一下T(1) = θ(1)。对于1 <= n < n0, θ(1) <= cn^3 (c足够大),即该推导出的解也满足初始条件,所以O(n^3)是T(n)的一个上界。但是O(n^3)是否是严紧的上界呢,我们不妨缩小上界范围再推导一次,这次我们猜测解为T(n) = O(n^2),根据O符号的定义,我们得到对k < n, 有T(k) <= ck^2,把这个解代入到T(n) = 4T(n/2) + n,并进行推导得出:

T(n) = 4T(n/2) + n

<= 4c((n/2)^2) + n

= cn^2 + n

= cn^2 - (-n)

不能严格符合T(n) <= cn^2的定义,所以推导失败。但是失败是不是说明,T(n) = O(n^2)一定不成立呢?我们再做一次最后的努力,当出现上面的这种情况时,我们假设解仍为:T(n) = O(n^2),只是我们选择对k < n, 有T(k) <= ak^2 - bk,我们选择减去一个低阶的项,这不会影响到n足够大时的渐进性的,这里是一个常用的技巧。

T(n) = 4T(n/2) + n

<= 4(a(n/2)^2 - b(n/2)) + n

= an^2 - bn - (bn - n)

<= an^2 - bn (当b >= 1时)

这样我们找到了严紧解T(n) = O(n^2)。

2、Iteration method(Recursion-tree method)

这个方法的思想是:"迭代地展开递归方程的右端,使之成为一个非递归的和式,然后通过对和式的估计来达到对方程左端即方程的解的估计"。而我们可以借助'树'的形式来帮助迭代展开的过程。

[Ex2.]

T(n) = T(n/4) + T(n/2)+ n^2;解这个递归等式,分析T(n)的渐近性。

解:

T(n) = n^2 + T(n/4) + T(n/2)

= n^2 + {(n/4)^2 + T(n/16) + T(n/8)} + {(n/2)^2 + T(n/8) + T(n/4)}

= ...

= n^2 {1 + 5/16 + (5/16)^2 + (5/16)^3 + ... }

= θ(n^2)

3、Master Method

这是一种典型的套用公式的方法,解决形如'T(n) = aT(n/b) + f(n)'递归方程形的解的方法。这种递归方程是一类分治法的时间复杂性所满足的递归关系,即一个规模为n的问题被分成规模均为n/b的a个子间题,递归地求解这a个子问题,然后通过对这a个子间题的解的综合,得到原问题的解。如果用T(n)表示规模为n的原问题的复杂性,用f(n)表示把原问题分成a个子问题和将a个子问题的解综合为原问题的解所需要的时间,我们便有方程'T(n) = aT(n/b) + f(n)'。

在f(n)的三类情况下,我们有T(n)的渐近估计式有三类情况:(log(b, a)表示以b为底的对数)

(1) 若对于某常数ε>0,有f(n) = O(n^log(b, a-ε)),即f(n)以慢于n^(log(b, a))的速率渐进增长,则T(n) = θ(n^(log(b, a));

(2) 若有f(n) = θ(n^log(b, a) * (lgn)^k),即f(n)以相似于n^(log(b, a))增长的速率渐进增长,则T(n) = θ(n^(log(b, a) * (lgn)^(k+1)),k为一常数,k >= 0;

(3) 若对于某常数ε>0,有f(n) = Ω(n^log(b, a+ε)),即f(n)以快于n^(log(b, a))的速率渐进增长,且对于某常数c > 1和所有充分大的正整数n有af(n/b) <= cf(n),则T(n) = θ(f(n))。

举例来说吧:

[Ex3.]

T(n) = 4T(n/2) + n,解这个递归等式,分析T(n)的渐近性。

解:对T(n) = 4T(n/2) + n我们得到a = 4, b = 2, f(n) = n, 计算得出n^(log(b, a) = n^(log(2, 4) = n^2,而f(n) = n = O(n^(2-ε)),此时ε= 1,根据Case (1),我们得到T(n) = θ(n^2)。

[Ex4.]

T(n) = 4T(n/2) + n^2,解这个递归等式,分析T(n)的渐近性。

解:对T(n) = 4T(n/2) + n^2,我们得到a = 4, b = 2, f(n) = n^2, 计算得出n^(log(b, a) = n^(log(2, 4) = n^2, f(n) = n^2 = θ(n^2 * (lgn)^0),即k = 0,这样按照Case (2),我们得到T(n) = θ(n^2 * (lgn)^(k+1)) = θ(n^2 * (lgn))。

[Ex5.]

T(n) = 4T(n/2) + n^3,解这个递归等式,分析T(n)的渐近性。

解:对T(n) = 4T(n/2) + n^3,我们得到a = 4, b = 2, f(n) = n^3, 计算得出n^(log(b, a) = n^(log(2, 4) = n^2, f(n) = n^3 = Ω(n^(2+ε),此时ε= 1,且4f(n/2) = (n^3)/2 <= cn^3(c >= 1/2),所以得到T(n) = θ(n^3)。

对于大部分人来说'Master Method'应该是最常用的,这几个Case可要牢牢记在心上才行哟。

递归下降语法分析程序设计

编译方法实验报告实验名称:简单的语法分析程序设计

实验要求 1.功能:对简单的赋值语句进行语法分析 随机输入赋值语句,输出所输入的赋值语句与相应的四元式 2.采用递归下降分析程序完成(自上而下的分析) 3.确定各个子程序的功能并画出流程图 4.文法如下:

5.编码、调试通过 采用标准输入输出方式。输入输出的样例如下: 【样例输入】 x:=a+b*c/d-(e+f) 【样例输出】(说明,语句和四元式之间用5个空格隔开) T1:=b*c (*,b,c,T1) T2:=T1/d (/,T1,d,T2) T3:=a+T2 (+,a,T2,T3) T4:=e+f (+,e,f,T4) T5:=T3-T4 (-,T3,T4,T5) x:=T5 (:=,T5,-,x) 【样例说明】程序除能够正确输出四元式外,当输入的表达式错误时,还应能检测出语法错误,给出相应错误提示。 6.设计3-5个赋值语句测试实例,检验程序能否输出正确的四元式;当输入错误 的句子时,检验程序能够给出语法错误的相应提示信息。 7.报告内容包括: 递归程序的调用过程,各子程序的流程图和总控流程图,详细设计,3-5个测试用例的程序运行截图及相关说明,有详细注释的程序代码清单等。

目录 1.语法分析递归下降分析算法............................... 错误!未定义书签。 背景知识............................................. 错误!未定义书签。 消除左递归........................................... 错误!未定义书签。 2.详细设计及流程图....................................... 错误!未定义书签。 函数void V( ) .|z ................................. 错误!未定义书签。 函数void A( ) 错误!未指定书签。错误!未指定书签。错误!未指定书签。错误!未指定书签。错误!未指定书签。试用例及截图........................ 错误!未定义书签。 测试用例1及截图..................................... 错误!未定义书签。 测试用例2及截图..................................... 错误!未定义书签。 测试用例3及截图..................................... 错误!未定义书签。 代码清单................................................. 错误!未定义书签。

编译原理-编写递归下降语法分析器

学号107 成绩 编译原理上机报告 名称:编写递归下降语法分析器 学院:信息与控制工程学院 专业:计算机科学与技术 班级:计算机1401班 姓名:叶达成 2016年10月31日

一、上机目的 通过设计、编制、调试一个递归下降语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,掌握常用的语法分析方法。通过本实验,应达到以下目标: 1、掌握从源程序文件中读取有效字符的方法和产生源程序的内部表示文件的方法。 2、掌握词法分析的实现方法。 3、上机调试编出的词法分析程序。 二、基本原理和上机步骤 递归下降分析程序实现思想简单易懂。程序结构和语法产生式有直接的对应关系。因为每个过程表示一个非终结符号的处理,添加语义加工工作比较方便。 递归下降分析程序的实现思想是:识别程序由一组子程序组成。每个子程序对应于一个非终结符号。 每一个子程序的功能是:选择正确的右部,扫描完相应的字。在右部中有非终结符号时,调用该非终结符号对应的子程序来完成。 自上向下分析过程中,如果带回溯,则分析过程是穷举所有可能的推导,看是否能推导出待检查的符号串。分析速度慢。而无回溯的自上向下分析技术,当选择某非终结符的产生时,可根据输入串的当前符号以及各产生式右部首符号而进行,效率高,且不易出错。 无回溯的自上向下分析技术可用的先决条件是:无左递归和无回溯。 无左递归:既没有直接左递归,也没有间接左递归。 无回溯:对于任一非终结符号U的产生式右部x1|x2|…|x n,其对应的字的首终结符号两两不相交。 如果一个文法不含回路(形如P?+ P的推导),也不含以ε为右部的产生式,那么可以通过执行消除文法左递归的算法消除文法的一切左递归(改写后的文法可能含有以ε为右部的产生式)。 三、上机结果 测试数据: (1)输入一以#结束的符号串(包括+—*/()i#):在此位置输入符号串例如:i+i*i# (2)输出结果:i+i*i#为合法符号串 (3)输入一符号串如i+i*#,要求输出为“非法的符号串”。 程序清单: #include #include char str[50]; int index=0; void E(); //E->TX; void X(); //X->+TX | e void T(); //T->FY void Y(); //Y->*FY | e void F(); //F->(E) | i int main() /*递归分析*/ { int len; int m;

算法设计题详解

算法设计的特征:有穷性,确定性,输入和输出,可行性 运行算法的时间:硬件的速度。书写程序的语言。问题的规模,编译生成程序的代码质量算法复杂度: 时间复杂度和空间复杂度 1.迭代法 迭代法又称为辗转法,是用计算机解决问题的一种基本方法,为一种不断用变量的旧值递推新值的过程,与直接法相对应,一次性解决问题。迭代法分为精确迭代和近似迭代,“二分法”和“牛顿迭代法”属于近似迭代法。迭代法利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。 利用迭代算法解决问题,需要做好以下三个方面的工作: 1.确定迭代变量(在可以用迭代算法解决的问题中,至少存在一个直接或间接地 不断由旧值递推出新值的变量,这个变量就是迭代变量。) 2. 建立迭代关系式(所谓迭代关系式,指如何从变量的前一个值推出其下一个值 的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以顺推 或倒推的方法来完成。) 3.对迭代过程进行控制(在什么时候结束迭代过程?这是编写迭代程序必须考虑 的问题。不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可分为 两种情况:一种是所需的迭代次数是个确定的值,可以计算出来;另一种是所 需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实 现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程 的条件。) 2.穷举搜索法 穷举搜索法是对可能是解的众多候选解按某种顺序进行逐一枚举和检验,并从众找出那些符合要求的候选解作为问题的解。 即本方法使用可以理解为暴力循环方法,穷举所有可能性,一般这种方法的时间效率太低,不易使用。但是方法简单,易理解。 3.递推法 递推是计算机数值计算中的一个重要算法,思路是通过数学推导,将复杂的运算化解为若干重复的简单运算,以充分发挥计算机长于重复处理的特点。递推法: 递推法实际上是一种递推关系,就是为了得到问题的解,把它推到比原问题简单的 问题求解,可分为顺推法和倒推法。 i.顺推法,就是先找到递推关系式,然后从初始条件出发,一步步地按 递推关系式递推,直至求出最终结果。 ii.倒推法,就是在不知道初始条件的情况下,经某种递推关系而获知问题的解,再倒过来,推知它的初始条件。 4.递归法(递推加回归) 一个过程或函数在其定义或说明中又间接或间接调用本身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题类似的规模较小的问题来求解,递 归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了 程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想 写出的程序往往十分简洁易懂。 一般来说,递归需要有边界条件、递归前进段和递归前往段。当边界条件不满脚时,递归前进;当边界条件满脚时,递归前往。

高中信息技术 算法与程序设计-递归算法的实现教案 教科版

递归算法的实现 【基本信息】 【课标要求】 (三)算法与问题解决例举 1. 内容标准 递归法与问题解决 (1)了解使用递归法设计算法的基本过程。 (2)能够根据具体问题的要求,使用递归法设计算法、编写递归函数、编写程序、求解问题。 【教材分析】 “算法的程序实现”是《算法与程序设计》选修模块第三单元的内容,本节课是“递归算法的程序实现”,前面学习了用解析法解决问题、穷举法解决问题、在数组中查找数据、对数进行排序以及本节的前一小节知识点“什么是自定义函数”的学习,在学习自定义函数的基础上,学习递归算法的程序实现是自定义函数的具体应用,培养学生“自顶向下”、“逐步求精”的意识起着重要的作用。 『递归算法在算法的学习过程中是一个难点,在PASCAL和C语言等程序语言的学习过程中,往往是将其放在“函数与过程”这一章节中来讲解的。递归算法的实现也是用函数或是过程的自我调用来实现的。从这一点上来讲,作者对教材的分析与把握是准确的,思路是清晰的,目标是明确的。』 【学情分析】 教学对象是高中二年级学生,前面学习了程序设计的各种结构,在学习程序设计各种结构的应用过程中培养了用计算机编程解决现实中问题的能力,特别是在学习循环语句的过程中,应用了大量的“递推”算法。前一节课学习了如何自定义函数,在此基础上学习深入学习和体会自定义函数的应用。以递推算法的逆向思维进行求解问题,在学习过程中体会递归算法的思想过程。多维度的思考问题和解决问题是提高学生的学习兴趣关键。 『递归算法的本质是递推,而递推的实现正是通过循环语句来完成的。作者准确把握了学生前面的学习情况,对递归算法的本质与特征也分析的很透彻,可以说作者对教学任务的分析是很成功的,接来就要看,在成功分析的基础上作者是如何通过设计教学来解决教学难点的了。』 【教学目标】

递归下降分析法

《编译原理》课程实验报告 实验名称:递归下降分析法 一.实验目的 1.理解递归下降语法分析方法的主要原理 2.理解递归下降分析法对文法的要求 3.熟练掌握Predict集合的求法 4.熟练掌握文法变换算法(消除左递归和消除公共前缀) 二.实验内容 #include char token[20]; char sym; int p; void S(); void T(); void U(); void Scanner(); void Error(); void S() { if (sym == 'a' || sym == '^') Scanner(); else if (sym == '(') { Scanner(); T(); if (sym == ')') Scanner(); else Error(); } else Error();

} void T() { S(); U(); } void U() { if (sym == ',') { Scanner(); S(); U(); } else if(sym != ')') Error(); } void Scanner() { sym = token[p++]; } void Error() { //exit(0); } int main() { printf("输入: "); gets(token); p = 0; Scanner(); S(); if (sym == '$') printf("Success!"); else printf("Fail!"); return 0; } 三.实验步骤 调试程序的结果:

递归算法详解

递 归 冯文科 一、递归的基本概念。 一个函数、概念或数学结构,如果在其定义或说明内部直接或间接地出现对其本身的引 用,或者是为了描述问题的某一状态,必须要用至它的上一状态,而描述上一状态,又必须用到它的上一状态……这种用自己来定义自己的方法,称之为递归或递归定义。在程序设计中,函数直接或间接调用自己,就被称为递归调用。 二、递归的最简单应用:通过各项关系及初值求数列的某一项。 在数学中,有这样一种数列,很难求出它的通项公式,但数列中各项间关系却很简单,于是人们想出另一种办法来描述这种数列:通过初值及n a 与前面临近几项之间的关系。 要使用这样的描述方式,至少要提供两个信息:一是最前面几项的数值,一是数列间各项的关系。 比如阶乘数列 1、2、6、24、120、720…… 如果用上面的方式来描述它,应该是: ???>==-1 ,1,11n na n a n n 如果需要写一个函数来求n a 的值,那么可以很容易地写成这样:

这就是递归函数的最简单形式,从中可以明显看出递归函数都有的一个特点:先处理一 些特殊情况——这也是递归函数的第一个出口,再处理递归关系——这形成递归函数的第二个出口。 递归函数的执行过程总是先通过递归关系不断地缩小问题的规模,直到简单到可以作为 特殊情况处理而得出直接的结果,再通过递归关系逐层返回到原来的数据规模,最终得出问题的解。 以上面求阶乘数列的函数)(n f 为例。如在求)3(f 时,由于3不是特殊值,因此需要计 算)2(*3f ,但)2(f 是对它自己的调用,于是再计算)2(f ,2也不是特殊值,需要计算 )1(*2f ,需要知道)1(f 的值,再计算)1(f ,1是特殊值,于是直接得出1)1(=f ,返回上 一步,得2)1(*2)2(==f f ,再返回上一步,得62*3)2(*3)3(===f f ,从而得最终解。 用图解来说明,就是 下面再看一个稍复杂点的例子。 【例1】数列}{n a 的前几项为

实验三_递归下降法的语法分析器

魏陈强 23020092204168 实验3 递归下降法的语法分析器 一、实验目的 学习用递归下降法构造语法分析器的原理,掌握递归下降法的编程方法。 二、实验内容 用递归下降法编写一个语法分析程序,使之与词法分析器结合,能够根据语言的上下文无关文法,识别输入的单词序列是否文法的句子。 这里只要求实现部分产生式,文法的开始符号为program。(完整的源语言的文法定义见教材附录 A.1,p394) program→ block block→{stmts } stmts→stmt stmts | stmt→id=expr; | if(bool)stmt | if( bool)stmt else stmt | while(bool)stmt | do stmt while(bool ) ; | break ; | block bool →expr < expr | expr <= expr | expr > expr | expr >= expr | expr expr→ expr + term | expr - term | term

term→ term * factor | term / factor | factor factor→ ( e xpr ) | id| num 三、实验要求 1.个人完成,提交实验报告。 2.实验报告中给出采用测试源代码片断,及其对应的最左推导过程(形式可以自行考虑)。 测试程序片断: { i = 2; while (i <=100) { sum = sum + i; i = i + 2; } } 对应的推导过程为: program?block ?{stmts } ?{stmt stmts} ?{id=expr;stmts } ?{id=num;stmts } ?{id=num;stmt stmts } ?{id=num;while(bool)stmt stmts } ?{id=num;while(e xpr<= expr)stmt stmts } ?{id=num;while(id<= expr)stmt stmts } ?{id=num;while(id<= num)stmt stmts } ?{id=num;while(id<= num)block stmts } ?{id=num;while(id<= num){stmts }stmts } ?....... 四、实验思路 之前编写的词法分析器,能够将语句中的每一个词素都识别出来,因此,在此基础上,定义一个二维字符串数组finaltable[100][20],用于存放由词法分析器提取出来的每个词素,比如,i=2,则finaltable[0]=”id”,

算法设计及分析递归算法典型例题

算法递归典型例题 实验一:递归策略运用练习 三、实验项目 1.运用递归策略设计算法实现下述题目的求解过程。 题目列表如下: (1)运动会开了N天,一共发出金牌M枚。第一天发金牌1枚加剩下的七分之一枚,第二天发金牌2枚加剩下的七分之一枚,第3天发金牌3枚加剩下的七分之一枚,以后每天都照此办理。到了第N天刚好还有金牌N枚,到此金牌全部发完。编程求N和M。 (2)国王分财产。某国王临终前给儿子们分财产。他把财产分为若干份,然后给第一个儿子一份,再加上剩余财产的1/10;给第二个儿子两份,再加上剩余财产的1/10;……;给第i 个儿子i份,再加上剩余财产的1/10。每个儿子都窃窃自喜。以为得到了父王的偏爱,孰不知国王是“一碗水端平”的。请用程序回答,老国王共有几个儿子?财产共分成了多少份? 源程序: (3)出售金鱼问题:第一次卖出全部金鱼的一半加二分之一条金鱼;第二次卖出乘余金鱼的三分之一加三分之一条金鱼;第三次卖出剩余金鱼的四分之一加四分之一条金鱼;第四次卖出剩余金鱼的五分之一加五分之一条金鱼;现在还剩下11条金鱼,在出售金鱼时不能把金鱼切开或者有任何破损的。问这鱼缸里原有多少条金鱼? (4)某路公共汽车,总共有八站,从一号站发轩时车上已有n位乘客,到了第二站先下一半乘客,再上来了六位乘客;到了第三站也先下一半乘客,再上来了五位乘客,以后每到一站都先下车上已有的一半乘客,再上来了乘客比前一站少一个……,到了终点站车上还有乘客六人,问发车时车上的乘客有多少? (5)猴子吃桃。有一群猴子摘来了一批桃子,猴王规定每天只准吃一半加一只(即第二天吃剩下的一半加一只,以此类推),第九天正好吃完,问猴子们摘来了多少桃子? (6)小华读书。第一天读了全书的一半加二页,第二天读了剩下的一半加二页,以后天天如此……,第六天读完了最后的三页,问全书有多少页? (7)日本著名数学游戏专家中村义作教授提出这样一个问题:父亲将2520个桔子分给六个儿子。分完后父亲说:“老大将分给你的桔子的1/8给老二;老二拿到后连同原先的桔子分1/7给老三;老三拿到后连同原先的桔子分1/6给老四;老四拿到后连同原先的桔子分1/5给老五;老五拿到后连同原先的桔子分1/4给老六;老六拿到后连同原先的桔子分1/3给老大”。结果大家手中的桔子正好一样多。问六兄弟原来手中各有多少桔子? 四、实验过程 (一)题目一:…… 1.题目分析 由已知可得,运动会最后一天剩余的金牌数gold等于运动会举行的天数由此可倒推每一 天的金牌剩余数,且每天的金牌数应为6的倍数。 2.算法构造 设运动会举行了N天, If(i==N)Gold[i]=N; Else gold[i]=gold[i+1]*7/6+i;

递归下降分析法

编译原理课程实验报告 班级学号:姓名: 实验名称:递归下降分析法 一、实验目的: 根据某一文法编制调试递归下降分析程序,以便对任意输入的符号串进行分析。本次实验的目的主要是加深对递归下降分析法的理解。 二、实验要求: 对下列文法,用递归下降分析法对任意输入的符号串进行分析: (1)E->TG (2)G->+TG|—TG (3)G->ε (4)T->FS (5)S->*FS|/FS (6)S->ε (7)F->(E) (8)F->i 输出的格式如下: (1)递归下降分析程序,编制人:姓名,学号,班级 (2)输入一以#结束的符号串(包括+—*/()i#):在此位置输入符号串例如:i+i*i# (3)输出结果:i+i*i#为合法符号串 备注:输入一符号串如i+i*#,要求输出为“非法的符号串”。 注意: 1.表达式中允许使用运算符(+-*/)、分割符(括号)、字符i,结束符#; 2.如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好); 三、实验过程: 程序设计: 1.模块设计:将程序分成合理的多个模块(函数),每个模块做具体的同一事情。 2.写出(画出)设计方案:模块关系简图、流程图、全局变量、函数接口等。 程序编写: 1.定义部分:定义常量、变量、数据结构。 2.初始化:从文件将输入符号串输入到字符缓冲区中。 3.利用递归下降分析法,对每个非终结符编写函数,在主函数中调用文法开始符号的函数。 四、实验结果

(1)程序流程图 主函数main( )流程图 E( )过程流程图 T( )过程流程图

G( )过程流程图 F( )过程流程图

汉诺塔问题非递归算法详解

Make By Mr.Cai 思路介绍: 首先,可证明,当盘子的个数为n 时,移动的次数应等于2^n - 1。 然后,把三根桩子按一定顺序排成品字型(如:C ..B .A ),再把所有的圆盘按至上而下是从小到大的顺序放在桩子A 上。 接着,根据圆盘的数量确定桩子的排放顺序: 若n 为偶数,按顺时针方向依次摆放C ..B .A ; 若n 为奇数,按顺时针方向依次摆放B ..C .A 。 最后,进行以下步骤即可: (1)首先,按顺时针方向把圆盘1从现在的桩子移动到下一根桩子,即当n 为偶数时,若圆盘1在桩子A ,则把它移动到B ;若圆盘1在桩子B ,则把它移动到C ;若圆盘1在桩子C ,则把它移动到A 。 (2)接着,把另外两根桩子上可以移动的圆盘移动到新的桩子上。 即把非空桩子上的圆盘移动到空桩子上,当两根桩子都非空时,移动较小的圆盘。 (3)重复(1)、(2)操作直至移动次数为2^n - 1。 #include #include using namespace std; #define Cap 64 class Stake //表示每桩子上的情况 { public: Stake(int name,int n) { this->name=name; top=0; s[top]=n+1;/*假设桩子最底部有第n+1个盘子,即s[0]=n+1,这样方便下面进行操作*/ } int Top()//获取栈顶元素 { return s[top];//栈顶 } int Pop()//出栈 { return s[top--];

} void Push(int top)//进栈 { s[++this->top]=top; } void setNext(Stake *p) { next=p; } Stake *getNext()//获取下一个对象的地址 { return next; } int getName()//获取当前桩子的编号 { return name; } private: int s[Cap+1];//表示每根桩子放盘子的最大容量 int top,name; Stake *next; }; void main() { int n; void hanoi(int,int,int,int); cout<<"请输入盘子的数量:"; cin>>n; if(n<1) cout<<"输入的盘子数量错误!!!"<

编译原理-实验报告2-递归下降分析法

计算机硬件实验室实验报告 一、实验目的: 根据某一文法编制调试递归下降分析程序,以便对任意输入的符号串进行分析。本次实验的目的主要是加深对递归下降分析法的理解。 二、实验要求: 对下列文法,用递归下降分析法对任意输入的符号串进行分析: (1)E->TG (2)G->+TG|—TG (3)G->ε (4)T->FS (5)S->*FS|/FS (6)S->ε (7)F->(E) (8)F->i 输出的格式如下: (1)递归下降分析程序,编制人:姓名,学号,班级 (2)输入一以#结束的符号串(包括+—*/()i#):在此位置输入符号串例如:i+i*i# (3)输出结果:i+i*i#为合法符号串 备注:输入一符号串如i+i*#,要求输出为“非法的符号串”。 注意: 1.表达式中允许使用运算符(+-*/)、分割符(括号)、字符i,结束符#; 2.如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好); 三、实验过程:

程序设计: 1.模块设计:将程序分成合理的多个模块(函数),每个模块做具体的同一事情。 2.写出(画出)设计方案:模块关系简图、流程图、全局变量、函数接口等。 程序编写: 1.定义部分:定义常量、变量、数据结构。 2.初始化:从文件将输入符号串输入到字符缓冲区中。 3.利用递归下降分析法,对每个非终结符编写函数,在主函数中调用文法开始符号的函数。 四、实验结果 (1)程序流程图 (2)运行结果 示例程序: #include <> #include<> #include<> #include<> char a[50] ,b[50],d[500],e[10]; char ch; int n1,i1=0,flag=1,n=5; int E(); int E1(); int T(); int G();

递归算法详解

递归算法详解 C通过运行时堆栈支持递归函数的实现。递归函数就是直接或间接调用自身的函数。 许多教科书都把计算机阶乘和菲波那契数列用来说明递归,非常不幸我们可爱的著名的老潭老师的《C语言程序设计》一书中就是从阶乘的计算开始的函数递归。导致读过这本经书的同学们,看到阶乘计算第一个想法就是递归。但是在阶乘的计算里,递归并没有提供任何优越之处。在菲波那契数列中,它的效率更是低的非常恐怖。 这里有一个简单的程序,可用于说明递归。程序的目的是把一个整数从二进制形式转换为可打印的字符形式。例如:给出一个值4267,我们需要依次产生字符‘4’,‘2’,‘6’,和‘7’。就如在printf函数中使用了%d格式码,它就会执行类似处理。 我们采用的策略是把这个值反复除以10,并打印各个余数。例如,4267除10的余数是7,但是我们不能直接打印这个余数。我们需要打印的是机器字符集中表示数字‘7’的值。在ASCII码中,字符‘7’的值是55,所以我们需要在余数上加上48来获得正确的字符,但是,使用字符常量而不是整型常量可以提高程序的可移植性。‘0’的ASCII码是48,所以我们用余数加上‘0’,所以有下面的关系: ‘0’+ 0 =‘0’ ‘0’+ 1 =‘1’ ‘0’+ 2 =‘2’ ... 从这些关系中,我们很容易看出在余数上加上‘0’就可以产生对应字符的代码。接着就打印出余数。下一步再取商的值,4267/10等于426。然后用这个值重复上述步骤。 这种处理方法存在的唯一问题是它产生的数字次序正好相反,它们是逆向打印的。所以在我们的程序中使用递归来修正这个问题。 我们这个程序中的函数是递归性质的,因为它包含了一个对自身的调用。乍一看,函数似乎永远不会终止。当函数调用时,它将调用自身,第2次调用还将调用自身,以此类推,似乎永远调用下去。这也是我们在刚接触递归时最想不明白的事情。但是,事实上并不会出现这种情况。 这个程序的递归实现了某种类型的螺旋状while循环。while循环在循环体每次执行时必须取得某种进展,逐步迫近循环终止条件。递归函数也是如此,它在每次递归调用后必须越来越接近某种限制条件。当递归函数符合这个限制条件时,它便不在调用自身。 在程序中,递归函数的限制条件就是变量quotient为零。在每次递归调用之前,我们都把quotient除以10,所以每递归调用一次,它的值就越来越接近零。当它最终变成零时,递归便告终止。 /*接受一个整型值(无符号0,把它转换为字符并打印它,前导零被删除*/

递归算法的优缺点

○1优点:结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性,因此它为设计算法、调试程序带来很大方便。 ○2缺点:递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多。 边界条件与递归方程是递归函数的二个要素 应用分治法的两个前提是问题的可分性和解的可归并性 以比较为基础的排序算法的最坏倩况时间复杂性下界为0(n·log2n)。 回溯法以深度优先的方式搜索解空间树T,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树T。 舍伍德算法设计的基本思想: 设A是一个确定性算法,当它的输入实例为x时所需的计算时间记为tA(x)。设Xn是算法A的输入规模为n的实例的全体,则当问题的输入规模为n时,算法A所需的平均时间为 这显然不能排除存在x∈Xn使得的可能性。希望获得一个随机化算法B,使得对问题的输入规模为n的每一个实例均有 拉斯维加斯( Las Vegas )算法的基本思想: 设p(x)是对输入x调用拉斯维加斯算法获得问题的一个解的概率。一个正确的拉斯维加斯算法应该对所有输入x均有p(x)>0。 设t(x)是算法obstinate找到具体实例x的一个解所需的平均时间 ,s(x)和e(x)分别是算法对于具体实例x求解成功或求解失败所需的平均时间,则有: 解此方程可得:

蒙特卡罗(Monte Carlo)算法的基本思想: 设p是一个实数,且1/2

递归下降分析算术表达式

递归下降分析算术表达式 计算机092—07 邹芬芬 ●实验目的: (1)掌握自上而下语法分析的要求与特点。 (2)掌握递归下降语法分析的基本原理和方法。 (3)掌握相应数据结构的设计方法。 ●实验内容: 编程实现给定算术表达式的递归下降分析器。 算术表达式文法如下:E→E+T | T T→T*F | F F→(E) | i ●设计分析 题目所给的文法不为LL(1)文法,应改写成如下文法: E →TE2 E2→+TE2 |∑ T →FT2 T2→*FT2 | ∑ F →(E) | i 采用递归下降分析法时,需要求出E2和T2 的FOLLOW集: FOLLOW(E2)={),#} FOLLOW(T2)={+,),#} 递归下降分析法是确定的自上而下分析法,基本思想是,对文法中的每个非终结符编写一个函数,每个函数的功能是识别由该非终结符所表示的语法成分。因此需要分别构造E,E2,T,T2,F函数来执行自己的识别功能,根据文法的内容顺序决定函数的识别功能。advance函数用于字符串的推进,input函数用于字符串的输入。 ●程序代码 #include using namespace std; char a[80]; // 字符串的存入 char sym; // 单个的判断字符 int i=0; // 字符串下标 void E(); // 功能识别函数 void E2(); // 功能识别函数 void T(); // 功能识别函数 void T2(); // 功能识别函数 void F(); // 功能识别函数 void input(); // 输入函数

递归算法工作栈的变化详解

通常,一个函数在调用另一个函数之前,要作如下的事情:a)将实在参数,返回地址等信息传递给被调用函数保存; b)为被调用函数的局部变量分配存储区;c)将控制转移到被调函数的入口. 从被调用函数返回调用函数之前,也要做三件事情:a)保存被调函数的计算结果;b)释放被调函数的数据区;c)依照被调函数保存的返回地址将控制转移到调用函数.所有的这些,不论是变量还是地址,本质上来说都是"数据",都是保存在系统所分配的栈中的. ok,到这里已经解决了第一个问题:递归调用时数据都是保存在栈中的,有多少个数据需要保存就要设置多少个栈,而且最重要的一点是:控制所有这些栈的栈顶指针都是相同的,否则无法实现同步. 下面来解决第二个问题:在非递归中,程序如何知道到底要转移到哪个部分继续执行?回到上面说的树的三种遍历方式,抽象出来只有三种操作:访问当前结点,访问左子树,访问右子树.这三种操作的顺序不同,遍历方式也不同.如果我们再抽象一点,对这三种操作再进行一个概括,可以得到:a)访问当前结点:对目前的数据进行一些处理;b)访问左子树:变换当前的数据以进行下一次处理;c)访问右子树:再次变换当前的数据以进行下一次处理(与访问左子树所不同的方式). 下面以先序遍历来说明: void preorder_recursive(Bitree T) /* 先序遍历二叉树的递归算法*/ { if (T) { visit(T); /* 访问当前结点*/ preorder_recursive(T->lchild); /* 访问左子树*/ preorder_recursive(T->rchild); /* 访问右子树*/ } } visit(T)这个操作就是对当前数据进行的处理, preorder_recursive(T->lchild)就是把当前数据变换为它的左子树,访问右子树的操作可以同样理解了. 现在回到我们提出的第二个问题:如何确定转移到哪里继续执行?关键在于一下三个地方:a)确定对当前数据的访问顺序,简单一点说就是确定这个递归程序可以转换为哪种方式遍历的树结构;b)确定这个递归函数转换为递归调用树时的分支是如何划分的,即确定什么是这个递归调用树的"左子树"和"右子树"c)确定这个递归调用树何时返回,即确定什么结点是这个递归调用树的"叶子结点".

递归下降分析器设计与实现

实验二递归下降分析器设计与实现 1、实验目的: (1)掌握自上而下语法分析的要求与特点。 (2)掌握递归下降语法分析的基本原理和方法。 (3)掌握相应数据结构的设计方法。 2、实验内容: 编程实现给定算术表达式的递归下降分析器。 算术表达式文法如下: E-->E+T|T T-->T*F|F F-->(E)|i 3、设计说明: 首先改写文法为LL(1)文法;然后为每一个非终结符,构造相应的递归过程,过程的名字表示规则左部的非终结符;过程体按规则右部符号串的顺序编写。4、设计分析 这个题目属于比较典型的递归下降语法分析。需要先将原算术表达式方法改写为LL(1)文法为: E-->TE' E'-->+TE'|ε T-->FT' T'-->*FT'|ε F-->(E)|i 然后再为每个非终结符设计一个对应的函数,通过各函数之间的递归调用从而实现递归下降语法分析的功能。具体方法为: (1)当遇到终结符a时,则编写语句 If(当前读到的输入符号==a)读入下一个输入符号 (2)当遇到非终结符A时,则编写语句调用A()。 (3)当遇到A-->ε规则时,则编写语句 If(当前读到的输入符号不属于Follow(A))error() (4)当某个非终结符的规则有多个候选式时,按LL(1)文法的条件能唯一地选择一个候选式进行推导. 5、程序代码 #include void E(); void T(); void E1(); void T1(); void F();

char s[100]; int i, SIGN; int main() { printf("请输入一个语句,以#号结束语句(直接输入#号推出)\n"); while( 1 ) { SIGN = 0; i=0; scanf("%s",&s); if( s[0] == '#') return 0; E(); if(s[i]=='#') printf("正确语句!\n"); printf("请输入一个语句,以#号结束语句\n"); } return 1; } void E() { if(SIGN==0) { T(); E1(); } } void E1() { if(SIGN==0) {

04.递归算法讲解

1.用递归法计算n! 【讲解】 递归是算法设计中的一种基本而重要的算法。递归方法即通过函数或过程调用自身将问题转化为本质相同但规模较小的子问题,是分治策略的具体体现。 递归方法具有易于描述、证明简单等优点,在动态规划、贪心算法、回溯法等诸多算法中都有着极为广泛的应用,是许多复杂算法的基础。 递归概述 一个函数在它的函数体内调用它自身称为递归(recursion)调用。是一个过程或函数在其定义或说明中直接或间接调用自身的一种方法,通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。 使用递归要注意以下几点: (1)递归就是在过程或函数里调用自身; (2)在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。 例如有函数r如下: int r(int a) { b=r(a?1); return b; } 这个函数是一个递归函数,但是运行该函数将无休止地调用其自身,这显然是不正确的。为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。 构造递归方法的关键在于建立递归关系。这里的递归关系可以是递归描述的,也可以是递推描述的。 例4-1 用递归法计算n!。 n!的计算是一个典型的递归问题。使用递归方法来描述程序,十分简单且易于理解。 (1)描述递归关系 递归关系是这样的一种关系。设{U 1,U 2 ,U 3 ,…,U n ,…}是一个序列,如果从某一项k开始, U n 和它之前的若干项之间存在一种只与n有关的关系,这便称为递归关系。 注意到,当n≥1时,n!=n*(n?1)!(n=0时,0!=1),这就是一种递归关系。对于特定的k!,它只与k与(k?1)!有关。 (2)确定递归边界 在步骤1的递归关系中,对大于k的U n 的求解将最终归结为对U k 的求解。这里的U k 称 为递归边界(或递归出口)。在本例中,递归边界为k=0,即0!=1。对于任意给定的N!,程序将最终求解到0!。 确定递归边界十分重要,如果没有确定递归边界,将导致程序无限递归而引起死循环。例如以下程序: #include int f(int x) { return(f(x?1));}

递归算法详解完整版

递归算法详解标准化管理处编码[BBX968T-XBB8968-NNJ668-MM9N]

递归 冯文科一、递归的基本概念。 一个函数、概念或数学结构,如果在其定义或说明内部直接或间接地出现对其本身的引用,或者是为了描述问题的某一状态,必须要用至它的上一状态,而描述上一状态,又必须用到它的上一状态……这种用自己来定义自己的方法,称之为递归或递归定义。在程序设计中,函数直接或间接调用自己,就被称为递归调用。 二、递归的最简单应用:通过各项关系及初值求数列的某一项。 在数学中,有这样一种数列,很难求出它的通项公式,但数列中各项间关系却很简 a与前面临近几项之间的关单,于是人们想出另一种办法来描述这种数列:通过初值及 n 系。 要使用这样的描述方式,至少要提供两个信息:一是最前面几项的数值,一是数列间各项的关系。 比如阶乘数列 1、2、6、24、120、720…… 如果用上面的方式来描述它,应该是: a的值,那么可以很容易地写成这样: 如果需要写一个函数来求 n

这就是递归函数的最简单形式,从中可以明显看出递归函数都有的一个特点:先处理一些特殊情况——这也是递归函数的第一个出口,再处理递归关系——这形成递归函数的第二个出口。 递归函数的执行过程总是先通过递归关系不断地缩小问题的规模,直到简单到可以作为特殊情况处理而得出直接的结果,再通过递归关系逐层返回到原来的数据规模,最终得出问题的解。 以上面求阶乘数列的函数) f为例。如在求)3(f时,由于3不是特殊值,因此需 (n 要计算)2( 3f,但)2(f是对它自己的调用,于是再计算)2(f,2也不是特殊值,需要计 * 算)1( f,返回 )1(= 2f,需要知道)1(f的值,再计算)1(f,1是特殊值,于是直接得出1 * 上一步,得2 3 * )2( )3(= = f,从而得最终 =f )1( 3 2 * * )2(= =f 2 f,再返回上一步,得6 解。 用图解来说明,就是

递归算法的优缺点

递归算法的优缺点: ○ 1优点:结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性,因此它为设计算法、调试程序带来很大方便。 ○2缺点:递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多。 边界条件与递归方程是递归函数的二个要素 应用分治法的两个前提是问题的可分性和解的可归并性 以比较为基础的排序算法的最坏倩况时间复杂性下界为0(n·log2n)。 回溯法以深度优先的方式搜索解空间树T ,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树T 。 舍伍德算法设计的基本思想: 设A 是一个确定性算法,当它的输入实例为x 时所需的计算时间记为tA(x)。设Xn 是算法A 的输入规模为n 的实例的全体,则当问题的输入规模为n 时,算法A 所需的平均时间为 这显然不能排除存在x ∈Xn B ,使得对问题的输入规模为n 拉斯维加斯( Las Vegas )算法的基本思想: 设p(x) 是对输入x 调用拉斯维加斯算法获得问题的一个解的概率。一个正确的拉斯维加斯算法应该对所有输入x 均有p(x)>0。 设t(x)是算法obstinate 找到具体实例x 的一个解所需的平均时间 ,s(x)和e(x)分别是算法对于具体实例x 蒙特卡罗(Monte Carlo)算法的基本思想: 设p 是一个实数,且1/2

相关文档
最新文档