AICon全球人工智能与机器学习技术大会8折特惠,购票立减¥960! 了解详情
写点什么

扫盲:关于计算机,我们还应该知道些什么?

2018 年 12 月 28 日

扫盲:关于计算机,我们还应该知道些什么?

2017 年,有关人工智能的新闻再次震惊世界。


《自然》杂志上发表的一篇论文显示,英国 DeepMind 公司研发出了“AlphaGo Zero”,它在仅输入围棋规则、未输入任何人类棋谱的情况下,通过自学,就具备了完胜“阿尔法狗”的超强棋力。


经过短短 3 天的自我训练,AlphaGo Zero 就强势打败了此前战胜李世石的旧版 AlphaGo,战绩是 100:0。而经过 40 天的自我训练后,AlphaGo Zero 又打败了 AlphaGo Master 版本。要知道,“Master”曾击败过世界顶尖的围棋选手,甚至包括世界排名第一的柯洁。


人类几千年的围棋成就被计算机通过 40 天自我训练超越。计算机的学习能力如此强大,让人不禁想要了解计算机如何一步一步进化到今天的样子。


第一台计算机:埃尼亚克

世界上第一台电子计算机“埃尼亚克”(ENIAC,即 Electronic Numerical Integratorand Calculator“,电子数字积分计算机”的缩写)诞生的时间是 1946 年 2 月 10 日。


早在第一次世界大战期间,诺伯特·维纳(Norbert Wiener)在马里兰州的阿伯丁军械界试验场工作,参加火炮火力表测算的时候,就痛感高速自动计算手段的必要性。


1940 年,美军军械部又找上门来,请维纳去阿伯丁重新制订火力表,说希特勒的战斗机速度很快,飞行员们又大耍曲线飞行、翻筋斗的特技,盟军的高射炮根本打不着他们,需要制订新的火力表。维纳认为用手工和器械算肯定不行,只有研制高速计算机。他特意给时任罗斯福总统科学顾问的万·布什写了一封信,提出设计新型电子计算机的几条原则:


不是模拟式,而是数字式;


由电子元件构成,尽量减少机械部件;


采用二进制,而不是十进制;


内部存放计算表;


在计算机内部存贮数据。


数字式的加法和乘法装置、电子管元件、二进制、自动程序运算和内部存储数据,对现代计算机的设计有着深远的影响。万·布什点头称是,于是人类历史上大名鼎鼎的高速计算机——“埃尼亚克”诞生了。



图 1:埃尼亚克 (图片来源:http://detail.zol.com.cn/picture_index_1740/index17395727.shtml


现代计算机的诞生

艾伦·麦席森·图灵(Alan Mathison Turing)的可计算性把计算问题都变成了数学计算问题。有了维纳的控制论,计算机就可以根据指令自动执行计算任务。克劳德·艾尔伍德·香农(Claude Elwood Shannon)的思想体现在模块化和等价性。他提出所有的数学问题都可以等价为逻辑运算(简单的二进制运算),而逻辑运算可以使用简单的逻辑电路单元方便地实现。


有了以上前提,就可以绕开复杂的十进制计算机的思路(巴贝奇差分机于 1822 年研制成功,是世界上第一台“会制表的机器”。运算的精确度达 6 位小数,但由于当时的技术条件限制,巴贝奇终其一生也没有完成精度更高的十进制计算机。)转而使用大量简单的逻辑电路的组合,实现与十进制计算机等价的计算。


约翰·冯·诺依曼 (John von Neumann)从织布机的“卡片”得到启示,将控制计算机的指令与数据同时放在存储器中。参加埃尼亚克研制小组后的 1945 年,在共同讨论的基础上,冯·诺依曼发表了一个全新的"存储程序通用电子计算机方案"–EDVAC(Electronic Discrete Variable Automatic Computer 的缩写)。冯·诺依曼计算机由控制器、运算器、存储器、输入设备、输出设备五部分构成,这一结构奠定了现代计算机的体系结构理念:


  • 计算机处理的数据和指令一律用二进制数表示;

  • 顺序执行程序,即在计算机运行过程中,程序和被处理的数据首先存入主存储器(内存),计算机执行程序时,将自动地并按顺序从主存储器中取出指令一条一条地执行,这一概念称作顺序执行程序。

  • 计算机硬件由运算器、控制器、存储器、输入设备和输出设备五大部分组成。



图 2:冯·诺依曼体系结构


冯·诺依曼体系结构计算机是为逻辑和数值运算而诞生的,在数值处理方面已经达到很高的速度和精度,但对非数值数据的处理效率比较低。随着计算机体系结构研究方面进展,越来越多的非冯计算机相继出现,如光子计算机、量子计算机、神经计算机以及 DNA 计算机等等。


值得一提的是神经网络计算神经网络计算机,它能够模仿人类大脑的判断能力和适应能力,具有并行处理多种数据的能力,可以判断对象的性质与状态,并能采取相应的行动。


计算机思维

我们所说的计算机思维是指,了解计算机解决问题的方法,把现实世界的实际问题抽象为计算机可以解决的等价问题(等价性)。把复杂问题分解为许多模块化的小问题,解决了这些小问题,就得到了问题的解(模块化)。在云计算时代,掌握计算机思维、高效地利用计算机解决问题具有更大的优势。


下面我们就用一个例子,来说明计算机将如何解决现实世界中的问题。比如,你要安排一次旅行(旅行商问题:Travelling Salesman Problem),途经 n 个景点,有多少种行程安排?在计算机世界里这类问题的时间复杂是最高的,随着 n 取值的增加,计算复杂度随着指数方式增长,时间复杂度为 O(n!)


我列举一下随着景点数增加计算复杂度的变化,你可以感受一下这个恐怖的速度:


n=3   可能路线数为:6n=6   可能路线数为:720n=8   可能路线数为:40320n=15  可能路线数为:1,207,674,368,000n=30  可能路线数为:265,252,859,812,191,058,636,308,480,000,000
复制代码


当 n=30 结果约为 2.65 后面 32 个 0,如果按照每秒计算 5000 个结果(埃尼亚克加法速度)需要计算 1.68 百万亿亿年,已经远远大于宇宙年龄的 138 亿年。


为了简单起见,n 取值为 4。我们整理一下思路,首先我们在 4 个景点中选择一个,然后在剩余的 3 个景点中选择一个,直到最后一个景点。所有的可能性为 4x3x2x1=4!


这是人类的思维方式,而计算机善于使用递归方法解决问题,如果想得到 4!只要知道 4x3! 就可以了,如果想知道 3!只要知道 3x2!,类似地,需要知道 2!的话,知道 2x1!就可以了。我们知道 1!=1,这样我们就把问题自顶向下逐步分解为简单问题,最后当遇到 1!就结束递归给出结果。


递归算法实现

递归是一种优雅的问题解决方法,比如一个董事长如何管理好一个公司,使用递归方法的思路是董事长只要管理好下属的 CEO、CFO 等人;CEO 如何管理好一个公司呢?只要管理好公司的副总就可以了;以此类推,最后每个员工管理好自己手里的工作,就解决了如何治理好一个公司的问题。这样是不是很优雅的解决方案呢?


递归算法有两个关键条件,第一是基线条件(base case)告诉程序何时停止递归。第二是递归条件(recursive case)告诉程序何时调用自己。


如前所说,计算机将指令和数据存入内存,然后按照顺序执行。计算阶乘的递归函数 Python 代码如:


def factorial(x):if x == 1:#基线条件   return 1else:  #递归条件return x * factorial(x-1)
复制代码


如果计算机遇到一条递归令,如何执行呢?这里需要引入一个计算机编程概念:调用栈(call stack)。因为计算机顺序执行的特点,当遇到调用另外一条递归指令的时候,(x*factorial(x-1))就把这条指令放入调用栈,然后处理新的 factorial(x-1)递归指令。(注意调用栈的特点是先进后出。


下面分析一下遇到 factorial(4)时调用栈的变化。



图 3:处理递归指令时调用栈的变化情况


同样的方法处理 factorial(3)返回递归调用 factorial(2),将 factorial(2)放入调用栈;处理 factorial(2)返回 factorial(1),将 factorial(1)放入调用栈;当处理 factorial(1)时返回为 1,这时的调用栈的出栈处理过程如下最后返回结果为 24:



图 4:调用栈出栈过程


设计模式:经典问题的解决方案

你知道什么是 GOF(“四人帮”,全拼 Gang of Four) book 吗?


1994 年,由 Erich Gamma(苏黎世大学计算机博士,Eclipse 项目主要技术负责人)、Richard Helm(墨尔本大学计算机博士,IBM Watson 研究员)、Ralph Johnson(伊利诺伊大学教授,模式和重构领域大神) 和 John Vlissides(斯坦福大学计算机博士,IBM Watson 研究员) 四人合著出版了一本名为《 Design Patterns - Elements of Reusable Object-Oriented Software 》的书(中文译名:设计模式——可复用面向对象软件的基础),这本书凝聚了软件开发界几十年经验的结晶,已经成为面向对象技术的“圣经”,对后面出现的极限编程(敏捷开发的一种方法)有着深远的影响。


书中介绍了 23 种设计模式,这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。设计模式并不高深,我们使用的 Java 语言中的 MVC 就是一种设计模式。通过设计模式,可以实现代码编写工作的工程化。设计模式代表了最佳实践,可以使代码更加容易增加新功能而不必改动原有代码。


设计模式的本质是在已有方案之上的渐进式重构(《重构:改善既有代码的设计》作者: Martin Fowler 原作名:Refactoring: Improving the Design of Existing Code),从而控制业务逻辑复杂度和代码实现复杂度。但是随着业务发展,代码的业务逻辑复杂度增加,如何控制代码的实现复杂度和相应的易理解性、可维护性和可扩展性也成了需要考虑的问题。在设计模式领域最常遇到以下两个误区:


在一开始写代码之前使用设计模式,这违背了“在已有的方案上发现更好的方案”的原则,如果没有动手写代码,何谈更好的方案呢?


使用太多设计模式。这会导致方案逻辑分层更加复杂,引入多余的接口和分层。如果业务复杂度不高,使用一个简单直接的方案就足够了。设计模式不是万能的,只有出现代码的坏味道才有必要使用设计模式。


为什么选择策略模式?


在 20 多种设计模式中选择一个适合的模式,的确是很困难的事情。这需要平时多练习和思考,考虑设计模式是如何解决设计问题的,研究相似的设计模式和模式之间是如何关联的。策略模式是设计模式中行为型模式类型的一种。


同时,策略模式也符合开闭原则(Open Close Principle),即在程序需要进行拓展的时候,不必修改原有的代码,实现软件特性的热插拔效果。策略模式对于解决 if 条件分支泛滥的问题有很好的效果。


先看一下已有的业务模型和解决方案:现在已知景点之间的路径所经历的距离、时间、和成本,而且景点个数有可能很大的情况下,我们如何选择旅行商算法呢?


穷举法,优点是实现简单,找到所有的可能路径后计算最适合路径,当然缺点是当 n 数量增加时复杂度呈指数级增长;


动态规划算法,会降低当 n 增加时的计算量,但是也不适合 n 过大的情况;


贪心算法,可以处理 n 很大的情况,但是有可能得出的是局部的最优解而不是全局最优解。


算法各有优劣,不能在设计软件时确定使用哪种算法。如何实现用户在程序运行的时候选择算法和评价策略呢?最简单的方法是使用 if 分支语句把所有可能的条件罗列出来,但是算法和评价策略的选择是相互耦合的,造成 if 分支语句泛滥。伪代码如下:


def tsp(evaluate, method):  if (method == SMALL):     if (evaluate == DISTANCE):         #穷举法实现距离最短代码     elif(evaluate == TIME):         #穷举法实现时间最短代码     elif(evaluate == MONEY):         #穷举法实现金钱最省代码  elif (method == LARGE):      if (evaluate == DISTANCE):         #动态规划算法实现距离最短代码      elif (evaluate == TIME):         #动态规划算法实现时间最短代码      elif (evaluate == MONEY):         #动态规划算法实现金钱最省代码
复制代码


随着算法实现的增加和评价策略的增多,代码行数呈几何式增长。存在的一个关键问题是:很难在不改变已有代码的基础上增加新功能。因为这样会导致代码复杂性增加,难以维护。


下面是使用策略模式后,实现旅行商问题的类图。如果增加一种算法,只需要增加一个新的 Strategy 接口的实现类就可以了,不需要修改已有代码。而对评价原则的增加只需要实现不同的 ContextInterface()接口就可以了。这样的程序风格是不是比那个又臭又长、难于增加新功能的 if 语句优雅了很多呢?



总结

我们知道,计算机的设计思想发展经历了图灵的可计算性、维纳的控制论、香农的等价性和模块化,最后由冯·诺依曼提出了现在计算机的体系架构。很有趣的一个事实是,埃尼阿克的设计原则是由电子元件构成,尽量减少机械部件;而多年以后的今天,软件的设计模式(设计模式是经典问题的解决方案,可以使软件研发实现工程化)同时又参考了机械部件的特性——热插拔设计思想(开闭原则)。数十载光阴过去,计算机的发展就像是经历了一个轮回,只是一切已不再是原点。作为人类智慧成果的最佳代表,人工智能已经占据了时代的主流,人类社会生活的各大领域将继续因它而发生广泛而深刻的变化。前路漫漫,未来可期。


作者简介

万金,《DevOps 实施手册:在多级 IT 企业中使用 DevOps》译者


15 年知名外企与中国企业的 IT 从业经验,包括 IBM,华为,ThoughtWorks 具有 8 年云计算相关经验,多系统的研发和运维经验,敏捷和 DevOps 专家。公众号:DevOps 手册。


2018 年 12 月 28 日 16:312730

评论 3 条评论

发布
用户头像
编程语言教你如何实现功能,算法教你解决特定问题,而设计模式帮你把功能很好的封装起来,在增加新功能的时候还能保持仅仅有条
2018 年 12 月 28 日 22:39
回复
用户头像
占个坑
2018 年 12 月 28 日 18:24
回复
没有更多了
发现更多内容

SQL Server 多表数据增量获取和发布 2.1

happlyfox

28天写作 3月日更

java学习笔记(一)

陈皮

Java

代码从业者

ES_her0

28天写作 3月日更

手写一个LRU缓存淘汰算法

Simon郎

Java 大数据 缓存 LRU 数据结构与算法

想做技术自媒体实现财富自由?先看看广告报价吧,无编码学爬虫之二。

梦想橡皮擦

Python 28天写作 2月春节不断更 3月日更

数据库周刊59丨GaussDB(for openGauss)开放商用;人大金仓保障冬奥会演练顺利完成;MDL锁导致的MySQL问题分析;PG日志使用手册;达梦表空间查询;数据库笔试题面试题集……

墨天轮

MySQL 数据库 运维 postgre 程序员·

2021年美团/字节跳动/腾讯面经总结:互联网工程师必备的面试1000题

比伯

Java 编程 程序员 架构 面试

谁才是一级方程式赛车中的最强车手?

亚马逊云科技 (Amazon Web Services)

2021最新京东、字节跳动「3面面经」盘点大厂后端面试高频题

Java架构之路

Java 程序员 架构 面试 编程语言

图解CyclicBarrier运动员接力赛

叫练

面试 AQS CyclicBarrier

打破垄断 上海发布自主研发树图区块链重大成果

CECBC区块链专委会

区块链

加快布局区块链技术发展,助力网络强国建设

CECBC区块链专委会

区块链

第四章作业(二)

LouisN

四面美团开发岗,成功斩获offer,分享个人面经

Java架构之路

Java 程序员 架构 面试 编程语言

前端上手Docker超详细基础教程

1024肥宅

Docker Linux 前端 jenkins CI/CD

真实字节二面:什么是伪共享?

艾小仙

Java 程序员 字节跳动 面试 面试大厂

Docker 常用命令,还有谁不会?

xcbeyond

Docker 常用命令 28天写作

这个新春,你的云端安全守卫来咯 | 新服务上线

亚马逊云科技 (Amazon Web Services)

VR,正在上演一出“风月宝鉴”

脑极体

别梦依稀咒逝川,Ruby二十八年前|M1芯片Mac os系统配置Ruby(3.0.0) on Rails(6.1.1)开发环境(2021最新攻略)

刘悦的技术博客

ruby ruby-on-rails rubygems macOS Big Sur m1

「两次遍历」要比「一次遍历」要慢 ... 为啥呀?为啥呀??

宫水三叶的刷题日记

LeetCode 数据结构与算法 面试数据结构与算法

(28DW-S8-Day10) T型学习模式:迁移式学习

mtfelix

T型人才 28天写作 迁移学习 一万小时定律

面试系列二:精选大数据面试真题JVM专项-附答案详细解析

五分钟学大数据

大数据 28天写作 3月日更

浅谈基于ARP协议的网络攻击

行者AI

网络安全

#滴滴夜莺# Nightingale & Prometheus

漂洋散人

SuperBenchmarker sb在mac上的安装手记

edd

星环科技Sophon Edge边缘计算平台持续赋能千家万业

星环科技

七种分布式事务的解决方案,一次讲给你听

moon聊技术

环信大学 | 构建一套适合微服务的高可用架构

DT极客

javascript中的内存管理

程序那些事

JavaScript nodejs 内存管理 程序那些事

一线互联网大厂面经分享:阿里三面+头条四面+腾讯二面+美团四面

Java架构之路

Java 程序员 架构 面试 编程语言

扫盲:关于计算机,我们还应该知道些什么?-InfoQ