计算机科学基础_4 - 算法,数据结构

算法入门

  • 选择排序,Selection sort
  • 大O表示法,Big O notation
  • 归并排序 - Merge sort
  • Dijkstra 算法

写指数函数,只是无数解决方案的一种,还有其它方案。
用不一样顺序写不一样语句,也能获得同样的结果,不一样的是“算法”,意思是:解决问题的具体步骤。
即便结果一致,有些算法会更好。
通常来讲,所需步骤越少越好。不过有时候也会关心其余因素,好比占多少内存。node

“算法”一词来自 阿尔●花拉子密,1000多年前的代数之父之一。
如何想出高效算法,是早在计算机出现以前就有的问题,诞生了专门研究计算的领域,而后发展成一门现代学科,计算机科学。程序员

选择排序

记载最多的算法之一是“排序”。好比给名字,数字排序。
排序处处都是,找最便宜的机票,按最新的时间排邮件,按姓氏排联系人等等,这些都要排序。
计算机科学家花了数十年发明了各类排序算法,还起了酷酷的名字,“冒泡排序”, “意面排序”,“选择排序”算法

好比:
一堆机票价格,都飞往目的地。把价格一组数据存放到一个数组结构。
先找到最小数,从最上面开始,而后和第一个比较,因此看最小的数是否变化,移动位置。重复循环比较,持续移动位置。
意味着,若是要排N个东西,要循环N次,每次循环中再循环N次,共N*N。编程

clipboard.png

算法的输入规模运行步数之间的关系,叫算法的复杂度
表示运行速度的量级。
计算机科学家们把算法复杂度叫:大O表示法(big O notation)。数组

选择排序的算法复杂度O(n^2)效率不高。数据结构

归并排序

第一件事是检查数组大小是否>1,若是是,就把数组分红两半。再检查数组大小是否>1,若是是,继续分组,若是不是开始“归并”。
从前两个数组开始,读第一个(也是惟一一个)值,而后开始比较,若是更小,排在以前。重复这个过程,按序排列。而后数组大小增长,再次归并。
一样,取前二个数组,比较第一个数,而后再比较第一个数组的第一个数,第二个数组的第二个数。而后合并成更大有序的数组。
直到排序完整。编程语言

归并排序的算法复杂度O(n * logn),n是须要比较+合并的次数和数组大小成正比,logn是合并步骤的次数。函数

图搜索

clipboard.png

“图”是用线链接起来的一堆“节点”。能够想成地图,每一个节点是一个城市,线是公路。
一个城市到另一个城市,花的时间不一样,能够用成本(cost)或权重(weight)来代称,表明要几个星期。
假设想找“高庭”到“凛冬城”的最快路线。
最简单的方法是尝试每一条路,计算总成本。这是“蛮力办法”。测试

假设用蛮力方法,来排序数组,尝试每一种组合,看是否排好序。
这样的时间复杂度是: O(n!),n是节点数,n!是阶乘。比O(n^2)还糟糕。人工智能

从“高庭”开始,此时成本为0,把0标记在节点里,其它城市标记成问号,由于不知道成本多少。Dijkstra 算法老是从成本最低的节点开始,目前只知道一个节点“高庭”,因此从这里开始,跑到全部相邻节点,记录成本,完成一轮算法。
可是还未到“凛冬城”,因此再跑一次Dijkstra 算法
下一个成本最低的节点,是“君临城”,记录相邻节点的成本,到“三叉戟河”,然而想记录的是,从“高庭”到这里的成本,因此“三叉戟河”的总成本8+5=13。如今走另一条路到“奔流城”,成本高达25,总成本33,但“奔流城”中最低成本是10,因此无视新数字,保留以前的成本10。如今看了“君临城”的每一条路还没到“凛冬城”因此继续。
下一个成本最低的节点,是“奔流城”,要10周。先看到“三叉戟河”成本,10+2=12,比以前的13好一点,因此更新“三叉戟河”为12,“奔流城”到“派克城”成本是3。10+3=13,以前是14,因此更新“派克城”为13。
“奔流城”出发的全部路径都走了遍,还没到终点,因此继续。
下个成本最低的节点,是“三叉戟河”。从“三叉戟河”出发,惟一没看过的路,通往“凛冬城”。成本是10加上“三叉戟河”的成本12,总成本是22。再看最后一条路,“派克城”到“凛冬城”,总成本是31。
因此最佳线路是:“高庭” -> “奔流城” -> “三叉戟河” -> “凛冬城”。

clipboard.png
clipboard.png
clipboard.png
clipboard.png

Dijkstra的语录:
“有效的程序员不该该浪费不少时间用于程序调试,他们应该一开始就不要把故障引入。”
“程序测试是代表存在故障的很是有效的方法,但对于证实没有故障,调试是很无能为力的。”

Dijkstra 算法算法复杂度是:O(n^2)通过改造的Dijkstra 算法复杂度减低为:O(n * logn + l) n是节点数,l是多少条线

数据结构

  • 数组,Array
  • 字符串,String
  • 矩阵,Matrix
  • 结构体,Struct
  • 指针,Pointer
  • 节点,Node
  • 链表,Linked List
  • 队列,Queue
  • 栈,Stack
  • 树,Tree
  • 二叉树,Binary Tree
  • 图,Graph

算法处理的数据,存在内存里的格式是什么。
但愿数据是结构化,方便读取,所以计算机科学家发明了“数据结构”。

数组

数组Array,也叫列表(List),或向量(Vector)。有一些区别,在不一样语言中基本相似。

数组的值一个个连续存在内存里。能够把多个值存在数据变量里,为了拿出数组中的某个值,要指定一个下标(index)
大多数编程语言中,数组下标都从0开始。
用方括号[]表明访问数组。

j = [5, 3, 7, 21, 82, 4, 19]
a = j[0] + j[5]

数组存在内存里的方式,十分易懂。
为了简单,假设编译器从内存地址1000开始存数组,数组有7个数字,像上图同样按顺序存。
j[0],会去内存地址1000,加0个偏移,获得地址1000,拿值: 5。
j[5],会去内存地址1000,加5个偏移,获得地址1005,拿值: 4。

clipboard.png

数组的用途普遍,因此几乎全部的编程语言,都自带了不少函数来处理数组。

字符串

数组的亲戚是字符串String,其实就是字母,数字,标点符号等,组成的数组。

计算机怎么存储字符?
经过字符对应的ASCII表

写代码时,用引号括起来就好了: j = "STAN ROCKS"

虽然长的不像数组,但的确是数组。

注意:字符串在内存里以0结尾,不是“字符0”,是二进制“0”,这叫字符“null”,表示字符串结尾。
这个字符很是重要,若是调用print()函数,会从开始位置,逐个显示到屏幕,可是得知道何时中止下来。不然会把内存里全部东西都显示出来。0告知函数什么时候停下。

clipboard.png

由于计算机常常处理字符串,因此有不少函数专门处理字符串。

矩阵

能够用数组作一维列表,但有时想操做二维数据,好比电子表格,或屏幕上的像素。那么须要 矩阵
Matrix

能够把矩阵当作数组的数组,内存中的表示:

j = [[10, 15, 12], [8, 7, 42], [18, 12, 7]]

clipboard.png

clipboard.png

为了拿一个值,须要两个下标,好比j[2][1],告知计算机在数组2里,位置是1的元素。

clipboard.png

结构体

把几个有关系的变量存在一块儿,会颇有用。多个变量打包在一块儿叫 结构体(Struct)
多个不一样类型数据,能够放在一块儿。

这些数据在内存里,会自动打包在一块儿,若是写j[0],能拿到j[0]里的结构体,而后拿具体数据。

clipboard.png

存结构体的数组,和其它数组同样,建立时就有固定大小,不能动态增长大小。还有,数组在内存中,按顺序存储。在中间插入一个值很困难。但结构体能够创造更复杂的数据结构,消除这些限制。

节点和链表

一个结构体,叫节点(Node)。它存一个变量,一个指针(pointer)
“指针”是一种特殊的变量,指向一个内存地址,所以得名。

用节点能够作链表(linked list),链表是一种灵活数据结构,能存不少个节点(node), 灵活性是经过每一个节点指向,下一个节点实现的。

假设有三个节点,在内存地址1000,1002,1008。
内存地址隔开,多是建立时间不一样,它们之间有其它数据,能够看到第一个节点,值是7,指向地址1008,表明下一个节点,位于内存地址1008,来到下一个节点,值是112,指向地址1002,往下一个节点,地址1002的值是14的节点,这个节点,指回地址1000,也就是第一个节点,这个叫循环链表。
但链表也能够是非循环的,最后一个指针是0null,表明链表的尽头。

clipboard.png

clipboard.png

clipboard.png

当程序员用链表时,不多看指针具体指向哪里,而是用链表的抽象模型。

clipboard.png

数组大小须要预先定好,链表大小能够动态增减,能够建立一个新节点,经过改变指针值,把新节点插入链表中。

clipboard.png

链表也很容易从新排序,两端缩减,分割,倒序等。

由于灵活,不少复杂的数据结构都用链表。

最出名的是 队列(queue)和栈(stack)。

队列和栈

“队列”就像邮局排队,谁先来就排在前面。先进先出(FIFO)。
有个指针,指向链表的第一个节点,第一个节点是Hank,服务完Hank以后,读取Hank的指针,把指针指向下一我的,这样就把Hank“出队”(dequeue)了。

clipboard.png

clipboard.png

若是想加到队列里,“入队”(enqueue)。
要遍历整个链表到结尾,而后把结尾的指针,指向新人(Nick)。

clipboard.png

只要稍做修改,就能用链表作栈,栈是后进先出(LIFO)。能够把“栈”想成一堆松饼,作好一个新松饼,就堆在以前的上面,吃的时候,是从最上面开始吃的。栈出入叫“入栈(push)”,“出栈(pop)”。

若是节点改一下,改为2个指针,就能作成树(Tree)
不少算法用了树,这种数据结构,一样程序员不多看指针的具体值,而是把树抽象成:

  • 最高的节点叫作“根节点(root)”
  • 根节点下的全部节点,都叫“子节点(children)”
  • 任何子节点的直属上层节点,叫“父节点(parent node)”
  • 没有任何“子节点”的节点,也就是“树”结束的地方,叫“叶节点(leaf)”。

clipboard.png

其中有一个特例,节点就2个,叫“二叉树(Binary Tree)”。

节点能够用链表存全部的子节点。
“树”的一个重要性质是(无论是现实仍是数据结构中):“根”到“叶”都是单向的。

若是数据随意链接,包括循环,能够用“图”表示。这种结构,能够用有多个指针的节点表示,所以没有根,叶,子节点,父节点这些概念。能够随意指向节点。

不一样数据结构使用于不一样场景,选择正确数据结构会让工做简单。

图灵

  • 可断定性问题
  • Lambda算子
  • 图灵机
  • 停机问题
  • 图灵测试

计算机科学之父,阿兰●图灵。
于1912年出生伦敦,从小就表现出惊人的数学和科学能力。
对计算机科学的建树始于1935年,他开始解决德国数学家提出的问题:可断定性问题,是否存在一种算法,输入正式的逻辑语句,输出准确的“是”或“否”答案?

美国数学家阿隆佐●丘奇于1935年首先提出解决方法,开发了一个叫“Lambda 算子”的数学表达系统,证实了这样算法不存在。
虽然“Lambda 算子”能表示任何计算,但它使用的数学技巧难以理解和使用。

图灵想出了本身办法来解决“可断定性问题”,提出了一种假想的计算机,如今叫“图灵机”。
图灵机提供了简单又强大的数学计算模型,虽然用的数学不同,但图灵机的计算能力和Lambda 算子同样。同时由于图灵机更简单,因此在新兴的计算机领域更受欢迎。

图灵机是一台理论计算设备,有无限长的纸带,纸带能够存储符号,能够读取和写入纸带上的符号,还有一个状态变量,保存当前状态,还有一组规则,描述机器作什么。规则是根据:当前状态+读写头看到的符号,决定机器作什么。结果多是在纸带写入一个符号,或改变状态,或把读写头移动一格,或执行这些动做的组合。

图片描述

简单例子:
让图灵机读一个以0结尾的字符串,并计算1个出现次数,是否是偶数。若是是,在纸带上写一个1,若是不是,在纸带上写一个0。

首先要定义“图灵机”的规则:

  • 若是当前状态是偶数,当前符号是1,那么把状态更新为“奇数”,把读写头向右移动。
  • 若是当前状态为偶数,当前符号是0,意味着到了字符串结尾。那么在纸带上写一个1,而且把状态改为停机(halt),状态改成“停机”,是由于图灵机已完成计算。
  • 还须要2条规则,来处理状态为奇数的状况:
    一条处理 奇数 + 纸带是0的状况
    一条处理 奇数 + 纸带是1的状况
  • 最后,要决定机器的初始状态,定成“偶数”。

图片描述

图片描述

图片描述

定义好了 起始状态+规则,就像写好了程序,如今能够输入了,假设把“1 1 0”放在纸带上,有两个1,是偶数。
规则只让读写头向右移动,其它部分可有可无,为了简单留空。

图灵机开始运行:

  1. 机器起始状态为“偶数”,看到的第一个数是1,符合第一条规则,因此执行对应的步骤,把状态更新到“奇数”,读写头向右移动一格。
  2. 而后又看到1,可是机器状态是“奇数”,因此执行第三条规则,使机器状态变回“偶数”,读写头向右移动一格。
  3. 如今看到0,而且机器状态是偶数,因此执行第二条规则。在纸带上写1,表示“真”的确有偶数个1。
  4. 而后机器停机。

图片描述

若是有足够时间和内存,能够执行任何计算,它是一台通用计算机。
只要有足够的规则,状态和纸带,能够创造任何东西。

因此,图灵机是很强大的计算模型。
事实上,就可计算和不可计算而言,没有计算机比图灵机更强大,和图灵机同样强大的,叫“图灵完备”。

每一个现代计算系统,好比笔记本电脑,智能手机,甚至微波炉和恒温机内部的小电脑,都是“图灵完备”。

停机问题

为了回答可断定性问题,把图灵机用于一个有趣计算的问题:停机问题。
简单说就是:给定图灵机描述和输入纸带,是否有算法能够肯定,机器会永远算下去仍是会到某一点会停机?

有没有办法在不执行的状况,弄清会不会停机呢?一些程序可能要运行好几年,因此在运行前知道,会不会出结果颇有用。不然就要一直等啊等,忧虑到底会不会出结果。图灵经过一个巧妙的逻辑矛盾,证实了停机问题是没法解决的。

想象有一个假想图灵机,输入: 问题的描述 + 纸带的数据,输出“yes”表明会停机,输出“no”表明不会停机。

clipboard.png

推理:若是有个程序,H(halt的第一个字母)没法判断是否会“停机”,意味着“停机问题”没法解决。
为了找到这样的程序,图灵用H设计了另外一个图灵机,若是H说程序会“停机”,那么新机器会永远运行(即不会停机),若是H的结果为No,表明不会停机,那么让新机器输出No,而后“停机”。

图灵机1(H): 输出yes->停机, 输出no->不会停机
图灵机2:H(yes) -> 不会停机,永远运行, H(no)-> 机器停机,输出no

实质上是一台和H输出相反的机器,若是程序不停机,就机器停机。若是程序停机,就机器永远运行下去。

还须要在机器前面加一个分离器,让机器只接收一个输入,这个输入既是程序,也是输入。把这台新机器叫“Bizzaro(DC漫画的一名反派角色,他的能力和超人相反)”

clipboard.png

若是把“Bizzaro”的描述,做为自己的输入会怎样,意味着在问H,当“Bizzaro”的输入是本身时,会怎样。
但若是H说“Bizzaro”会停机,那么“Bizzaro”会进入无限循环,所以不会停机。
若是H说“Bizzaro”不会停机,那么“Bizzaro”会输出No而后停机。
因此H不能正确断定,停机问题,由于没有答案,这是一个悖论,意味着“停机问题”不能用图灵机解决。

图灵证实了图灵机能够实现任何计算。
可是,“停机问题”证实了,不是全部问题都能用计算解决。

丘奇和图灵证实了计算机的能力有极限
不管多少时间或内存,有些问题,是计算机没法解决的,计算是有极限的,起步了可计算性理论,称之为:“丘奇-图灵论题”。

图灵测试

当时是1936年,图灵只有24岁,在1939年,二次世界大战,图灵的才能很快被投入战争,事实上,在战争开始前一年,已经在英国政府的密码破译研究。工做之一,是破解德国的通讯加密,特别是“英格玛机(Enigma)”加密信息。
简单说,英格玛机会加密明文,若是输入字母H-E-L-L-O,机器输出X-W-D-B-J,这个过程叫“加密”。
文字不是随便打乱的,加密由“英格玛机”顶部的齿轮组合决定。每一个齿轮有26个可能位置,机器前面还有插板,能够将两个字母互换。总共有上十亿种可能。
知道正确的齿轮和插头的设置,输入 X-W-D-B-J就会输出hello,解密了这条信息。

有数十亿的组合,根本无法手工尝试全部组合。英格玛机和操做员不是完美的,一个大缺陷是:字母加密后毫不会是本身。H加密后绝对不是H。
图灵在前人的基础上,设计了一个机电计算机,叫: Bombe。利用了这个缺陷,它对加密消息尝试多种组合,若是发现字母解密后和原先同样,就知道英格玛机不会这样子作,这个组合会被跳过,接着试另外一个组合,Bombe大幅度减小了搜索量,让破译人员把精力花在更有可能的组合,好比在解码文本中找到经常使用的德语单词。
德国人时不时会怀疑有人在破解,而后升级英格玛机,好比加一个齿轮,创造更多可能组合,甚至还作了全新的加密机,整个战争期间,图灵和同事都在努力破解加密,解密到德国情报,为盟军赢得了不少优点,有些史学家认为他们把战争缩短好几年,战后,图灵回到学术界,为许多早期计算机工做做出贡献。
好比曼切斯特1号,一个早期有影响力的存储程序计算机。但他最有名的战后贡献是“人工智能”。这个领域很新,直到1956年才有名字。

1950年,图灵设想了将来的计算机,拥有和 人类同样的智力,或至少难以区分。
图灵提出若是计算机能欺骗人类相信它是人类,才算是智能。这成了智能测试的基础,现在叫“图灵测试”

想象在和两我的沟通,不用嘴或面对面,而是来回发消息,能够问任何问题,而后会收到回答,但其中一个是计算机,若是分不出哪一个是人类,哪一个是计算机,那么计算机就经过了图灵测试。

这个测试的现代版叫:“公开全自动图灵测试,用于区分计算机和人类”,简称,验证码,防止机器人发垃圾信息等。

图灵于1954年服毒,年仅41岁。

因为图灵对计算机科学贡献巨大,许多东西以他命名,其中最著名的是“图灵奖”(计算机领域的最高奖项)。

  • 可计算性理论
  • 断定问题
  • 电子计算机
  • 人工智能
  • 数理生物学
  • 图灵试验
  • 破解德国的著名密码系统英格玛机(Enigma)
相关文章
相关标签/搜索