### 1.2.2 计算思维的具体例子
基于计算机的能力和局限,计算机科学家提出了很多关于计算的思想和方法,从而建立
了利用计算机解决问题的一整套思维工具。下面我们简要介绍计算机科学家在计算的不同阶 段所采用的常见思想和方法。
问题表示
用计算机解决问题,首先要建立问题的计算机表示。问题表示与问题求解是紧密相关的, 如果问题的表示合适,那么问题的解法就可能如水到渠成一般容易得到,否则可能如逆水行 舟一般难以得到解法。
抽象(abstraction)是用于问题表示的重要思维工具。例如,小学生经过学习都知道将 应用题“原来有五个苹果,吃掉两个后还剩几个”抽象表示成“5 - 2”,这里显然只抽取了 问题中的数量特性,完全忽略了苹果的颜色或吃法等不相关特性。一般意义上的抽象,就是 指这种忽略研究对象的具体的或无关的特性,而抽取其一般的或相关的特性。计算机科学中 的抽象包括数据抽象和控制抽象,简言之就是将现实世界中的各种数量关系、空间关系、逻
辑关系和处理过程等表示成计算机世界中的数据结构(数值、字符串、列表、堆栈、树等) 和控制结构(基本指令、顺序执行、分支、循环、模块等),或者说建立实际问题的计算模型。 另外,抽象还用于在不改变意义的前提下隐去或减少过多的具体细节,以便每次只关注少数 几个特性,从而有利于理解和处理复杂系统。显然,通过抽象还能发现一些看似不同的问题 的共性,从而建立相同的计算模型。总之,抽象是计算机科学中广泛使用的思维方式,只要 有可能并且合适,程序员就应当使用抽象。
可以在不同层次上对数据和控制进行抽象,不同抽象级对问题进行不同颗粒度或详细程 度的描述。我们经常在较低抽象级之上再建立一个较高的抽象级,以便隐藏低抽象级的复杂 细节,提供更简单的求解方法。例如,对计算本身的理解就可以形成“电子电路®门逻辑® 二进制®机器语言指令®高级语言程序”这样一个由低到高的抽象层次,我们之所以在高级 语言程序这个层次上学习计算,当然是为了隐藏那些低抽象级的繁琐细节。又如,在互联网 上发送一封电子邮件实际上要经过不同抽象级的多层网络协议才得以实现,写邮件的人肯定 不希望先掌握网络低层知识才能发送邮件。再如,我们经常在现有软件系统之上搭建新的软 件层,目的是隐藏低层系统的观点或功能,提供更便于理解或使用的新观点或新功能。
算法设计
问题得到表示之后,接下来的关键是找到问题的解法——算法。算法设计是计算思维大 显身手的领域,计算机科学家采用多种思维方式和方法来发现有效的算法。例如,利用分治 法的思想找到了高效的排序算法,利用递归思想轻松地解决了 Hanoi 塔问题,利用贪心法寻 求复杂路网中的最短路径,利用动态规划方法构造决策树,等等。前面说过,计算机在各个 领域中的成功应用,都有赖于高效算法的发现。而为了找到高效算法,又依赖于各种算法设 计方法的巧妙运用。
对于大型问题和复杂系统,很难得到直接的解法,这时计算机科学家会设法将原问题重 新表述,降低问题难度,常用的方法包括分解、化简、转换、嵌入、模拟等。如果一个问题 过于复杂难以得到精确解法,或者根本就不存在精确解法,计算机科学家不介意退而求其次, 寻求能得到近似解的解法,通过牺牲精确性来换取有效性和可行性,尽管这样做的结果可能 导致问题解是不完全的,或者结果中混有错误。例如搜索引擎,它们一方面不可能搜出与用 户搜索关键词相关的所有网页,另一方面还可能搜出与用户搜索关键词不相关的网页。作为 对比,很难想象数学家在解决数学问题时会寻求什么近似证明或对错参杂的解。
编程技术
找到了解决问题的算法,接下来就要用编程语言来实现算法,这个领域同样是各种思想 和方法的宝库。例如类型化与类型检查方法将待处理的数据划分为不同的数据类型,编译器 或解释器借此可以发现很多编程错误,这和自然科学中的量纲分析的思想是一致的。又如结 构化编程方法使用规范的控制流程来组织程序的处理步骤,形成层次清晰、边界分明的结构 化构造,每个构造具有单一的入口和出口,从而使程序易于理解、排错、维护和验证正确性。 又如模块化编程方法采取从全局到局部的自顶向下设计方法,将复杂程序分解成许多较小的 模块,解决了所有底层模块后,将模块组装起来即构成最终程序。又如面向对象编程方法以 数据和操作融为一体的对象为基本单位来描述复杂系统,通过对象之间的相互协作和交互实 现系统的功能。还有,程序设计不能只关注程序的正确性和执行效率,还要考虑良好的编码 风格(包括变量命名、注释、代码缩进等提高程序易读性的要素)和程序美学问题。
编程范型(programming paradigm)是指计算机编程的总体风格,不同范型对编程要素(如数据、语句、函数等)有不同的概念,计算的流程控制也是不同的。早期的命令式(或 称过程式)语言催生了过程式(procedural)范型,即一步一步地描述解决问题的过程。后来
发明了面向对象语言,数据和操作数据的方法融为一体(对象),对象间进行交互而实现系统 功能,这就形成了面向对象(object-oriented)范型。逻辑式语言、函数式语言的发明催生了 声明式(declarative)范型——只告诉计算机“做什么”,而不告诉计算机“怎么做”。有的语 言只支持一种特定范型,有的语言则支持多种范型。本书采用的 Python 就是支持多种编程范 型的语言,Python 程序可以是纯过程式的,也可以是面向对象的,甚至可以是函数式的。
可计算性与算法复杂性
在用计算机解决问题时,不仅要找出正确的解法,还要考虑解法的复杂度。这和数学思维不同,因为数学家可以满足于找到正确的解法,决不会因为该解法过于复杂而抛弃不用。 但对计算机来说,如果一个解法太复杂,导致计算机要耗费几年几十年乃至更久才能算出结 果,那么这种“解法”只能抛弃,问题等于没有解决。有时即使一个问题已经有了可行的算 法,计算机科学家仍然会去寻求更有效的算法。
有些问题是可解的但算法复杂度太高,而另一些问题则根本不可解,不存在任何算法过 程。计算机科学的根本任务可以说是从本质上研究问题的可计算性。例如,科幻电影里的计 算机似乎都像人类一样拥有智能,从计算的本质来说,这意味着人类智能能够用算法过程描 述出来。虽然现代计算机已经能够从事定理证明、自主学习、自动推理等“智能”活动,但 是人类做这些事情并非采用一步一步的算法过程,像阿基米德大叫“尤里卡”那样的智能活 动至少目前的计算机是没有可能做到的。
虽然很多问题对于计算机来说难度太高甚至是不可能的任务,但计算思维具有灵活、变 通、实用的特点,对这样的问题可以去寻求不那么严格但现实可行的实用解法。例如,计算 机所做的一切都是由确定性的程序决定的,以同样的输入执行程序必然得到同样的结果,因 此不可能实现真正的“随机性”。但这并不妨碍我们利用确定性的“伪随机数”生成函数来模 拟现实世界的不确定性、随机性。
又如,当计算机有限的内存无法容纳复杂问题中的海量数据时,这个问题是否就不可解 了呢?当然不是,计算机科学家设计出了缓冲方法来分批处理数据。当许多用户共享并竞争 某些系统资源时,计算机科学家又利用同步、并发控制等技术来避免竞态和僵局。
- 前言
- 第 1 章 计算与计算思维
- 1.1 什么是计算?
- 1.1.1 计算机与计算
- 1.1.2 计算机语言
- 1.1.3 算法
- 1.1.4 实现
- 1.2 什么是计算思维?
- 1.2.1 计算思维的基本原则
- 1.2.2 计算思维的具体例子
- 1.2.3 日常生活中的计算思维
- 1.2.4 计算思维对其他学科的影响
- 1.3 初识 Python
- 1.3.1 Python 简介
- 1.3.2 第一个程序
- 1.3.3 程序的执行方式
- 1.3.4 Python 语言的基本成分
- 1.4 程序排错
- 1.5 练习
- 第 2 章 用数据表示现实世界
- 2.1 数据和数据类型
- 2.1.1 数据是对现实的抽象
- 2.1.1 常量与变量
- 2.1.2 数据类型
- 2.1.3 Python 的动态类型*
- 2.2 数值类型
- 2.2.1 整数类型 int
- 2.2.2 长整数类型 long
- 2.2.3 浮点数类型 float
- 2.2.4 数学库模块 math
- 2.2.5 复数类型 complex*
- 2.3 字符串类型 str
- 2.3.1 字符串类型的字面值形式
- 2.3.2 字符串类型的操作
- 2.3.3 字符的机内表示
- 2.3.4 字符串类型与其他类型的转换
- 2.3.5 字符串库 string
- 2.4 布尔类型 bool
- 2.4.1 关系运算
- 2.4.2 逻辑运算
- 2.4.3 布尔代数运算定律*
- 2.4.4 Python 中真假的表示与计算*
- 2.5 列表和元组类型
- 2.5.1 列表类型 list
- 2.5.2 元组类型 tuple
- 2.6 数据的输入和输出
- 2.6.1 数据的输入
- 2.6.2 数据的输出
- 2.6.3 格式化输出
- 2.7 编程案例:查找问题
- 2.8 练习
- 第 3 章 数据处理的流程控制
- 3.1 顺序控制结构
- 3.2 分支控制结构
- 3.2.1 单分支结构
- 3.2.2 两路分支结构
- 3.2.3 多路分支结构
- 3.3 异常处理
- 3.3.1 传统的错误检测方法
- 3.3.2 传统错误检测方法的缺点
- 3.3.3 异常处理机制
- 3.4 循环控制结构
- 3.4.1 for 循环
- 3.4.2 while 循环
- 3.4.3 循环的非正常中断
- 3.4.4 嵌套循环
- 3.5 结构化程序设计
- 3.5.1 程序开发过程
- 3.5.2 结构化程序设计的基本内容
- 3.6 编程案例:如何求 n 个数据的最大值?
- 3.6.1 几种解题策略
- 3.6.2 经验总结
- 3.7 Python 布尔表达式用作控制结构*
- 3.8 练习
- 第 4 章 模块化编程
- 4.1 模块化编程基本概念
- 4.1.1 模块化设计概述
- 4.1.2 模块化编程
- 4.1.3 编程语言对模块化编程的支持
- 4.2 Python 语言中的函数
- 4.2.1 用函数减少重复代码 首先看一个简单的用字符画一棵树的程序:
- 4.2.2 用函数改善程序结构
- 4.2.3 用函数增强程序的通用性
- 4.2.4 小结:函数的定义与调用
- 4.2.5 变量的作用域
- 4.2.6 函数的返回值
- 4.3 自顶向下设计
- 4.3.1 顶层设计
- 4.3.2 第二层设计
- 4.3.3 第三层设计
- 4.3.4 第四层设计
- 4.3.5 自底向上实现与单元测试
- 4.3.6 开发过程小结
- 4.4 Python 模块*
- 4.4.1 模块的创建和使用
- 4.4.2 Python 程序架构
- 4.4.3 标准库模块
- 4.4.4 模块的有条件执行
- 4.5 练习
- 第 5 章 图形编程
- 5.1 概述
- 5.1.1 计算可视化
- 5.1.2 图形是复杂数据
- 5.1.3 用对象表示复杂数据
- 5.2 Tkinter 图形编程
- 5.2.1 导入模块及创建根窗口
- 5.2.2 创建画布
- 5.2.3 在画布上绘图
- 5.2.4 图形的事件处理
- 5.3 编程案例
- 5.3.1 统计图表
- 5.3.2 计算机动画
- 5.4 软件的层次化设计:一个案例
- 5.4.1 层次化体系结构
- 5.4.2 案例:图形库 graphics
- 5.4.3 graphics 与面向对象
- 5.5 练习
- 第 6 章 大量数据的表示和处理
- 6.1 概述
- 6.2 有序的数据集合体
- 6.2.1 字符串
- 6.2.2 列表
- 6.2.3 元组
- 6.3 无序的数据集合体
- 6.3.1 集合
- 6.3.2 字典
- 6.4 文件
- 6.4.1 文件的基本概念
- 6.4.2 文件操作
- 6.4.3 编程案例:文本文件分析
- 6.4.4 缓冲
- 6.4.5 二进制文件与随机存取*
- 6.5 几种高级数据结构*
- 6.5.1 链表
- 6.5.2 堆栈
- 6.5.3 队列
- 6.6 练习
- 第 7 章 面向对象思想与编程
- 7.1 数据与操作:两种观点
- 7.1.1 面向过程观点
- 7.1.2 面向对象观点
- 7.1.3 类是类型概念的发展
- 7.2 面向对象编程
- 7.2.1 类的定义
- 7.2.2 对象的创建
- 7.2.3 对象方法的调用
- 7.2.4 编程实例:模拟炮弹飞行
- 7.2.5 类与模块化
- 7.2.6 对象的集合体
- 7.3 超类与子类*
- 7.3.1 继承
- 7.3.2 覆写
- 7.3.3 多态性
- 7.4 面向对象设计*
- 7.5 练习
- 第 8 章 图形用户界面
- 8.1 图形用户界面概述
- 8.1.1 程序的用户界面
- 8.1.2 图形界面的组成
- 8.1.3 事件驱动
- 8.2 GUI 编程
- 8.2.1 UI 编程概述
- 8.2.2 初识 Tkinter
- 8.2.3 常见 GUI 构件的用法
- 8.2.4 布局
- 8.2.5 对话框*
- 8.3 Tkinter 事件驱动编程
- 8.3.1 事件和事件对象
- 8.3.2 事件处理
- 8.4 模型-视图设计方法
- 8.4.1 将 GUI 应用程序封装成对象
- 8.4.2 模型与视图
- 8.4.3 编程案例:汇率换算器
- 8.5 练习
- 第 9 章 模拟与并发
- 9.1 模拟
- 9.1.1 计算机建模
- 9.1.2 随机问题的建模与模拟
- 9.1.3 编程案例:乒乓球比赛模拟
- 9.2 原型法
- 9.3 并行计算*
- 9.3.1 串行、并发与并行
- 9.3.2 进程与线程
- 9.3.3 多线程编程的应用
- 9.3.4 Python 多线程编程
- 9.3.5 小结
- 9.4 练习
- 第 10 章 算法设计和分析
- 10.1 枚举法
- 10.2 递归
- 10.3 分治法
- 10.4 贪心法
- 10.5 算法分析
- 10.5.1 算法复杂度
- 10.5.2 算法分析实例
- 10.6 不可计算的问题
- 10.7 练习
- 第 11 章 计算+X
- 11.1 计算数学
- 11.2 生物信息学
- 11.3 计算物理学
- 11.4 计算化学
- 11.5 计算经济学
- 11.6 练习
- 附录
- 1 Python 异常处理参考
- 2 Tkinter 画布方法
- 3 Tkinter 编程参考
- 3.1 构件属性值的设置
- 3.2 构件的标准属性
- 3.3 各种构件的属性
- 3.4 对话框
- 3.5 事件
- 参考文献