从零开始学Excel VBA
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第5章 设计VBA算法

在生活中,做任何事情都会提前将事情的步骤设计好,到实际操作的过程中按照预先设计好的步骤逐步往下做就行了,编制程序同样如此。在程序设计中,将需要做的操作按实际需要编排顺序,然后用程序设计语言实现,就完成了程序的编制。其中各个操作顺序的排列就是程序设计中所说的算法。本章将学习的主要内容有:

❑ 掌握算法的基本概念、特点、表示形式等。

❑ 了解VBA中常用的算法。

5.1 算法概述

从广义的角度来看,任何解决问题的方法都可以说是一个算法。算法中都描述了解决面对的问题时,需要哪些操作步骤,这些操作步骤在顺序上是怎样安排的。因此,对于一个程序而言,算法就像程序的灵魂,主导着程序对数据的每一步操作。著名的计算机科学家沃思(Nikiklaus Wirth)曾提出一个公式

        程序 = 数据结构 + 算法

其实一个程序不仅包括上述两项,还需要有描述算法的语言和开发程序所使用的开发工具,以及程序设计所使用的方法和程序所要处理的数据。因此上述公式就该充实为如下所述。

        程序 = 数据结构 + 算法 + 程序设计语言 + 开发工具 + 程序设计方法 + 处理数据

在上述公式中,数据结构指数据之间的相互关系,即数据的组织方式,其包括了数据元素之间的逻辑关系、数据的存储结构和对数据所进行的运算。数据的逻辑结构是指从具体问题抽象出来的数学模型;数据的存储结构是指数据元素及其相互关系在计算机内的表示方法。

从上面的公式中可以看到算法在程序设计中的重要性,可以说没有算法,就没有程序。本节将介绍几个简单的算法例子,进而引出算法的基本特点,并围绕其基本特点对算法设计做一个全面的讨论,使读者对算法有一个本质性的认识。

Tips

算法分为数值运算算法和非数值运算算法。数值运算算法指求解数学运算方法,例如求解高次方程的根,求解矩阵的加、减、乘、除运算等。非数值运算指的是事务管理应用、车辆调度管理、信息检索应用等。最初计算机只是用来处理科学计算数据,经过多年发展以后,计算机在非数值计算领域的应用已经远远超过了数值计算的应用,计算机在各行各业已得到了广泛应用。在本节所介绍的Excel 2007 VBA中既有数值计算方面的应用,也有非数值计算方面的应用。

5.1.1 简单算法举例

本节将列举几个常用算法,方便读者将生活中的方法与计算机编程结合在一起。在算法的介绍上,首先分析问题,按照人的思维来处理问题,由于人的思维具有智能性,机器具有机械性和准确性,所以面对问题时,还需要将问题转化为机器能够处理的问题。

【例5-1】计算加法1+2+3+4+5+6。常规的计算方法是按下述步骤进行的。

(1)计算1+2的和得3。

(2)将1+2的和与3相加得6。

(3)将1+2+3的和与4相加得10。

(4)将1+2+3+4的和与5相加得15。

(5)将1+2+3+4+5的和与6相加得21。计算完毕最终得结果21。

从上述计算过程中可知,此计算结果正确,但是计算过程比较麻烦。当数据比较多时,如要计算1+2+3+4+5+…+10000时,需要计算9999次,而且每次都要使用前一步骤的结果,此种方法不便于转化成计算机的操作。因此需要分析问题的特点,将其转化成计算机能执行的操作。

从整体上来看,此问题属于数值计算问题,由于数值计算问题在数学中都有比较成熟的数学模型,所以可从数学角度来求解此问题。从形式上看1+2+3+4+5+6,属于等差数列的数学模型,因此,可以采用等差数列的计算方法。令等差数列的首项a1=1,末项a n=6,公差d=1,由题中条件根据项数计算公式1计算得项数6,由等差数列求和公式计算数列和为21。

从上述分析可知,采用公式计算法计算方便,并且比较符合计算机的工作方式,只需要给定计算公式和公式中各个项的数值就可以计算出数列和。转化为便于机器实现的算法描述如下。

(1)赋予首项a1的值为1。

(2)赋予末项an的值为6。

(3)计算公差d得1。

(4)将前述3项代入项数计算公式得项数n

(5)将前述各个数值代入等差数列求和公式,即可计算数列的和。

即使项数比较多的数列也不会影响计算复杂度,因为所采用的算法是相同的,算法结束时,结果也就计算出来了。在计算的速度问题上,也不会有任何影响,因为计算机是高速运行计算设备,作为用户是不会感觉到计算速度的问题的。此种算法比较适合用顺序结构的设计方法,自上而下运行至程序末尾,最后输出计算结果。

【例5-2】在食品厂,生日蛋糕的制作需要经过一系列工序。在做生日蛋糕之前,需要知道生日蛋糕的所需使用的原料及各原料的用量。制作生日蛋糕需要按下述步骤进行操作。

(1)按实际需要准备原料。

(2)按生日蛋糕的配料方式将所需原料配在一起。

(3)对原料进行烘烤,制作蛋糕坯。

(4)用奶油在蛋糕坯上进行裱花。

(5)将生日蛋糕包装进蛋糕盒。

(6)将生日蛋糕产品配送到门店,进行生日蛋糕产品的销售。

上述过程就是一个生日蛋糕的制作算法,其中各个步骤说明了该步所需进行的操作。具体到程序设计中,准备原料就相当于程序设计中的声明变量,原料就相当于待处理的数据,烘烤原料、制作蛋糕坯就相当于程序设计中对数据所执行的操作,将生日蛋糕配送到门店就相当于程序设计中对数据操作进行完毕,输出程序的执行结果。

从此例中,可以看到,程序设计和日常的工作生活是相通的,没有什么高深的理论和非常难于理解的知识,其实程序设计的核心是算法设计,算法设计的原理同于生活中做事情、干工作。只要能安全、有效、稳定、可靠、快速地把问题解决了,就达到了要求;当今的计算机不管是硬件还是软件,早已突破了容量限制及工作方式的转变,在做程序设计时,没有必要再追求程序设计的技巧,只要所设计的算法能安全、有效、稳定、可靠、快速地解决问题,那么就是一个好的算法。

【例5-3】给定一个年份,例如1999年,判定给定的年份是不是闰年,并将结果输出。

对于此问题,首先需要考虑的是什么样的年份才是闰年,年份需要符合什么条件。闰年的条件有两条,只要符合其中一个条件,即可判定该年份为闰年。

(1)如果年份能被4整除但不能被100整除,则该年份是闰年。

(2)如果年份能被100整除但同时也能被400整除,则该年份也是闰年。

按常规算法,对于一个给定的年份,其判定步骤如下所述。

(1)判定该年份是否符合第1个条件,如果符合,则说明该年份是一个闰年。

(2)如果不符合,则验证是否满足第2个条件,如果条件满足,则说明该年份是一个闰年。

(3)否则,该年份不是一个闰年。

上述这种算法能够验证出一个年份是否为一个闰年。仔细观察,可以发现闰年是一定可以被4整除的,不能被4整除的肯定不是闰年,因此可将算法设计如下。

(1)能否被4整除可以作为判定的第1个条件,如果不能被4整除,则直接输出该年份不是闰年。

(2)如果可以被4整除,再验证是否不能被100整除或者能被100整除但也能被400整除。

(3)如果此条件满足,则输出该年份是闰年。

(4)否则输出该年份不是闰年。

此种算法在编制程序时可以降低逻辑复杂度,便于理解。所以在设计算法时,要注意观察,将人的灵活性与机器的准确性结合起来,方便程序编制;同时设计出来的算法,效率还很高。此种算法比较适合于用程序设计中的选择结构来实现。

【例5-4】有一个班级有50名学生,其考试成绩保存在名为“成绩.txt”的文件中,现需要将成绩大于85分的学生姓名输出。常规算法的执行步骤如下所述。

(1)打开“成绩.txt”文件。

(2)拿出一个学生的成绩检验其是否大于85分,如果满足条件,则输出学生的姓名。

(3)否则拿出下一个学生的成绩进行比较。直到比较完第50个学生的成绩为止。

从常规的算法中,可以看到,比较50个学生的成绩是一个重复的过程,每次都是取出一个学生的成绩,然后决定是否将该学生成绩输出。此过程是一个循环往复的过程,转换成计算机能够执行的算法设计如下。

(1)打开文件“成绩.txt”。

(2)将所有学生的姓名和成绩信息读入一个数组中。

(3)设置一个循环结构,遍历数组;在遍历的过程中,检验学生成绩是否大于85 分,如果大于85分,则输出学生姓名,否则跳过该学生,读取下一个学生的成绩信息进行检验,直到数组元素被检验完为止。

Tips

循环结构是程序的三大基本结构之一,其主要用于处理有规律、重复的操作。例如,此算法中对50个学生成绩的检验就是一个有规律的、重复的过程操作,所以此例适合采用循环结构来处理。这样人就可以从重复的劳动中脱离出来,机器来代替人操作重复性工作。

5.1.2 算法的特点

从上面的3个例子中,可以了解到什么是算法,如何将实际解决问题的方法转变成适合计算机操作的算法。归纳上述3个例子可以得到算法的特点如下。

(1)算法的有穷性:任何一个算法都是由有限个步骤组成的,并且在执行时间上都是有结束时间的。总之,一个算法不可能由无限多个步骤组成,在执行时间上算法在执行的过程中也不可能是无限的。特别是对于实际问题,都有一个实际存在期限,如果算法不能在有效的时间期限内解决问题,那么此算法也不是一个有穷的算法。

(2)算法的确定性,指算法在操作过程中,每一个步骤在操作对象、操作指令上都是明确的,不能有似是而非的问题。在计算机的世界中,只有0和1两种状态,所以算法在设计时,对数据的操作只能是0和1两种状态之一,不可能存在第3种状态。也就是算法在执行过程中不能有不确定的操作,要求每个步骤都要清晰、明确地描述出针对什么数据,进行什么操作。

(3)算法的有效性,有两层含义,从算法的整体上来讲,算法要能够有效地解决实际问题,从算法的执行步骤上来讲,算法的每一步操作都必须是有效的,例如a=a+0这样的操作就是无效的,在式子 =ba c中,如果c=0,则操作是无效的。

(4)存在输入数据,指算法处理的对象是数据,算法只是一个操作步骤,其中并没有数据,所以必须从外界输入数据,为算法提供操作对象,这样程序才能够正常运行。例5-1 通过在程序内部赋值完成数据输入,例5-3 通过从文件中读入数据完成数据输入。所以输入并不一定要用户通过键盘输入数据,当然用户也可根据需要设定从键盘录入数据。

(5)输出数据,算法就是为了解决实际问题而设计的,所以程序执行完毕后,必须给用户输出处理结果,例5-1输出求和的结果,例5-3输出所有满足条件的学生姓名。当然程序执行结果的输出并不一定要通过屏幕输出,也可以将结果保存到文件中。

5.2 算法的描述方法

算法是用于描述一个问题的解决方法,但是算法在描述问题时,也要有一个类似人们交流语言的工具作为描述载体,通常可以使用自然语言描述、流程图描述、伪代码描述和使用程序设计语言描述。本节将围绕这4种方法进行介绍。

5.2.1 使用自然语言描述法

例5-1 至例5-3 中所使用的算法描述方法都是用自然语言描述。自然语言描述就是运用日常交流语言作为描述算法的工具对算法设计操作步骤。自然语言可以是现今使用的多种语言,例如汉语、英语、法语、德语等都可以用于描述算法。

采用自然语言描述算法,通俗易懂,便于理解,通用性比较强,即使是不懂计算机的人也能够看懂用自然语言描述的算法。但是自然语言有其自身的缺点,例如同样的语言描述在不同的环境中,会使人产生不同的理解,具有一定的歧义性;自然语言描述问题时比较冗长,通常情况下对于比较简单的问题才用自然语言描述。

5.2.2 使用流程图描述法

流程图描述法是美国国家标准化协会ANSI(American National Standard Institute)提出的,目前已被各国程序设计工作者所普遍使用。流程图描述法是采用图形描述法,对于算法阅读者而言,图形描述简洁、易懂、直观形象。

流程图中的元素包括起止框、输入/输出框、判断框、处理框、流程线、连接点、注释框等,其各个元素的对应形状如图5.1所示。在流程图描述法中,一个流程图只允许有一个开始符和一个结束符,因此在流程图的构成元素中起止框在一个流程图中只允许出现两次,一个用于开始,一个用于结束;输入输出框用于描述输入/输出数据;判断框用于描述选择结构的程序语句,所以判断框有两个输出,分别对应“是”和“否”两种条件判定结果。

处理框用于描述顺序结构的程序语句;流程线用于将流程图中的各个元素连接起来;连接点用于将各个流程图连接起来,当流程图比较庞大时,将其合并在一起比较困难,此时就需要将流程图分裂开来,分裂时通常从流程图的某一条流程线处分开,并在此流程线处标注分裂号,同时在另一流程图的入口位置标注同样的分裂号,以表示两个流程图在此标号处连接,如图5.2所示。

图5.1 流程图构成元素

图5.2 流程图连接点

注释框的作用在于为流程图中各个操作添加说明。注释框不是程序流程图的必要构成部分,可以根据需要在流程图的适当位置,为其添加必要的文字说明,以帮助流程图的阅读者更好地理解流程图的作用。例5-1用流程图法描述如图5.3所示,例5-3用流程图法描述如图5.4和图5.5所示,例5-4流程图法描述如图5.6所示。

从上述的几个例子中,可以看到流程图是按自上而下顺序执行的,对于简单的问题,采用流程图描述法直观形象;由于流程图中的流程线可以随意拉向任意方向,所以当算法算得比较复杂时,流程线会变得比较乱,判断符号比较多时,流程线会随条件判定的结果形成任意走向,上上下下的流程线造成多线交叉的局面,即使运用连接点可以缓解此类问题,但仍然无法从根本上解决流程图的混乱局面。这样会使流程图的阅读者顺着流程线转来转去,最终陷入混乱的逻辑走向。

例5-1、例5-3、例5-4是很有代表性的例子,每个例子都代表了程序的3种基本结构之一,其中例5-1是一个顺序结构程序,例5-3是一个选择结构程序,例5-4是一个循环结构程序。这3种基本结构是1966年Bohra和Jacopini提出的。

顺序结构程序指的是程序按从上到下的顺序执行,在程序流程图上看就是处理框描述的信息,按照流程线指示的方向逐步执行至结束。选择结构程序是一种条件判断语句,当条件满足不同条件时,执行对应的语句,在程序流程图中就是判断框描述判断的条件,判断框的出口分别对应了所要执行的语句,两个出口中,只会执行一个出口的语句,因为条件判定结果,要么为真,要么为假。循环结构程序是指设定循环变量初始值和循环进行的条件,在循环内部可以包含顺序结构程序和选择结构程序用于处理数据。在程序流程图中,没有专门的元素描述循环结构的程序,循环结构的程序是由顺序结构与选择结构共同描述的,例如在图5.6中所描述的循环结构。

图5.3 例5-1算法流程图

图5.4 例5-3常规算法流程图

图5.4 例5-3优化后的流程图

图5.6 例5-4算法流程图

5.2.3 使用N-S图描述法

N-S图描述法是1973年美国学者I.Nassi和B.Shneiderman提出的一种全新的流程图表示形式。其表示方法是将全部算法都描述在一个矩形框内,矩形框之间可以相互嵌套,矩形框内省略了传统流程图的流程线。N-S图对3种基本结构的描述如图5.7至图5.10所示。

图5.7 顺序结构描述

图5.8 选择结构描述

图5.9 while型循环描述

图5.10 直到型循环描述

Tips

循环结构分为2种,即while型循环和直到型循环,还有一种for型循环,其本质上同于while型循环。在传统流程图中这两种循环在表示形式上只有一种,需要靠转化循环条件来表示。在N-S图中可以用两种结构明确表示两种循环。这两种循环的区别在于:条件判定的先后,是否让循环体执行一次,对于while循环,循环体只有当条件满足时才可以执行,而直到型循环的循环体至少会执行一次,当条件满足时会有后续的循环,条件不满足时,会跳出循环体,继续执行其余程序语句。

N-S图表示法与自然语言描述法相比,仍然具有简洁直观、通俗易懂的优点,描述过程更容易操作。与传统流程图表示法相比,N-S图看上去更简练,消去了流程线之后,使算法的执行流程变得不再混乱,不需要再顺着流程线查找算法操作。N-S图始终是按照从上到下的顺序执行,不会在执行到某一步时,又跳回前面某一步。

下面将例5-1、例5-3、例5-4算法用N-S图描述,如图5.11至图5.13所示。

图5.11 例5-1 N-S图描述

图5.12 例5-3 N-S图描述

图5.13 例5-4 N-S图描述

Tips

在图5.13中,成绩大于85分的条件下面有一个空格,其中没有任何操作表示在条件为假时,不进行什么操作,此处为空语句。

从上述几个例子中可以看到N-S图在制作方法和阅读上都非常方便,其中表示的操作都是由3种基本程序结构组成的。每个模块都有自己的操作范围,操作范围都在模块的矩形框内。如果一个算法不是3种基本结构之一,则不能直接用N-S图描述,需要将其转化为结构化的程序后才能由N-S图表示。从整体上看N-S图,就像一个多层次盒子,所以也常常把N-S图称为盒图。

5.2.4 使用伪代码描述法

传统流程图描述法适合描述比较简单的算法,N-S图适合描述结构化的算法,从阅读的角度讲,两种描述方法都能简洁直观地描述算法,但是作图过程是复杂的,特别是对于N-S图,在作图时,经常会因为初始的矩形框空间不够,而不得不重新再画一个矩形框。从上述分析中可知,传统流程图和N-S图描述法只适合于描述算法,而不适合在设计过程中使用。伪代码描述法主要是为方便用户在设计算法时使用而设计的,伪代码是一种介于自然语言和编程语言之间的语言。

伪代码一般是英文的翻译,采用编程语言中的部分关键字描述算法,例如在进行条件判断时,使用if…then…else语句,这种方法既符合人们正常的思维方法,便于理解,同时当转化成程序设计语言时也比较方便。

【例5-5】例5-1采用伪代码描述法如下所述。

        01  Begin
        02      1=>a1
        03      6=>an
        04      1=>d
        05      (an-a1)/d+1=>n
        06      (a1+an)*n/2=>s
        07      Print s
        08  End

【代码解析】第2~4行为数列的首项和末项赋值,第5行为数列的项数,第7行为求数列的和。

【例5-6】例5-3常规算法采用伪代码描述法如下所述。

        01  Begin
        02      输入year
        03      If  year能整除4而不能整除100  then
        04           Print  year是一个闰年
        05      Else
        06           If  year能被100整除但也能被400整除  then
        07               Print  year是一个闰年
        08           Else
        09               Print  year不是一个闰年
        10  End

【代码解析】第2行用于输入一个年份,第3行用于判断能不能被4整除,第5~7行用于判断输入的年份能不能既被100整除,同时又能被400整除,第8~9行用于判断输入的年份不满足闰年条件,则输出的年份不是闰年。

【例5-7】例5-3优化后的算法采用伪代码描述法如下所述。

        01  Begin
        02      输入year
        03      If  year能被4整除  then
        04           If  year不能被100整除或(year能被100整除并且也能被400整除)then
        05               Print  year是一个闰年
        06           Else
        07               Print  year不是一个闰年
        08      Else
        09           year不是一个闰年
        10  End

【代码解析】第2行用于输入年份,第3行首先判断该年份能不能被4整除,第4行用于控制如果输入的年份能被4整除,同时又不能被100整除或者输入的年份既能被100整除也能被400整除,则输入的年份是一个闰年,否则输入的年份不是一个闰年。第5 行用于输出判断的结果是一个闰年,第6~9行用于输出判断的结果不是一个闰年。

【例5-8】例5-4采用伪代码描述法如下所述。

        01  Begin
        02      打开文件“成绩.txt”
        03      将学生的姓名和成绩读入到数组a中
        04      0=>i
        05  While i < 50
        06      {
        07           If  a[i]的成绩大于85分then
        08               Print  a[i]所对应的学生姓名
        09           i + 1=>i
        10      }
        11  End

【代码解析】第2行用于打开文件,第3行将学生的信息读入到数组中,第4行为计数器清零,第5~10行使用循环输出成绩大于85分的学生姓名。

从上面几个例子中可以看到伪代码描述法描述的算法,结构清晰,代码简单,可读性强,非常接近于程序设计语言,其中所用语句均为日常生活用语,通俗易懂。伪代码描述法的结构是靠语句的缩进来保持,所以有规律的缩进对维护伪代码的结构来说非常重要。对于模块语句结构则可用大括号“{}”将其括起来即可,例5-8中while循环语句的循环体。对于运算或条件上的优先级用自然语言描述不清楚时,可将其按优先级顺序用括号“()”分隔。

5.2.5 使用计算机语言描述法

建筑设计师可以运用各种方法在图纸上设计出气势磅礴的高楼大厦,但要让图纸上的高楼大厦挺立于大地之上,却需要实际的施工建设,然后才有现实中的高楼大厦。在计算机科学中,不仅要有详细的算法设计,还必须将这些设计转变成计算机能识别的语言,然后才能用于解决实际问题。图纸上描绘的高楼大厦就像在计算机科学中描述的算法,施工建设就像将算法转变成计算机能够识别的语言。高楼大厦的建成就像用算法解决实际问题后的输出。

使用计算机语言描述算法就是要将算法转变成具体的语言程序。将程序提交给计算机后可由计算机执行,输出计算结果。使用计算机语言描述算法时,一定要遵循所用语言的语法规则,否则实现的算法仍然无法工作。

【例5-9】例5-2中的算法采用VBA语言描述如下,其执行结果如图5.14所示。

        01  Sub求数列的和()
        02    Dim a1 As Integer                   '用于保存数列首项
        03    Dim an As Integer                   '用于保存数列末项
        04    Dim d As Integer                    '用于保存数列公差
        05    Dim n As Integer                    '用于保存数列项数
        06    Dim s As Integer                    '用于保存数列的和
        07    a1 = 1                               '赋予首项值
        08    an = 6                               '赋予末项值
        09    d = 1                                '赋予公差值
        10    n = (an - a1) / d + 1               '计算项数
        11    s = (a1 + an) * n / 2               '计算数列的和
        '输出计算结果
        12    MsgBox "1 + 2 + 3 + 4 + 5 + 6 =  " & s, vbOKOnly, "求数列的和"
        13  End Sub

执行结果如图5.14所示。

【代码解析】第2~6行用于声明变量,第7~9行用于初始化变量,第10行用于计算数列的项数,第11行用于计算数列的和。第12行用于输出求和的结果。

图5.14 求数列的和

Tips

每行以“’”开头引导的汉字是为程序添加的注释,起到方便理解程序语句的作用。

【例5-10】例5-3中的常规算法采用VBA算法描述如下,其执行结果如图5.15所示。

        01  Sub判断闰年()
        02    Dim year As Integer                 '用于保存输入的年份
        03                                    '输入年份
        04    year = CInt(InputBox("请输入需要判断的年份: ", "判断闰年"))
        05                                    '判定闰年并输出结果
        06    If year Mod 4 = 0 And year Mod 100 <> 0 Then
        07       MsgBox "" & year & "是一个闰年", vbOKOnly, "判断闰年"
        08    Else
        09       If year Mod 100 = 0 And year Mod 400 = 0 Then
        10           MsgBox "" & year & "是一个闰年", vbOKOnly, "判断闰年"
        11       Else
        12           MsgBox "" & year & "不是一个闰年", vbOKOnly, "判断闰年"
        13       End If
        14    End If
        15  End Sub

执行结果如图5.15~图5.16所示。

【代码解析】第2~3行声明了一个变量用于保存输入的年份,第4~5 行用于输入年份,第6~9 行用于判断输入的年份是不是闰年,第10~14行用于输出结果。

【例5-11】计算10阶乘即10!。对于比较简单的问题,可直接使用计算机语言描述。

图5.15 输入年份

图5.16 输出判定结果

        01  Sub求阶乘()
        02    Dim i As Integer                    '用于控制循环次数
        03    Dim chengji As Long                 '用于保存求得的积
        04    chengji = 1                         '初始化变量chengji为1,用于保存求积的结果
        05    i = 1
        06                                         '循环求阶乘
        07    While i <= 10
        08       chengji = chengji * i
        09       i = i + 1
        10    Wend
        11                                         '输出计算结果
        12    MsgBox "10阶乘为: 10! = " & chengji, vbOKOnly, "求阶乘"
        13  End Sub

执行结果如图5.17所示。

【代码解析】第2~3行用于声明变量,第4~6行初始化变量,第7~10行用于循求阶乘,第12行用于输出所求得的结果。

图5.17 10的阶乘计算结果

5.3 VBA常用算法

在理解了算法的基本概念和表示方法后,就可以设计实际需要的算法了,在长期的实践过程中,一些经常使用的算法,已经被总结归纳,形成模板式的算法。本节将介绍一些在VBA中经常使用的算法,例如选择排序法,字符串的定位等。

5.3.1 选择排序法

选择排序法是最简单的排序方法,其算法的核心思想是每次从待排序的序列中选择一个最小的数,放到当前位置已排顺序的序列的最后。以给定的10个整数为例,使其按从小到大的顺序排列。按核心思想的处理方法,需要两层循环来控制程序运行,第1层循环用于控制排序的趟数,第2层用于控制每次从未排好序的余下序列中选择一个最小的数,加到已排好序的序列尾部。选择排序法采用伪代码描述法如下所述。

        01  Begin
        02  声明3个变量i,j,k;i用于控制外层循环,j用于控制内层循环,k用于记录每次找到的最小的数的下标位置。
        03  i = 0
        04  while i < 9
        05      {
        06           k = i
        07           j = i + 1
        08           while j < 10
        09               {
        10  if数组中第j个数值小于第k个数值then
        11                        k = j
        12               }
                If  k不等于i  then
        13  if  i不等于k   then
        14              {
        15                   第i个数组元素与第k个数组元素调换位置
        16               }
        17  输出已排好序的数组a
        18      }
        19  End

【代码解析】第2行用于声明变量,第3行用于初始循环变量,该变量用于控制循环次数,第4行用于控制循环,第6~12行用于选择余下的数据中的最小的数据,第13~16行用于将所选择的数据放到合适的位置上。第17行用于输出排好序的数组。

5.3.2 自左至右字符串定位算法

此算法描述的是给定一个主字符串mainstr和子字符串substr,检测出substr第1次在mainstr中出现的位置。每次从mainstr中取出与substr等长的字符串,与其比较,如果相同,则结束检测;否则将mainstr中下移一个字母,再取出与substr等长的字符串,进行比较,直至找到与其相同的位置或主串中余下字符串的长度小于子串的长度。此算法用伪代码描述法如下所述。

        01  Begin
        02      If  mainstr的长度小于substr的长度then
        03           Print子串长度大于主串长度,无法检测
        04      Else
        05           i = 0
        06           flag = false
        07           lm=mainstr的长度
        08           ls= substr的长度
        09           while lm-i<ls  and  not flag
        10               {
        11                   从mainstr中第i个位置开始,取出ls个字符,存于字符串temp中
        12                   If  temp = substr  then
        13                        令flag = true
        14                   Else
        15                        i = i + 1
        16               }
        17           If   flag = true  then
        18                Print  i值
        19           Else
        20               Print  mainstr中不包含substr
        21  End

【代码解析】第2~3行用于判断子字符串的长度是否大于主字符串的长度,如果大于则无法进行检测,若小于再进行如下判断,第4~18行用于确定子字符串的位置,如果不存在子串,则输出的主字符串中不包含该子字符串。

5.3.3 顺序查找算法

顺序查找算法是最简单的查找算法,也是一种通用的查找算法,适合于任何情况下的查找。其算法的核心思想是将所需查找的关键字与列表中所有的关键字逐一比较,然后输出找到的关键字所在的位置,或者所在的列表与该关键字相同的项。此算法用伪代码描述法如下所述。

        01  Begin
        02  声明n用于保存列表长度,key表示要查找的关键字,list[n]表示列表中的关键字,flag表示是否找到
        03    i = 0
        04    flag = false
        05    while   i < n  and  not flag
        06       {
        07      if key = list[i] then
        08           flag = true
        09      else
        10           i = i + 1
        11      }
        12   If  flag = true  then
        13      Print  i的值
        14      Else
        15           Print列表中不存在该关键字
        16  End

【代码解析】第2行声明相关变量,第3~4行用于初始化循环控制变量。第5~11行用于实现查找功能,第12~15行用于输出查找的结果。

5.4 习题

1. 什么是算法?

2. 程序的灵魂是什么?一个完整的程序由哪些部分组成?

3. 描述算法常用的方法有哪些?

4. 已知a= 5,b=8,c=10,求a、b、c3个数中的最大数,用伪代码描述法描述该算法。