算法可以理解为这样一种技术,它将告诉计算机怎么做,有人将编程理解为搭积木,直接用别人开发好的组件、库、甚至是类或API就行了,并美其名曰“不用重复发明轮子”。这认为这其实就是所谓的系统集成。

人类解决问题的方式是当遇到一个问题时,首先从大脑中搜索已有的知识和经验,寻找他们之间肯有关系的地方,将一个未知问题做适当转换,转化成一个或多个已知问题进行求解,最后集合起来得到原始问题的解决方案。

编写计算机程序实现算法,也就是设计算法程序的第一步就是让计算机理解问题是什么,这就需要建立现实问题的数学模型,建模的过程就是一个对现实问题的抽象过程,通常也伴随一些合理的假设,其目的是对问题进行简化。运用逻辑思维能力,抓住问题的主要因素,忽略将要因素。逐步用更精确的语言描述问题,最终达到用计算机语言能够描述问题为止。建立数学模型之后,第二个要考虑的问题就是输入输出问题,输入就是将自然语言或人类能够理解的其他表达方式描述的问题转换为数学模型中的数据;输出就是将数学模型中表达的运算结果转换成自然语言或人类能够理解的其他表达方式。最后就是算法的设计,其实就是设计一套对数学模型中的数据的操作和转换步骤,使其能演化最终的结果。

数学模型、输入输出方法和算法步骤是编写计算机程序的三大关键因素,对于非常复杂的数学问题,建立数学模型是非常难的事情,比如天文学家研究的“宇宙大爆炸”模型。对于简单的计算机算法而言,建立数学模型实际上就是设计合适的数据结构的问题。所以,输入输出方式和算法步骤都是基于相应的数据结构设计的,相应的数据结构要能很方便地将原始问题转换成数据结构中的各个属性,也要能很方便地将数据结构中的结果以人们能够理解的方式输出。同时,也要为算法转换过程中各个步骤的演化提供使得的支持。使用线性表还是关联结构,使用树还是图,都是设计输入输出和算法步骤时就要考虑的问题。

数据结构是建立解决问题的数据模型的基础,如果不了解数据结构,就没办法建立数学模型,或者建立的模型不合适导致算法演化困难,甚至无法实现。

采用什么样的数据结构由算法的数学模型决定,但是各不相同的数据结构自身的一些特点反过来也会影响数学模型的选择。数学模型是对问题域的高度抽象,而数据结构又是承载数学模型的基础。在简单的算法中,数学模型的定义有时可能简化成数据结构的定义,即使是复杂的数学模型也是要使用相应定义的数据结构来承载。

数据结构和算法:告诉你如何完成常见的任务,Java的设计者们设计了大量的方法将常用的数据结构和算法封装在里面;

算法:处理问题的步骤,也就是解决问题或完成问题最优的方法或步骤;较少的编码和较少的执行时间;因为计算机的特别是机械性强,速度极快,有优势应用看起来较笨的穷举法;

数据结构:作为处理对象的数据的排列方式;对于存储程序结构的计算机,数据如何存储,直接关系到执行能否有效地完成;实际的内存物理结构是,地址:值;对于程序的编码,则是变量:值;(内存与变量的关系)数组就是利用计算机的机械性,地址的递增而形成的一种数据结构,以数据为基础可以定义出堆、栈、链表、二叉树等典型的数据结构;

就像算法中有典型算法一样,在数据结构中也有典型数据结构,它们都是由老一辈程序员发明创造的。这些数据结构其实都是通过程序从逻辑上改变了内存的物理结构,即数据在内在上呈现出连续分布状态。

数据的结构化是为了算法的可实现、简洁、高效。数组、堆、栈、都是一种有序、有结构的数据存储。编程的思路在于有规律的寻找其规律,没有规律的创造条件让其变得有规律,然后应用判断、循环,某语言的函数、语法,可以简单、粗暴的解决问题。

典型的算法与典型的数据结构

Algoriths+Data Structures=Programs

程序=算法+数据结构

程序={对象}(对象=数据+操作)

算法=操作+控制结构

操作:逻辑运算、算术运算、数据比较、数据传送;

算法的控制结构:顺序、选择、循环;

分类 比例 处理方法 优缺点
数值计算 20% 数学公式 优势:可随机访问;
非数值计算 80% 数据结构 缺点:需要分配一块连续的存储空间;

数值计算算法:

主要用于科学计算。随着“数值分析”的发展,各类数学模型都设计了许多行之有效的算法。如,计算数值积分有梯形法、辛普生法与柯特斯法,对方程式求根有二分法、迭代法或牛顿法,解线性方程组可使用消元法和迭代法等。在这类算法中,算术运算居于主要地位。许多复杂的计算常常被转换为重复进行的简单算术运算。一般,它们使用的数据结构比较简单,“简单变量”加“数组”大致能满足需要。

非数值计算算法

常用于数据管理、实时控制以及人工智能等应用领域。与数值计算的算法不同,逻辑判断通常在这类算法中处于主导的地位,算术去处则居于相对次要的地位。算法处理的内容也从单纯的数值运算扩展到对数据、图形和字符信息的综合处理。这类算法起步略晚,但发展迅速。“排序”和“查找”是在这类算法中发展比较成熟且为大家所熟悉的两方面;

由于这类算法使用的数据结构一般比较复杂,对算法的设计和选择往往在很大程度上依赖于处理对象所用的数据结构。所以,这类算法的设计通常都与数据结构的选择联系在一起,使问题变得比较复杂。20世纪70年代,Wirth提出“算法+数据结构=程序”的公式,正是反映了非数值计算算法的发展需要。

计算机能够处理的问题一般可以分为两类:数值计算问题和非数值计算问题。

1 数值计算问题,如线性方程求解、矩阵的计算等。这类问题一般需要程序设计的技巧和相应的数学知识,而数据结构方法涉及比较少。

2 非数值计算问题,约占据80%的计算机工作时间。这类问题不再是单单需要数学知识,而且还需要设计合理的数据结构才能高效解决问题。

借助于计算机的问题求解过程

客观世界的问题→提取分析→数据、数学模型→设计→数据结构与算法→编码→程序代码(计算机语言)→编译→执行→输出→结束

对于计算机而言,数据的存储与处理是其最本质也是最核心的问题。通常情况下,我们必须先解决数据的存储问题,再讨论数据的处理问题。

数据结构类型:顺序存储结构、链式存储结构、索引存储结构、散列存储结构。

客观世界到计算机世界的映射方法

面向过程的结构化设计方法学:

1 由结构化分析、结构化设计、结构化编码;

2 确定输入、输出数据结构,使用自项向下的设计方法,列出需要解决的最主要的子问题,然后通过解决每个子问题来解决初始的问题。其本质是功能的分解,将系统按功能分解为若干模块,每个模块是实现系统某一功能的程序单元,每个模块都具有输入、输出和过程等基本特性。输入和输出分别是模块需要和产生的数据,

3 如三个对象对应四部分数据,这四部分数据分别是7个模块交互;

4 模块独立性可以通过两个准则来度量:聚合(Cohesion)和耦合(coupling)。一个聚合程序高的模块只完成软件过程内的一个单一的任务,而与程序其他部分的过程交互作用很小,简言之,一个聚合模块应当(理想地)只做一件事;耦合是对模块间关联程度的度量,模块间联系越多,其耦合性越强,同时表明其独立性越差。

5 缺点:分离了数据与操作之间的内在联系;

面向对象程序设计方法学:

OO=objects+classes+inheritance+communication with messages

如果一个程序系统只有对象和消息两个概念,程序设计就是建立一个彼此能发消息的对象集合,我们说这就是面向对象程序设计。

1 针对问题域做面向对象分析,找出问题示解所需要的各种相关对象与类;

2 与系统提供的类库相匹配,找到已有类。

3 若无完全匹配的类,则从相近的类中派生出新的子类。然后进行修正与补充,使之与问题求解所需要的类相吻合。

4 苦既无相近的类,也无匹配的类,则只好设计新的类(新类也可以入库);

5 等所需的类都有了以后,给指定类发消息(赋初值),生成程序的对象集;

6 给对象发消息,对象与对象之间发消息,完成计算。

冯诺依曼型计算机的特点

1 计算机由去处器、控制器、存储器、输入设备和输出设备五部分组成;

2 存储器是字长固定的、顺序线性编址的一维结构;

3 采用存储程序的方式,程序和数据都以同等地位存放于存储器内,运行程序或访问数据都需要提供相应的地址;

4 指令和数据都用二进制代码表示;

5 指令由操作码和地址码组成,操作码用来表示操作的性质,地址码用来表示操作数所在存储器中的位置;

6 指令在存储器中按执行顺序存放,由指令计数器指明要执行的指令所在的单元地址。程序一般按照指令在存储器中存放的顺序执行,程序分支由转移指令实现;

7 以运算器为中心,输入、输出设备与存储器间的数据传输都通过运算器。

8 指令流在存储器与控制器间流动,数据流在存储器与运算器间流动,控制流则在各部分间流动;

运算器Arithmetic and Logic Unit,ALU

执行各种算术运算、关系运算、逻辑运算的部件,又称为算术逻辑单元,它包括若干个寄存器、执行部件和控制电路。操作时,控制器控制运算器从存储器中取出数据,进行算术或逻辑运算,并把处理后的结果送回到存储器,或者暂时存放在去处器中的寄存器里。

控制器Control Unit

其主要作用是使整个计算机能自动地执行程序,并控制计算机各部件协调一致地工作。执行程序时,控制器先从存储器中按照特定的顺序取出指令,解释该指令并取出相关的数据,然后向其它部件发出执行该指令的所需要的时序控制信号,再从存储器中取出下一条指令执行,依次循环,直到程序执行结束。

存储器Memory

负责存储程序和数据,并根据控制命令提供这些数据,存储器一般分为很多存储单元,并按照一定的方式进行排列,每个单元都编了号,称为存储地址。指无影灯在储存器中基本上是按执行顺序存储的,由指令计数器指明要执行的指令在存储器中的存储地址。

1 CPU可以直接读写内在的程序,却不能直接存取外存中的程序与数据。外存中的程序和数据必须先加载进内存,才能被CPU读取;

2 内存的容量取决于CPU的寻址空间(与CPU地址线的数目有关);

3 内存的读写速度是“电子级的”,而外存的基本上是“机械级的”;

4 内存是电子设备,多为大规模集成电路,而外存多半是机械设备,利用磁记录、光学原理等做成磁盘、磁带、硬盘、光盘等;

补码运算:减法变加法

1 机器数:机器数有固定的位数,具体是多少位与机器有关,通常是8位或16位或32位。机器数把真值的符号数字化,通常用最高位表示符号,0表示正,1表示负;

2 原码:符号位用0或1表示;

3 反码:正数的表示方法与原码相同,负数的反码是把其原码除符号位以外的各位取反;

4 补码:正数的表示方法与原码相同,负数的补是在其反码的最低有效位上加1.

二进制

只有两个完全不一样的状态,运算规则简单,数据存储简单;

数制间转换的代价:效率、有损映射、从连续到离散

摘自:《计算思维-计算学科导论》(唐培和 徐奕奕),电子工业出版社