入门程序设计,一篇就够

以下内容来自中国大学慕课网的相关慕课(翁恺老师的程序设计入门、李戈老师的计算概论与程序设计基础、郭炜老师的程序设计与算法、陈斌老师的数据结构与算法Python版(除了这个其它的都是C或C++语言)、陈越老师的数据结构)以及C++ primer plus书籍,仅供个人学习参考使用,如有侵权请联系笔者删除。笔者水平有限,理解有错还望谅解指教。


目录

0. 前言

1. 计算机的基本原理

0)抽象的“计算”概念的提出

1)计算机的理论模型——图灵机

图灵机的构成

图灵机的工作步骤:

图灵机工作的例子

2)计算机为什么能计算?

数在计算机中如何表示?

逻辑上数是如何计算的?

物理上数的计算是如何实现的?

2. 程序设计语言

1)从汇编到C再到C++语言

2)计算机如何执行程序

3. 数据结构与算法

1)什么是数据结构(data structure)

2)如何描述数据结构

3)什么是算法

4)什么是好的算法

5) 为什么研究数据结构与算法


0. 前言

我们要让计算机做事情,就要找出算法,然后用编程语言写程序,然后让计算机去执行程序。

计算机——程序——算法

计算机科学不仅仅研究计算机硬件,还主要研究:问题(数据结构),问题解决的过程或步骤(算法)以及问题解决的方案(计算复杂度、算法分析)。为了更好地处理机器独立性相关性,需要引入“抽象”和“具体”的概念,用以从“逻辑”或“物理”的不同层次上看待问题及解决方案。


什么是抽象?以汽车打比方:从司机角度看到的是一台带人去往目的地的代步工具,司机驾驶时看到汽车的逻辑功能层次,即操作各个机构来实现各个功能(换挡、转弯、刹车、点火等)这些操纵机构(档位、方向盘、脚刹、油门等)就称为接口(Interface);从汽车修理工角度,还清楚汽车每项功能是如何实现的(发动机工作原理、档位操作的机械结构等),这些内部构造是汽车的物理层面,它们的工作过程称为实现(Implementation)。回到计算机:对于和我水平相当的计算机小白而言,看到的是计算机的逻辑层次,比如可以编辑文档、上网聊天、收发邮件等等,不用知道计算机内部如何处理;对于专业人士而言,看到的是计算机的物理层次,需要了解从硬件结构、操作系统原理到网络协议等各方面的低层次细节。当然,逻辑和物理是相对的。

下面简单讨论一下编程,后文还会详细展开。编程时也会涉及到“抽象”,实现一个功能时不用管这个功能是如何实现的,这种功能上的“黑盒子”称为“过程抽象”,比如调用库函数。但编程一般对应的是实现,指的是通过一种程序设计语言,将抽象的算法实现为计算机可执行的代码的过程。

程序设计语言 需要为 算法的实现 提供 实现“过程”和“数据”的机制,具体表现为“控制结构”和“数据类型”。其中,控制结构有顺序处理、分支选择、循环迭代。每种程序设计语言都有语句(像if,while,for)对应控制结构;也会提供基本数据类型来表示数据,但对于复杂问题而言,直接使用基本数据类型不利于算法的表达和问题的解决,比如C++的数据表示有多种类型——整数、小数、字符、字符串、用户定义的、由多种类型组成的复合结构。


问题可以分为三类:

  1. what:面向判断与分类的问题
  2. why:面向求因与证明的问题
  3. how:面向过程与构建的问题

用“有限能行方法”下的计算模型可以解决的问题都是可计算问题(后文有解释),问题的解决有三类:

  1. what:利用有限的规则和有限次的判断,通过树状的判断分支解决
  2. why:利用有限的推理规则和有限的公式序列,通过一步步推出公式解决
  3. how:通过算法流程(顺序、条件分支、循环)解决

基于有穷观点的能行方法的“可计算”概念仅仅涉及到问题的解决是否能在有限的资源(时间、空间)完成,并不关心花费多少计算步骤或多少存储空间(即规模)。问题的解决还需要考虑其可行性,有些问题的解决会爆炸性地吞噬资源,虽有解法但没什么可行性,如哈密顿回路、货郎担问题。

因此我们需要计算复杂性理论:研究问题的本质,定义一些衡量指标,对问题的难易程度(步骤数、存储空间大小)分类,研究各类问题的难度级别,并不关心解决问题的具体方案。然而对于同一个问题,会有不同的解决方案,解决效率也是千差万别。这时我们需要算法分析:研究问题在不同现实资源约束下的不同解决方案,致力于找到效率最高的方案。比如:不同硬件配置(手机、PC、超级计算机)、运行环境(网络环境、单机/多机)、应用领域(工业控制、医疗系统)、使用状况(省电/正常)下具体的算法哪个好。计算复杂性理论是研究对于问题的分类,算法分析是研究对不同运行条件下最高效率的实现。


有的问题需要我们突破计算的极限。

一种方法是超大规模分布式计算即机海战术,利用闲置计算力(公众利用全球联网计算机共同计算搜寻,如地外文明、蛋白质分子结构、气象);或者众包学术研究即人海战术,利用空闲智力,如通过多人在线游戏预测蛋白质结构。

另一种方法是研究新型计算技术:

  1. 光子计算:用纳米级的超微透镜取代晶体管,用光信号代替电信号进行运算。光芯片无需改变二进制计算机的软件原理,但可以轻易实现极高的运算频率,而且能耗低,不需要复杂的散热装置
  2. DNA计算:以DNA分子和酶的相互作用实现逻辑运算和数据存储(剪切、复制、粘贴映射为逻辑运算),计算并行度和存储能力高。
  3. 量子计算:利用量子力学态叠加原理,让每个信息单元处于多种可能的叠加状态(0,1,同时是0和1),从而实现指数级别的并行计算,根本上解决最高复杂度计算问题。

1. 计算机的基本原理

计算机做的所有事情都是“计算”。

0)抽象的“计算”概念的提出

20世纪20年代,为了解决数学本身的可检验性问题,大数学家希尔伯特提出“能否找到一种基于有穷观点的能行方法,来判定任何一个数学命题的真假“,这种基于有穷观点的能行方法:由有限数量的明确有限指令构成; 指令执行在有限步骤后终止; 指令每次执行都总能得到唯一结果; 原则上可以由人单独采用纸笔完成,而不依靠其它辅助; 每条指令可以机械地被精确执行,而不需智慧和灵感。

20世纪30年代,几位逻辑学家各自独立提出了几个关于“计算”的数学模型:哥德尔和克莱尼的递归函数模型 丘奇的Lambda演算模型 波斯特的Post机模型 图灵的图灵机模型(这几个计算模型后来被证明是等价的)。虽然希尔伯特的计划最终被哥德尔证明无法实现,但“能行可计算”概念成为计算理论的基础,其中的一些数学模型(如图灵机)也成为现代计算机的理论基础。

1)计算机的理论模型——图灵机

根据哥德尔不完备性定理,任何一个数学系统,只要它是从有限的公理和基本概念中推导出来,并且从中能推证出自然数系统,就可以在其中找到一个命题,对于它我们既没有办法证明,又没有办法推翻。那么,可以或不可以被证真、证伪的边界在哪里?也就是说,怎么判断一个未解的问题能不能有解?

在计算机中,我们称有解的问题为可计算问题,而无解的问题为不可计算问题

被证明的 不可计算问题 有 停机问题:判定任何一个程序在任何一个输入情况下是否能停机。还有不可计算数:除了圆周率pi和自然对数的底e,几乎所有的无理数都无法通过算法确定任意一位是什么数字。

如何判断可计算还是不可计算?研究思路有:为计算建立数学模型,称为计算模型;然后证明:凡是这个模型能计算的任务,就是可计算的问题。

图灵在《论可计算数在判定问题中的应用》提出了一种数学模型——图灵机。

图灵机的构成

  1. 一条存储带:双向无限延长,上有一个个小方格,每个小方格可存储一个数字或字母
  2. 一个控制器:含一个读写头(可以读、写、更改小方格),可以接受设定好的程序语句,可以存储当前自身的状态,可以变换自身的状态,可以沿着存储带一格格左移或右移

图灵机的工作步骤

1. 准备: (1)存储带上符号初始化; (2)控制器设置好自身当前状态; (3)读写头置于起始位置; (4)准备好工作程序;

2. 反复执行以下工作直到停机: (1)读写头读出存储带上当前方格中 的字母/数字; (2)根据 自身当前状态 和 所读到的 字符,找到相应的程序语句; (3)根据 相应程序语句,做三个动作: ① 在当前存储带方格上写入一个相 应的字母/数字; ② 变更自身状态至新状态; ③ 读写头向左或向右移一步

b表示空白。每一行表示一个程序语句,每个语句包含5个符号,前两个是条件,后三个是动作。

 

 图灵机停机表示计算完毕,意味着得出计算结果

图灵机工作的例子

 

B表示空白

 

2)计算机为什么能计算?

数在计算机中如何表示?

0和1足以表示和传播各种信息。 比如, 用8个连续的0或1(即1个字节)有256种组合,来表示一个字母、数字或标点符号,这就是ASCII编码方案。又比如,图片由一个个像素组成,用32个比特表示一个像素点的颜色;视频由一张张连续的图片构成。再比如,可执行程序(.exe文件)包含可执行的指令是0️⃣1️⃣串。
数的表示形式有进制之分。 数就是数,就是这么大,没有进制之分,只有数的表示形式,才有进制之分。 所谓“K进制数”,是“数的K进制表示形式" 的。
其中,十六进制数有16个数字:0~9,A~F
  • 下面讨论整数进制数的相互转换
1). K进制数到十进制数的转换
假设有一个n+1位的K进制数,它的形式: An A n-1 A n-2…… A 2 A 1 A0,
则其大小为: A0 × K^ 0 + A 1 × K^ 1 + ……+ A n-1 × K^( n-1) + A n × K^n
举例:
2). 十进制数到K进制数的转换 —— 短除法
3).  十六进制数到二进制数的互相转换
    4个二进制位正好对应于1个十六进制位
  •  下面讨论小数进制数的相互转换

 

 注意:小数运算有误差,因此慎用==判断等

逻辑上数是如何计算的?

二进制数的一位,取值只能是0或1,称为一个“比特”(bit),简写:b。八个二进制位称为一个“字节”(byte),简写:B。

1024(2^ 10 )字节称为1KB ,1024KB称作1MB(1兆),1024MB称作1GB, 1024GB称作1TB。

物理上数的计算是如何实现的?

计算机的电路由逻辑门电路组成。一个逻辑门电路可以看成一个开关,每个开关的状态是“开"(高电位)或“关”(低电位),即对应于1或0。

2. 程序设计语言

程序是一组计算机能识别和执行的指令。程序设计语言,又称编程语言、计算机语言。一般来说,计算机语言要处理2个概念——数据和算法。数据是程序处理和使用的信息,算法是程序使用的方法。

1)从汇编到C再到C++语言

C语言新增了诸如控制结构和函数等特性以便更好地控制程序流程,支持结构化和模块化程度更高的方法;C++又增加了对面向对象编程和泛型编程的支持,有助于提高模块化和创建可重用代码,节省编程时间并提高程序的可靠性。

计算机只懂二进制指令,称为操作码,这样的机器语言人根本读不懂。为了解决可读性问题,最早诞生了汇编语言(面向机器的程序设计语言),面向机器的意思是针对特定的cpu处理器,也就是说要将汇编程序移植到另一种计算机上必须使用不同的汇编语言重新编写程序。汇编语言是二进制指令的文本形式,也是直接操作硬件的低级语言,详见干货|汇编语言入门教程 (baidu.com)

下面,让我们了解高级语言发展的历史,可以领会不同的编程理念。

C语言是和UNIX操作系统(能够管理计算机资源、处理计算机与用户交互的程序)一起成长起来的,可以看 什么是操作系统?它和编程语言有什么关系?。UNIX是为在不同的计算机(平台)上工作而设计的,致力于解决问题而不针对特定的硬件。一种被称为编译器的特殊程序将高级语言翻译成特定计算机的内部语言。这样就可以通过对每个平台使用不同的编译器,来在不同的平台上使用同一个高级语言程序了。Dennis Ritchie 开发了C语言,将低级语言的效率硬件访问能力和高级语言的通用性可移植性融合在一起。

C语言(面向过程的程序设计语言)首先是过程性语言,强调的是编程的算法方面。过程化编程首先要确定计算机应采取的操作,然后使用编程语言实现这些操作,就像遵循菜谱的步骤做菜。后来随着程序规模的扩大,结构化编程把执行的操作自顶向下分解,显得更有序,即将大型程序分解为小型、便于管理的任务,再将任务分为更小的模块。结构化编程反映了过程化编程的思想。C语言的词汇表中就包含了决定接下来执行哪个指令的结构(for循环、while循环、do while循环、if else语句),且鼓励程序员用函数表示各个任务模块,满足结构化编程的需求。

虽然结构化编程提高了程序的清晰度、可靠性、易维护性,但在编写大型程序时仍面临挑战。这时,为了让重用代码和抽象概念更为简单,面向对象编程OOP诞生了。OOP强调数据,设计与问题的本质特性相对应的数据格式

面向对象编程OOP首先设计类。类是一种规范,描述了这种数据格式,而类的实例,称为对象,是根据这种规范构造的特定数据结构。类是用来代表事物的,对一种事物,可以设计一个类概括出它的共同特点,用成员变量(又称类的“属性“)表示;还要概括该种事物能进行的操作,用成员函数(又称类的“方法”)表示。也就是说,类规定了可使用哪些数据来表示对象以及可以对这些数据执行哪些操作。从类到程序的处理过程称为自下而上的编程。OOP编程不仅仅是将数据和方法合并为类定义,这有助于创建可重用的代码,还引入了信息隐藏、多态、继承等新的理念。

例如:要开发一个能够绘制矩形的计算机绘图程序,可以定义一个描述矩形的类。定义的数据部分包含顶点的位置、长和宽、边的颜色和样式、内部的填充颜色和图案等;定义的操作部分包含移动、改变大小、改变颜色和图案、旋转、复制等。当使用该程序来绘制矩形时,将根据类定义创建一个对象,该对象保存了描述矩形的所有数据值。

另一种让重用代码和抽象概念更为简单的编程模式是泛型编程(generic programming),强调的是独立于特定数据类型。泛型(generic)意即不是特定类型,创建独立于类型的代码的泛型编程需要对语言进行扩展,只编写一个泛型函数,并将其用于各种实际类型。 

OOP适于管理大型项目,泛型编程提供了执行常见任务(对数据排序、合并链表等)的工具。C++融合了OOP、泛型编程和C的过程性,在大型项目管理中很实用

2)计算机如何执行程序

有2种执行程序的方法:

  • 解释:解释器程序读懂你写的程序,让计算机按你的要求做事。你写的程序存在文件里,交给解释器一条条执行,下回执行还得把解释器拿出来。
  • 编译:编译器程序将你用高级语言写的程序翻译成机器语言写的程序,编译完再拿这个二进制的文件去运行。

有时会交叉编译,如在windows上编译但要放到单片机上运行。

程序设计语言本无解释、编译之分,任何一种编程语言既可以解释执行,又可以编译运行,只是一种语言常用什么执行方法,我们习惯上就称它为“什么型语言”。比如C语言是编译型语言,python是解释型语言。以前计算机没有现在这么快的时候,我们会认为编译型语言运行效率明显更高,但现在这不是主要矛盾了。解释型语言有特殊的运算能力,如运行过程中源代码可以被修改;编译型语言有确定的运算性能,这一点才是主要的区别。

以C++为例,程序运行的步骤大体如下:

  1. 使用文本编辑器(如Sublime Text)编写程序,并将其保存到文件(称为源代码)中。
  2. 编译源代码。意味着:运行一个程序将源代码翻译为主机所使用的机器语言,包含了翻译后的程序的文件称为程序的目标代码(object code)
  3. 将目标代码同其它代码链接起来。其它代码通常是库里的函数。链接指的是将 目标代码 同 使用的库函数的目标代码 以及 标准的启动代码(startup code)组合起来,让程序可执行。

3. 数据结构与算法

1)什么是数据结构(data structure)

官方定义有很多:

数据结构是抽象数据类型ADT(Abstract Data Type)的物理实现。

数据结构是数据对象,以及存在于该对象的实例和组成实例的数据元素之间的各种联系。这些联系可以通过定义相关的函数来给出。

数据结构是计算机中存储、组织数据的方式。通常,精心选择的数据结构可以带来最优效率的算法。

数据规模不同,数据的处理难度不同。以摆放图书为例,需要考虑两个问题:如何插入新书?怎么找到某本指定的书?递归函数对空间的占用是爆炸性的,解决问题方法的效率与空间的效率有关。对于多项式求和,秦九韶算法(提出x来)快于一项项累加,解决问题方法的效率与算法的巧妙程度有关。

数据结构是数据对象在计算机中的组织方式,包含:

  • 逻辑结构:线性结构、树状结构、图(一本书对应很多人,一个人对应很多书)
  • 物理存储结构:在内存怎么放(数组、链表)

数据对象必与一系列加在其上的操作相关联,完成这些操作所用的方法就是算法

2)如何描述数据结构

抽象 数据类型 ADT

抽象(前文也提到了)指:描述数据类型的方法不依赖于具体实现。只描述数据对象集和相关操作集合是什么,并不涉及如何做到的问题,即:

  • 与存放数据的机器无关
  • 与数据存储的物理结构无关:不关心怎么存——二维数组?一维数组?十字链表?
  • 与实现操作的算法和编程语言无关:如矩阵元素的变量类型是ElementType没说具体是float还是int;如描述矩阵加法时,没说先按行加还是先按列加,也没说用什么语言实现这个函数

数据类型包含:

  • 数据对象集
  • 数据集合相关联的操作集

在C语言中,这两者是独立处理的;在面向对象的语言如C++中,专门为数据类型设计了“类”,把数据集和与它相关的操作集封装在一个类里。


3)什么是算法

算法(Algorithm)是

  •  一个有限指令集
  • 接受一些输入(有些情况下不需要输入) 
  • 产生至少一个输出
  •  一定在有限步骤之后终止 
  • 每一条指令必须有充分明确的目标,不可以有歧义;必须在计算机能处理的范围之内;描述应不依赖于任何一种计算机语言以及具体的实现手段。

4)什么是好的算法

 我们更关心最坏情况复杂度,因为平均不总是好算。对于算法,只粗略地知道增长趋势即可。

 Θ ( h ( n))符号读作/thi:ta/,表示输入规模n增长,函数值基本不变。为了让分析算法效率时与真实情况更贴近,一般O(f(n))函数取上确界,Ω (g ( n))函数取下确界。

 

把两段算法拼起来:求和 T1 ( n) + T2 ( n) ,把两段算法嵌套:求积T1 ( n) *T2 ( n)

5) 为什么研究数据结构与算法

算法+数据结构=程序                                                                 ——图灵奖获得Niklaus Wirth

为了控制问题和问题解决过程的复杂度,利用抽象来保持问题的“整体感”。对现实问题进行建模的时候,对算法所要处理的数据,也要保持与问题本身的一致性,不要有太多与问题无关的细节,引入抽象数据类型ADT。然后对不同的对象,我们去实现不同的数据结构。

研究算法能够:

  1. 有助于我们在面对未知问题的时候,能够根据类似问题的解决方案来更好解决问题
  2. 通过算法分析技术来评判算法本身特性,而不仅仅根据实现算法的程序在特定机器和特定数据上运行的表现来评判它(即使同一个程序,在不同的运行环境和输入数据的情况下,其表现的差异可能也会很大)
  3. 当我们碰到棘手的难题 得能区分这种问题是根本不存在算法 还是能找到算法,但需要耗费大量的资源
  4. 最恰当的折衷:在不同算法之间进行选择折衷的处理
  • 2
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值