计算机科学速成课(10-14)

早期编程方式

视频链接:【计算机科学速成课 第10集】

程序需要加载进内存,程序如何“进入”计算机?

最早在织布行业出现的,在织图案、花纹时,每隔一会工人都需要调整织布机,消耗劳动力,因此1801年,Joseph·Marie·Jacquard发明了可编程纺织机,每一行的图案由可穿孔纸卡决定,特定位置有无穿孔,决定了线的高低。
过了一个世纪后,1890年美国穿孔卡纸用来做人口普查,一张卡存一个人的信息,例如种族、婚姻状况、出生国家等,针对每个问题,统计员会在相应的位置打孔,当卡片插入汇总机,孔会让对应的信息总和值+1。早期汇总机不算计算机,因为它们只做一件事——汇总数据,操作是固定的,不能编程。穿孔卡纸存的是数据不是程序。
之后又加入了加、减、乘、除等其他指令,为了正确地执行不同的计算,程序员需要某种控制面板“control panel”。面板有很多小插孔,程序员可以插电线让机器不同的部分互相传数据和信号,因此也叫做“插线板plug boards”,意味着运行不同的程序要重新接线。
到了1920年代控制面板变成了可拔插,让编程更方便,可以给机器插入不同程序。世界上第一台通用电子计算机——ENIAC,完成于1946年,用了一大堆插线板。程序在纸上设计好之后,给ENIAC连线最多可能花三个星期。因为早期计算机非常昂贵,停机几个星期只为换程序,完全无法接受。
幸运的是,到1940年到晚期-1950年代初期,内存变得可行。成本下降、容量上升,与其把程序存在插线板,可以存在内存里。这样程序易于修改、方便CPU快速读取。这种机器称为“存储程序计算机”。如果内存足够,不仅可以存要运行的程序,还可以存程序需要的数据,把程序和数据存在一个地方,我们称之为“冯诺依曼结构Von Neumann Architecture”,命名来自John·Von·Neumann,杰出的数学家和物理学家,参与了曼哈顿计划和早期电子计算机项目。

冯诺依曼计算机的标志是: 处理器(有算术逻辑单元)+ 数据寄存器 + 指令寄存器 + 指令地址寄存器 + 内存(负责存数据和指令)

第一台冯诺依曼结构的“存储程序计算机”由曼彻斯特大学于1948年建造完成,绰号“Baby”。
虽然有内存很棒,但是仍然需要把程序和数据以某种方式输入计算机,所以用到了穿孔卡纸。到了1980年代,几乎所有的计算机都有穿孔卡纸读取器,可以吸入一张卡片,把卡片内容写入内存,即使是简单的程序也有几百条指令,要一堆纸卡来存,为了标记纸卡的顺序以防散落乱序,在卡片侧面画对角线。用卡纸的最大型程序是美国空军的SAGE防空系统,于1955年完成,雇佣了世上20%的程序员。
除了前两种编程方式,1980年代前,还有一种常见编程方式——“面板编程Panel Programming”,用了一堆开关和按钮。

编程语言发展史

视频链接:计算机速成课 第11集

计算机能处理二进制,二进制也是处理器的“母语”,事实上它们“只能”理解二进制,这叫“机器码Machine Language”或者“Machine Code”。在计算机早期阶段,必须用机器码写程序,具体来说会先在纸上用英语写一个“高层次版”,例如“从内存取下一个销售额,然后加到天、周、年的总和,然后算税”等等,这种程序的高层次描述叫“伪代码Pseudo-Code”。在纸上写好以后,用操作码把伪代码转成二进制机器码,翻译完成后,程序就可以在计算机上运行了。
在1940~1950年代,程序员开发了一种新语言,可读性高更强,层次更高,给每个操作吗分配一个简单的名字,叫“助记符mnemonics”,它的后面紧跟数据,形成完整的指令,与其用“0”“1”写代码,程序员可以写“LOAD_A 14”,更容易理解。
当然计算机并不知道什么叫“LOAD_A 14”,只能理解二进制,这时候就需要写二进制程序来将文字转成二进制指令,这种程序叫做“汇编器Assembler”。汇编器读取用“汇编语言(assembly language)”写的程序,然后转成Machine Language。一般来说,一条汇编指令对应一条机器指令,所以汇编码和底层硬件的连接很紧密。汇编器仍然强迫程序员思考用什么寄存器和内存地址。
美国海军军官Dr.Grace Hopper作为哈佛1号计算机的首批程序员之一,设计了一个高级编程语言,叫“算术语言版本0(Arithmetic Language Version )”或者简称“A-0”,一行高级编程语言可以转成几十条二进制指令,为了做到这种复杂转换,Hopper在1952年创造了第一个编译器,专门把高级语言转成低级语言比如汇编或机器码(CPU可以直接执行机器码)。

现在假设相加两个数字并保存结果,如果用汇编语言,得从内存取值,和寄存器打交道,以及其他底层细节。但同样的程序可以用Python这样写:
A=3
B=9
C=A+B
不用管寄存器或内存位置,编译器会搞定这些细节。程序员只需要创建代表内存地址的抽象——“变量variables”。现在把两个数存在变量里,这里取名A和B。在底层操作时,编译器可能把变量A存在寄存器A里,但是不需要程序员去考虑。

但是A-0和之后的版本并没有被广泛使用,“FORTRAN(formula translation公式翻译)”这门语言数年后由IBM在1957年发布,主宰了早期的计算机编程。平均来说,FORTRAN写的程序要比等同手写的汇编代码短20倍,然后FORTRAN编译器会把代码转成机器码。但是当时由于FORTRAN只能跑在IBM计算机上,如果升级电脑,则所有的程序都需要重写。因此在1959年组建了一个联盟——“数据系统语言委员会the Committee on Data System Languages”,开发了一种通用编程语言,可以在不同机器上使用——“普通面向商业语言Common Business-Oriented Language”,简称“COBOL”。为了兼容不同的硬件,这个计算架构都需要一个COBOL编译器,最重要的是这些编译器可以接受相同的COBOL代码。
之后陆陆续续出现了更多的编程语言,1960年代,有ALGOL,LISP和BASIC等语言;1970年代有Pascal,C,Smalltalk等;80年代有C++,Objective-C和Perl等;90年代有Python,Ruby和Java;新千年Swift,C#,Go在崛起。

编程基础(语句和函数)

视频链接:计算机速成课 第12集

规定句子结构的一系列规则——语法syntax

“初始化变量”——设置最开始的值

“控制流语句”:if语句、while语句、for循环

if语句
if expression then
code here
···
endif

while语句
while expression
code to be looped here
···
loop

for循环
for variable = start_value to end_value
code to be looped here
···
next

为了代码的隐藏复杂度,可以把代码打包成“函数function”,也叫做“methods”或“subroutines”,做后还需要把结果交给使用这个函数的代码,所以用“return”语句。现代编程语言有很多预先写好的函数集合,称作“库Libraries”在这里插入图片描述

算法入门

视频链接:计算机速成课 第13集

不同的“算法algorithm”意思是解决问题的具体步骤,即使结果一样,有些算法会更好。一般来说,步骤越少越好。算法一词来源于波斯博识者al-Khwarizmi,1000多年前的代数之父之一。
记载最多的算法之一是“排序sorting”,比如名字排序、数字排序。“选择排序”中每次循环中再循环N次,共 N ∗ N N*N NN,或 N 2 N^2 N2次。随着数组的增大,对效率的影响越来越大。
算法的输入大小和运行步骤之间的关系叫做算法的“复杂度complexity”,表示运行速度的量级。计算机科学家们把算法复杂度叫“大O表示法”,即 O ( n 2 ) O(n^2) O(n2)“归并排序”的算法复杂度是 O ( n ∗ l o g n ) O(n*log n) O(nlogn) 其中n是需要比较+合并的次数 ,和数组大小成正比, l o g n logn logn是合并步骤的次数。重复切成两半和数量呈对数关系,即使数量扩大一千倍,分割次数也不会增大多少,因此归并排序比选择排序效率更高.
一个经典算法问题:图搜索
在这里插入图片描述现假设想找“高庭”到“凛冬城”的最快路线,最简单的方法就是尝试每一条路,计算总成本,这是蛮力方法。假设用蛮力方法来排序数据尝试每一种组合,这样的时间复杂度是 O ( n ! ) O(n!) O(n!)其中n是节点数,显然比 O ( n 2 ) O(n^2) O(n2)还糟糕。图搜索问题的经典算法发明者是理论计算机科学的伟人Edsger Dijkstra,所以叫做“Dijkstra算法”。Dijkstra算法总是从成本最低的节点开始。

①首先目前只知道一个节点“高庭”,所以从这开始,跑到所有相邻节点,记录成本就完成了一轮算法。在这里插入图片描述
②由于还没到凛冬城,所以需要再跑一次算法。从成本最低的君临城出发,记录所有相邻节点的成本,从君临城到三叉戟河的成本是5,但是我们需要记录的是从高庭到这里的成本,所以三叉戟河的总成本就是8+5=13;再来走另一条奔流城,成本高达8+25=33,但之前记录的奔流城中最低成本是10,所以可以无视新的成本,保留原来的成本10
在这里插入图片描述
③还没到凛冬城,继续跑算法。下一个成本最低的节点是10,即奔流城,所以从奔流城出发,记录所有相邻节点的成本。先看到三叉戟河成本:10+2=12,比之前记录的成本13低,则修改三叉戟河的成本为12;再看到派克城成本10+3=13,也比之前的成本低,更新派克城成本为13。奔流城的所有路径也都走遍了。
在这里插入图片描述
④再跑一次算法,从成本最低的三叉戟河出发,它只有一条路径成本是:12+10=22.再看另一条路,派克城到凛冬城的成本是:13+18=33。现在就得到了最低成本路线。
在这里插入图片描述

Dijkstra算法的原始版本构思于1956年,复杂程度是 O ( n 2 ) O(n^2) O(n2),前面说过这样效率不够好,所以在几年后得到了改进,复杂程度变成了 O ( n ∗ l o g n + l ) O(n*logn+l) O(nlogn+l)其中n是节点数,l是多少条线。和之前的对比

复杂程度是 O ( n 2 ) O(n^2) O(n2) 时: O ( 6 2 ) = 36 O(6^2)=36 O(62)=36
复杂程度是 O ( n l o g n + l ) O(nlogn+l) O(nlogn+l) 时 : O ( 6 ∗ l o g 6 + 9 ) = 13.7 O(6*log6+9)=13.7 O(6log6+9)=13.7

数据结构

视频链接:计算机科学速成课 第14集

数组(Array),也叫做列表(List)或向量(Vector),数组的值一个一个连续存在内存里。为了拿出数组中的某个值,我们需要指定下标(index),在大多数编程语言里,数组下标都是从0开始的,用“[]”方括号代表访问数据。
数组的亲戚是字符串(String),其实是字母、数字、符号等组成的数组。值得注意的是字符串在内存里以0结尾,不是字符0,而是二进制值0,是字符“null”,表示字符串结尾,像这样:
在这里插入图片描述这个字符很重要,如果调用print函数,会从开始位置,逐个显示到屏幕,但得知道什么时候停下,否则会把内存里所有东西都显示出来。
矩阵(Matrix)”可以看作数组的数组,例如一个3×3的矩阵就是一个长度为3的数组,数组里的每个元素都是一个长度为3的数组。
除了只是把单个数字或字符存进数组或矩阵,但有时可以把几个有关系的变量存在一起,数据类型也可以不一致,叫做“结构体(Struct)”。结构体也可以放到数组中,存结构体的数组和其他数组一样,创建时就有固定大小,不能动态增加大小。除此之外,数组在内存中是按顺序存储的,所以在中间插值就很困难,但是结构体可以创造更复杂的数据结构消除这些限制。
指针(pointer)是一种特殊的变量,指向一个内存地址。现在来看一个名为节点(node)的结构体,它存一个变量和一个指针,用这个节点可以做“链表(linked list)”。链表是指一种很灵活的数据结构,能存很多个节点,并通过每个节点可以指向下一个节点。

假设有三个节点,在内存地址1000,1002,1008处,隔开的原因可能是创建时间不同,它们之间有其他数据可以看到第一个节点,值是7,指向地址是1008,代表下一个节点的内存地址是1008,以此类推,最后一个节点又回到第一个节点,这叫“循环列表
在这里插入图片描述

但链表也可以是非循环的,最后一个节点的内存地址指向0,即null,代表链表的尽头。数组大小需要预先定好,链表可以动态增减,例如在增加节点时,只需要改变指针的值,就可以把新节点插入链表。链表也很容易重新排序、两端缩减、分割、倒序等。很多复杂的数据结构也会用到链表,如队列(queue)和栈(stack)
队列——先进先出(“FIFO” First-In-First-Out),入队和出队;
栈——后进先出(“LIFO” Last-In-First-Out),入栈push和出栈pop。
如果把节点变成含有2个指针,就变成了“树tree”,很多算法也用到了树。同样程序员很少看指针的具体值,而是把树抽象成:最高的节点叫“根节点root”;根节点下的所有节点都叫“子节点(children)”;任何子节点的直属上层节点,叫做“母节点parent node”。当节点最多有2个子节点时,称之为“二叉树binary tree”。树的一个重要性质就是“根”到“叶”是单向的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值