算法问题求解基础

有一个很重要的观点是:可以认为算法是问题的程序化解决方案。这里的解决方案并不是精确的答案,而是用于获取答案的精确的指令。

以下是算法在设计和分析过程中的一系列典型步骤:

一、理解问题

在进行算法输入时,我们要首先了解该算法解决问题的一个实例,严格确定算法需要处理的实例的范围是非常重要的。例如,在上一节所介绍的解决最大公约数问题的三种算法,它们能处理的例子的范围是不同的;如果不注意这一点的话,可能解决大多数例子时不会有什么影响,但是在处理某些边界值时可能会出现错误,正确的算法是要求能够解决所有实例的,所有的合法输入都应该能够得到对应的合法输出。

二、计算设备的性能

在完全了解所要处理的问题之后,就要关注来运行算法的计算设备的性能。如今我们的大多数算法都要在以类冯·诺依曼的机器为代表的运行,这个体系结构的根本在于随机存取机(RAM),运行方式为指令逐条运行,每次执行一步操作;在这种机器上运行的算法被称为顺序算法。除此之外,对于计算机进行更新后还产生了在同一时间执行多条操作的并行算法,但是这并不能撼动顺序算法的基石作用。

如果把设计算法当作是科学实验,那么我们并不需要考虑计算机的计算速度和存储容量;如果把算法当作是实用工具的话,对于计算速度以及存储容量的需求要取决于你所解决的实际问题是如何的,对于一些十分复杂的问题,需要对于海量的数据进行处理,那么我们就需要较快的速度与较大的容量。

三、精确算法与近似算法

直白来说就是你在处理问题时是要精确计算还是估算结果,对于一些在特定情况下无法求得确切值的问题我们一般要使用近似算法,例如求解平方根、求解定积分等问题;还有一种情况使用近似算法,就是要处理的问题极其复杂,如果使用精确的算法来进行求解,恐怕会消耗极长的时间,因此通过近似来估算;

四、算法设计的一般方法

通过以上的几个操作,我们的准备阶段就结束了,接下来就要介绍如何来设计算法。算法设计技术(策略/范例)是指用算法解题的一般性方法,用于解决不同计算领域的多种问题。

确定适当的数据结构

设计人员需要根据算法执行的操作为算法选择适合的数据结构。例如,在介绍埃拉托色尼筛选法时,如果使用链表而不是数组,它的运行时间会更长。此外,还有一些算法设计技术非常依赖于对问题实例的数据进行构造和重构;

算法的描述

当今对于算法的常用描述有文字(自然语言)和伪代码等形式;但是自然语言由于自身的局限性而不能严谨而简明的描述算法;伪代码是自然语言和类编程语言组成的混合结构,伪代码描述往往比自然语言要更加的简明严谨,但是它的形式是不固定的。在计算机应用早期,描述算法的主要工具是流程图,但是除了一些简单的算法之外,其他的较为复杂的算法使用这种方法很不便利。当然,这些描述我们还不能够直接的注入到计算机里,我们需要把算法变成由特定语言编写的程序,我们也能把程序看作是一种算法的表述形式。

算法的正确性证明

所谓正确性,就是指我们给定的每一个合法的输入,都能够在有限时间内得到正确的的输出。例如,我们在上一节提到的计算最大公约数的欧几里得算法的正确性就是依赖于等式gcd(m,n)=gcd(n,m mod n)这一条件的正确性。

证明正确性的一般方法是使用数学归纳法,而对于算法不正确的判定则只需要找出一个算法不能够正确处理的实例就够了。对于近似算法而言,我们在证明其正确性时,常常证明其误差不超过预定义的范围即可;

五、算法的分析

除了算法的正确性之外,我们最注重的就是算法的效率了。实际上,有两种算法效率:时间效率(算法运行有多快)和空间效率(算法所需要的额外的存储空间)。

算法还具有简单性,简单的算法更容易理解和实现,相应的程序也能包含更少的bug。我们希望算法应该具有的另一个特性是指一般性,这里的一般性是指算法所解决问题的一般性和算法所接受输入的一般性;

六、为算法写代码

为算法编写程序可能会出现错误或效率低下,就其实用性而言,对程序的验证还需要依赖测试,无论我们实现何种算法,都要对程序进行彻底的测试。算法的正确实现是必要的,但并不是全部,我们还需要注重效率的提升,这方面我们就需要掌握一些技巧,例如合并公共的子表达式、用低开销操作代替高开销操作等 。设计算法是一种工程行为,需要在有限的资源情况下,在互斥的目标之间进行选择衡量。

算法问题求解,既是严谨的科学探索,也是充满智慧的工程实践。从理解问题边界到权衡资源约束,从选择设计策略到验证实现效率,每一步都考验着我们对问题本质的洞察与对工程细节的把控。

在这个过程中,我们不仅是在编写一段段高效的代码,更是在构建一套套解决问题的思维框架。当我们能够清晰地定义问题、审慎地选择策略、严谨地验证结果时,算法便不再是冰冷的指令集合,而是我们手中解决现实挑战、创造价值的有力工具。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐