原文:
zh.annas-archive.org/md5/D0EC374B077B4C90AC07697977B27176译者:飞龙
前言
《Python 应用计算思维》提供了一种实践方法,介绍了实施和相关方法,让您可以立即投入并提高工作效率。使用 Python 的开发人员将能够使用这本实用指南中的计算思维方法解决问题。
本书将帮助您在解决各种领域的现实问题时,培养逻辑处理和算法思维。这是一项必不可少的技能,您应该在这个信息技术的现代时代保持领先地位。开发人员可以将他们对计算思维的知识应用于实践,解决多个领域的问题,包括经济学、数学和人工智能。
本书首先帮助您了解分解、模式识别、模式概括和抽象以及算法设计,并教您如何在设计解决复杂问题的过程中实际应用这些要素。
我们将了解如何使用分解通过可视化表示解决问题。我们还将使用模式概括和抽象来设计解决方案,并建立评估算法解决方案所需的分析技能。我们还将使用 Python 进行统计分析时使用计算思维。
我们将了解设计算法解决方案的输入和输出要求,并使用计算思维来解决数据处理问题。我们将识别逻辑处理中的错误,以完善您的解决方案设计,并在密码学、经济学和机器学习等各个领域应用计算思维。
然后,您将了解问题分析、逻辑推理、算法设计、分类和聚类、数据分析和建模等涉及的各种技术,并了解如何将计算思维要素与这些方面结合起来设计解决方案。
我们还将培养逻辑推理和解决问题的能力,帮助您解决复杂问题。我们还将使用实际示例探索核心计算机科学概念和重要的计算思维要素,并了解如何确定最适合您问题的算法解决方案。
最后,您将了解如何在解决方案设计过程中识别陷阱,以及如何选择正确的功能来创建最佳的算法解决方案。
在阅读完本书之后,您将获得应用计算思维技术于软件开发的信心。
本书适合对象
这本书适用于希望培养解决问题技能和策略的学生、开发人员和专业人士,这些技能涉及编写或调试软件程序和应用程序。需要熟悉 Python 编程。
本书涵盖内容
第一章《计算机科学基础》帮助您了解计算机科学的基本要素,包括理论、设计、计算过程和系统以及计算机。本章重点将放在计算机科学的软件要素上。
第二章《计算思维要素》解释了计算思维的每个要素——分解、模式识别、模式概括和抽象以及算法设计——以及计算思维过程并非线性。相反,开发人员可以在算法设计过程的各个阶段回顾这些要素,直到找到特定问题的解决方案。本章将包括一些简短的相关问题,用于演示如何使用计算思维要素来得出算法。
第三章《理解算法和算法思维》为您介绍了算法及其定义。您还将回顾一些算法,以帮助您在评估算法时培养必要的分析技能。
第四章《理解逻辑推理》探讨了条件语句、算法推理和布尔逻辑等逻辑推理过程。在整个章节中,您将通过真实和相关的问题分析学习基本和中级逻辑处理技能。
第五章《探索问题分析》探讨了问题分析的主题,重点放在问题定义、分解和分析上。为了练习和进一步理解计算思维的第一个元素——分解,您将面临真实和相关的问题。然后,您将能够定义和分解问题,例如识别输入和输出,以及开始规划解决问题所需的其他相关信息。
第六章《设计解决方案和解决过程》为您提供了一个机会,利用先前学习的计算思维过程设计多个问题的解决方案,并开始将逻辑处理纳入其中,以创建解决方案的决策过程的可视化表示。可视化表示包括图表、流程图和其他有用的过程。
第七章《识别解决方案中的挑战》提供了练习识别一些常见错误和/或更好可能解决方案的机会。虽然大多数问题可以用多种算法解决,以满足问题的需求,但有些解决方案最适合特定问题。本章的目标是介绍您在解决方案设计过程中识别一些陷阱。
第八章《Python 简介》介绍了基本的 Python 命令和功能,同时将它们应用于问题。利用计算思维的元素,您将能够通过纳入先前学到的概念来设计解决方案。
第九章《理解输入和输出以设计解决方案算法》帮助您评估问题,以确定设计和实施问题的解决方案算法所需的输入和输出。
第十章《控制流》帮助您更多地了解条件语句以及在使用计算思维和 Python 编程语言解决问题时如何使用for和while循环。您将应用先前学到的逻辑处理来创建 Python 算法来解决问题。
第十一章《在简单挑战中使用计算思维和 Python》帮助您将先前获得的知识应用于多个学科中解决挑战的计算思维过程,使用真实和相关的例子,以设计每种情景的最佳算法。
第十二章《在实验和数据分析问题中使用 Python》解释了如何利用 Python 的功能来解决涉及实验数据和数据处理的问题。计算思维元素将被用来使用高级功能解决真实和相关的问题。
第十三章, 使用分类和聚类,涵盖了分类和聚类。您将使用 Pandas、Scikit-Learn 和其他软件包来创建训练和测试模型,并学习一些用于大数据分析的基本定义,如机器学习和神经网络模型。
第十四章, 在统计分析中使用计算思维和 Python,深入探讨了与统计分析相关的进一步主题,如导入数据、索引和预处理数据。然后,您将使用数据可视化来决定要探索哪些变量以进行进一步分析。
第十五章, 应用计算思维问题,帮助您解决结合了前几章各个主题的多个问题,以解决问题并设计 Python 算法。您将使用计算思维元素来确定设计模型和解决语言学、密码学等问题所需的功能。
第十六章, 高级应用计算思维问题,通过各种领域的其他应用问题,包括几何镶嵌、创建房屋数据模型、创建电场、分析基因数据、分析股票、创建卷积神经网络(CNN)等。您将使用计算思维元素来解决问题,并为问题和数据集创建不同的图形和可视化表示。
要充分利用本书
您需要在计算机上安装 Python 3.9 才能运行代码。所有代码示例都在使用 Python 3.9 的 Windows 操作系统上进行了测试,并应该在后续版本中运行。所有代码还在 Anaconda 虚拟环境中进行了测试。
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Preface_table.jpg
本书中使用的其他库和软件包包括以下内容:
-
NumPy
-
Scikit-Learn
-
TensorFlow/Keras
-
Matplotlib
-
Seaborn
-
Cairos
-
NLTK
-
Pandas
如果您想在 Spyder 环境或 Jupyter 笔记本中运行代码,可以安装 Anaconda,这是 Python 和 R 编程语言的环境管理器。
如果您使用的是本书的数字版本,我们建议您自己输入代码或通过 GitHub 存储库(链接在下一节中提供)访问代码。这样做将有助于避免与复制和粘贴代码相关的任何潜在错误。
下载示例代码文件
您可以从 GitHub 上下载本书的示例代码文件github.com/PacktPublishing/Applied-Computational-Thinking-with-Python。如果代码有更新,将在现有的 GitHub 存储库上进行更新。
我们还有来自我们丰富书籍和视频目录的其他代码包,可在github.com/PacktPublishing/上找到。去看看吧!
下载彩色图片
我们还提供了一个 PDF 文件,其中包含本书中使用的屏幕截图/图表的彩色图片。您可以在这里下载:static.packt-cdn.com/downloads/9781839219436_ColorImages.pdf。
使用的约定
本书中使用了许多文本约定。
文本中的代码:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄。例如:“最后,我们要求程序使用Readability函数分析文本。请注意,我们将其保存到r中。”
代码块设置如下:
def encrypt(message, key):
encryptedM = ''
for letts in message:
if letts in LETTERS:
num = LETTERS.find(letts)
num += key
encryptedM += LETTERS[num]
return encryptedM
任何命令行输入或输出都以以下方式编写:
There once was a citizen in the town of Narnia, whose name was Malena. Malena loved to hang with their trusty dog, King Kong.
You could always see them strolling through the market in the morning, wearing their favorite blue attire.
粗体:表示一个新术语,一个重要的词,或者屏幕上看到的词。例如,菜单或对话框中的单词会以这种方式出现在文本中。这是一个例子:“正如您从前面的截图中所看到的,死亡列继续上升,病例数量也在增加,我们稍后会在这个问题中再看一下。”
提示或重要说明
像这样出现。
第一部分:引言计算思维
在我们生活的世界中,我们在一天中不断地与代码互动,却没有意识到。当我们在网上搜索东西,使用智能手机,使用健身追踪器或智能手表,以及许多其他情况下,都涉及到算法。甚至我们的汽车也有我们每天都要与之互动的计算机。我们将从计算机科学的一些基本原理开始,看看编程和设计算法背后的东西,以及一些应用计算思维和 Python 编程语言的算法设计。
在本节中,您将清楚地了解计算机科学,计算思维的要素 - 即分解、模式识别、模式概括和抽象 - 以及算法设计。
本节包括以下章节:
-
[第一章],计算机科学基础
-
[第二章],计算思维要素
-
[第三章],理解算法和算法思维
-
[第四章],理解逻辑推理
-
[第五章],探索问题分析
-
[第六章],设计解决方案和解决方案过程
-
[第七章],识别解决方案中的挑战
第一章:计算机科学基础
计算机科学的世界是一个广阔而复杂的世界。它不仅不断变化和发展,而且我们认为是计算机科学的组成部分也在不断适应和调整。计算思维过程使我们能够有目的地和专注地解决任何问题。无论问题是什么,我们都可以将其分解,找到可以帮助我们找到解决方案的模式,概括我们的解决方案,并设计可以帮助我们提供解决方案的算法。
在本书中,我们将仔细研究计算思维过程,解决多个领域的问题,并使用 Python 编程语言及相关库和包来创建算法,帮助我们解决这些问题。然而,在进入问题之前,我们将探讨一些重要的计算机科学概念,这些概念将帮助我们导航本书的其余部分。
在本章中,我们将探讨以下主题:
-
计算机科学简介
-
理论计算机科学
-
系统软件
-
计算
-
数据类型和结构
技术要求
您需要最新版本的 Python 来运行本章中的代码。您可以在此处找到本章使用的完整源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter01
计算机科学简介
当寻找计算机科学的定义时,您会遇到多种变体,但所有都指出计算机科学涵盖了计算机和计算概念的所有方面,包括硬件和软件。在计算机科学中,硬件设计主要在工程或计算机工程课程中学习。计算机科学的软件方面包括操作系统和应用程序等编程领域。在本书中,我们将集中在计算机科学的软件方面。
在本章中,我们将研究一些基本的定义、理论和系统,这些对我们深入计算思维世界至关重要。一旦我们确定了关键领域并定义了概念,我们就准备好进入应用和现实世界中面临的挑战,同时探索计算思维的要素和可以帮助我们应对这些挑战的 Python 编程能力。
计算机科学中提供的广泛主题范围既令人望而生畏又令人兴奋,它不断发展。其中一些主题包括游戏设计、操作系统、移动或桌面设备的应用程序、机器人编程等。计算机和计算领域的不断突破为我们提供了新的和令人兴奋的机会,其中许多是我们所不知道的。对计算机科学背后的系统有基本的了解可以帮助我们更有效地与技术互动并解决问题。
学习计算机和二进制系统
所有计算机都将信息存储为二进制数据。二进制系统将所有信息视为开关,可以是开或关,0 或 1。二进制系统是一个基于 2 的系统。您需要对二进制数字和二进制系统有基本的了解,才能在计算机科学中取得进展。
二进制系统将所有数据转换为字符串,只使用两个数字:0 和 1。数据在计算机中使用比特存储。比特(代表二进制数字)是计算机中可以找到的最小数据单元,即 0 或 1。
在二进制系统中计数时,前两个数字是 0(或 00)和 1(或 01),就像我们在日常生活中使用的十进制数系统一样。如果我们继续以二进制计数,下一个数字将是 10。在学习如何从一个系统转换为另一个系统之前,让我们比较一下十进制系统和二进制系统中的前三个数字:
图 1.1-十进制和二进制比较
十进制系统中的下一个数字将是 3。在二进制系统中,下一个数字将是 11,读作“一一”。十进制和二进制系统中的前 10 个数字如下所示:
图 1.2-十进制和二进制比较(续)
如前所述,二进制系统是一个基于 2 的系统。这意味着十进制系统的每个数字都与 2 的幂配对,因此我们使用这些幂来在数字之间进行转换。了解如何从二进制转换为十进制,反之亦然,可以帮助我们更好地理解不同系统中数字之间的关系。
从二进制转换为十进制
我们将从一个示例开始,将二进制数转换为十进制数。取数字 101101。要转换该数字,每个数字都乘以相应的基数 2 的幂。给定的二进制数字有 6 位,因此我们将使用的 2 的幂是 5、4、3、2、1 和 0。这意味着数字转换如下:
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_01_001.jpghttps://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_01_002.jpg
二进制数 101101 相当于十进制系统中的 45。在日常生活中,我们以十进制书写数字,因此我们理解 45 这个数字的书写方式。然而,我们的计算机将这些信息转换为二进制以便能够处理它,因此该数字变成了二进制数 101101,以便计算机能够轻松读取。
从十进制转换为二进制
再次,让我们从一个示例开始,以演示从十进制数转换为二进制数的过程。取数字 591。要将十进制数转换为二进制数,我们必须迭代地将数字除以 2。如果结果没有余数,我们插入 0(如果是第一个数字)或在现有数字的左侧插入 0。
如果结果有余数 1,我们插入 1(如果是第一个数字)或在现有数字的左侧插入 1。
当我们将 591 除以 2 时,结果是 295,余数为 1。这意味着我们最右边的数字,也就是我们的第一个数字,是 1。
现在将 295 除以 2。结果是 147,余数为 1。因此,我们在 1 的左侧插入 1。我们的数字现在是 11。
现在将 147 除以 2。结果是 73,余数为 1。我们的结果现在是 111。现在我们将进行进一步的除法:
-
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_01_003.png余数为 1。我们的数字现在是 1111。
-
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_01_004.png没有余数。我们的数字现在是 01111。
-
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_01_005.png没有余数。我们的数字现在是 001111。
-
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_01_006.png余数为 1。我们的数字现在是 1001111。
-
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_01_007.png没有余数。我们的数字现在是 01001111。
-
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_01_008.png没有余数。我们的数字现在是 001001111。
-
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_01_009.png余数为 1。我们的数字现在是 1001001111。
十进制中的数字 591 相当于二进制系统中的数字 1001001111。
另一种转换数字的方法是使用表格进行除法:
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/B15413_Table_1.1.jpg
表 1.1-将十进制数 591 转换为二进制
使用表格,取最右边一列的数字,并从底部到顶部开始写下这些数字。结果是 1001001111。
学习如何转换数字只是将数据转换为二进制的一小部分,但它是一个重要的部分。所有信息,包括字母和符号,都必须转换为二进制才能被计算机读取。ASCII(代表美国信息交换标准代码)是一个被普遍采用的协议,用于转换信息。也就是说,该协议的一些部分已经过时,因此其他协议使用 ASCII 作为基础来扩展其功能。Unicode 是一个广泛使用的基于 ASCII 的 16 位字符集。
正如讨论的,在本节中,我们了解到信息必须被编码或转换才能被计算机读取。存在多个系统和协议,但现在我们将继续讨论计算机科学理论。然而,在解决问题时,回顾二进制、ASCII 和 Unicode 可能会有所帮助。
理解理论计算机科学
虽然你不需要成为数学大师才能喜欢计算机科学,但这两个学科是内在联系的。计算机科学,特别是编程,使用的是代数性质的算法。我们将在后面深入探讨算法,但重要的是它们是数学的。逻辑过程源自数学的哲学性质和历史。现在,如果数学主题不是你的菜,不要绝望。成为程序员和开发人员所需的逻辑过程可以在不学习更高数学的情况下使用。了解更高的数学只是为那些具有这方面背景的人简化了一些概念。
理论计算机科学包括多个理论和主题。以下列出了一些主题和理论,但请记住,理论计算机科学中还包括其他可能在本书中未讨论的主题。以下列出的每个理论或术语都包括简短的描述和解释供您审阅:
-
算法
-
编码理论
-
计算生物学
-
数据结构
-
密码学
-
信息理论
-
机器学习
-
自动机理论
-
形式语言理论
-
符号计算
-
计算几何
-
计算数论
我们将在接下来的章节中看一下上述的理论。
算法
算法是计算机可以读取的一组指令。算法以一种计算机可以逻辑处理提供的输入信息并创建输出的方式提供规则或指令。在大多数书籍中,你是通过创建*Hello World!*程序来介绍算法和编程的。我不会让这本书成为例外。
在 Python 中,代码需要打印消息到屏幕上。因为 Python 语言易学易读,许多代码都力求逻辑。因此,为了将消息打印到屏幕上,我们使用print()命令。以下是我们将使用的代码:
print("Hello world!")
同样,我们可以使用以下给出的代码:
print('Hello world!')
当涉及到字符串时,Python 将*"和’*视为相同的东西。
当我们运行算法时,前面代码的结果如下截图所示:
图 1.3 – ‘Hello World!’ Python 程序
别担心,我们将在第二章后面讨论 Python 编程语言,计算思维要素,以及在第二部分,应用 Python 和计算思维中更深入地讨论,从第八章开始,Python 简介。
尽管讨论算法很长,但对于本书和你学习 Python 的进展至关重要。因此,我们将在本书的第二章——计算思维要素和第三章——理解算法和算法思维中深入探讨算法,因为算法是计算思维过程的关键要素。
重要提示:
第二章——计算思维要素将专注于计算思维过程本身,它有四个要素:分解、模式识别、模式概括和抽象和算法设计。正如你所看到的,最后一个要素是算法设计,因此我们需要更加了解算法是什么以及如何创建它们,这样你就可以在用 Python 解决问题时实施和设计算法。第三章——理解算法和算法思维将专注于更深入地理解算法定义以及设计过程的介绍。
接下来我们将看编码理论。
编码理论
编码理论有时也被称为代数编码理论。在处理代码和编码理论时,有三个研究领域:数据压缩、错误校正和密码学。我们将在接下来的章节中更详细地介绍这些内容。
数据压缩
数据压缩的重要性不容忽视。数据压缩使我们能够在占用最少空间的情况下存储最大量的信息。换句话说,数据压缩使用最少的位来存储数据。
重要提示:
请记住,比特是计算机中最小的数据单位,即 0 或 1,而一组比特称为字节。一个字节通常有 8 个比特。我们使用字节作为计算机内存、存储设备(如存储卡或外部驱动器)等大小的度量单位。
随着技术和存储容量的增长和改善,我们存储额外数据的能力也在增加。从历史上看,当计算机首次进入家庭时,它们通常只有千字节或兆字节的存储空间,但现在它们拥有千兆字节和太字节的存储空间。各种存储单位的换算如下所示:
图 1.4 – 字节转换
如果你在网上寻找信息,可能会发现一些来源声称一太字节等于 1,024 千兆字节。这是一个二进制换算。在十进制系统或十进制系统中,一太字节等于 1,000 千兆字节。要更好地理解换算,重要的是要了解适用于十进制系统和二进制系统的前缀:
https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/B15413_Table_1.2.jpg
表 1.2 – 带值的十进制和二进制前缀
如前所述,目标始终是尽量使用最少的位来存储尽量多的数据。因此,我们压缩或减少数据的大小,以便使用更少的存储空间。
那么,*数据压缩为什么如此重要呢?*让我们回到 2000 年。那时,一台售价约 1000 美元的笔记本电脑大约有 64MB 的RAM(随机存取存储器)和 6GB 的硬盘内存。我们手机上的一张照片在使用其实际大小时需要 2 到 5 兆字节的内存。这意味着我们的计算机无法存储现在拍摄的许多(在某些情况下是任何)现代照片。数据压缩的进步使我们能够存储更多内存,创建更好的游戏和应用程序,以及更多,因为我们可以拥有更好的图形和额外的信息或代码,而无需太担心它们使用的内存量。
错误更正
在计算机科学中,错误是生活中的一个事实。我们在我们的过程、算法、设计和各种其他方面都会犯错误。错误更正,也称为错误处理,是计算机自动纠正错误或多个错误的过程,这种错误发生在数字数据被错误传输时。
错误更正码(ECC)可以帮助我们分析数据传输。ECC 可以定位和纠正传输错误。在计算机中,ECC 内置在一个可以识别常见内部数据损坏问题的存储空间中。例如,ECC 可以帮助读取损坏的代码,比如QR(快速响应)代码的缺失部分。一种 ECC 是汉明码。汉明码是一种二进制线性码,可以检测最多两位错误。
重要说明:
汉明码是以 1950 年发现它们的理查德·韦斯利·汉明命名的。汉明是一位数学家,他在与电信和计算机工程相关的编码方面进行了研究。
另一种 ECC 是奇偶校验位。奇偶校验位检查数据的状态,并确定是否有任何数据丢失或被覆盖。错误更正对所有开发的软件都很重要,因为任何更新、更改或升级都可能导致整个程序或部分程序或软件的损坏。
密码学
密码学用于计算机科学中隐藏代码。在密码学中,信息或数据被编写成只有消息的预期接收者才能阅读的形式。简单来说,密码学将可读文本或信息转换为不可读的文本或信息。
当我们现在考虑密码学时,我们往往会想到数据的加密。编码者通过将数据转换为未经授权用户无法看到的代码来加密数据。然而,密码学已经存在了几个世纪,即它早于计算机。历史上,密码学的第一个用途可以追溯到公元前 1900 年左右的埃及一座墓穴中。在墓穴的各个部分,常见的象形文字与非典型或不寻常的象形文字混合在一起。
非典型的象形文字的原因是未知的,但这些信息被隐藏起来,不让其他人看到。后来,密码学被用于在战争和和平时期的政府和间谍之间进行秘密通信。如今,密码学被用于加密数据,因为我们的信息存在于数字格式中,因此保护敏感信息,如银行、人口统计或个人数据,是很重要的。
我们将通过本书中提出的一些问题进一步探讨编码理论的主题。
计算生物学
计算生物学是理论计算机科学的一个领域,专注于生物数据和生物信息学的研究。生物信息学是一门科学,允许我们收集生物数据并对其进行分析。生物信息学的一个例子是收集和分析基因密码。在生物学研究中,大量数据被探索和记录。
研究可以涉及各种各样的主题和跨学科。例如,遗传研究可能包括来自整个州、整个种族或整个国家的数据。计算生物学的一些领域包括分子、细胞、组织和生物体。计算生物学使我们能够研究这些事物的组成,从最基本的水平到更大的生物体。生物信息学和计算生物学为这些领域的实验研究提供了一个结构,创建了预测和比较,并提供了一种发展和测试理论的方式。
计算思维和编码使我们能够处理和分析这些数据。在本书中,所提出的问题将使我们能够探索如何使用 Python 结合计算思维来解决复杂问题,包括计算生物学中的问题。
数据结构
在编码理论中,我们使用数据结构来收集和组织数据。目标是准备数据,以便我们可以高效和有效地执行操作。数据结构可以是原始的或抽象的。软件具有内置的数据结构,这些是原始的数据结构,或者我们可以使用编程语言定义它们。原始数据结构是预定义的。一些原始数据结构包括整数、字符(char)和布尔结构。抽象或用户定义的数据结构的示例包括数组和二维数组、栈、树和二叉树、链表、队列等。
用户定义的数据结构具有不同的特征。例如,它们可以是线性的或非线性的,均匀的或非均匀的,静态的或动态的。如果我们需要将数据排列成线性序列,我们可以使用数组,这是一种线性数据结构。如果我们的数据不是线性的,我们可以使用非线性数据结构,例如图形。当我们有相似类型的数据时,我们使用均匀的数据结构。
请记住,例如,数组既是线性的又是均匀的数据结构。非均匀或异质数据结构具有不同的数据。用户可以创建的非均匀数据结构的一个例子是类。静态和动态数据结构的区别在于静态结构的大小是固定的,而动态结构的大小是灵活的。为了更好地理解数据结构,我们将通过本书中的计算思维元素来解决问题,再次简要地回顾数据结构,因为它们与数据类型有关,那时会讨论。
信息理论
信息理论被定义为一种数学研究,允许对信息进行编码,以便通过计算机电路或电信信道进行传输。信息通过可能包含符号、脉冲甚至无线电信号的序列进行传输。
在信息理论中,计算机科学家研究信息的量化、数据存储和信息通信。信息在信息理论中可以是模拟的或数字的。模拟数据指的是由模拟信号表示的信息。反过来,模拟信号是随着给定时间段的连续波动。数字信号将数据显示为二进制,即离散波。我们将模拟波表示为正弦波,将数字波表示为方波。以下图显示了正弦曲线作为时间值的函数:
与反比关系相关,我们也可以说:
- 正弦曲线的相移是曲线从 0 处的偏移量。这在下图中显示:
图 1.6 - 相移示例
相比之下,数字信号图看起来像条形图或直方图。它们只有两个数据点,0 或 1,所以它们看起来像是方盒状的山丘和山谷:
图 1.7 - 数字信号
数字信号具有有限的离散数据集。数据集是离散的,因为它包含单独和不同的数据点。对于模拟信号,数据是连续的和无限的。在计算机科学中,这两种类型的信号都很重要和有用。我们将在本书的后续问题中探索数字信号,并特别在第十六章中,高级应用计算思维问题中探索数字信号的应用。
自动机理论
自动机理论是理论计算机科学中最迷人的话题之一。它涉及研究机器以及如何以最可靠和高效的方式完成计算。自动机理论涉及简单机器的物理方面以及逻辑处理。那么,自动机到底用于什么,它是如何工作的呢?
自动机是使用预定条件来响应外部输入的设备。当你看你的恒温器时,你正在使用一个自动机。你设置你想要的温度,恒温器会对外部来源进行信息收集并相应地调整温度。
自动机的另一个例子是外科手术机器人。这些机器人可以改善患者的手术结果,并不断得到改进。由于自动机理论的目标是制造可靠和高效的机器,它是人工智能和智能机器(如外科手术机器人)发展中的关键部分。
形式语言理论
形式语言理论在计算机科学中通常与自动机理论联系在一起。形式语言是研究语法、语法、词汇和一切涉及形式语言的学科。在计算机科学中,形式语言指的是计算机编程语言的逻辑处理和语法。关于自动机,机器处理形式语言以执行为其提供的任务或代码。
符号计算
符号计算是计算数学的一个分支,涉及计算代数。术语符号计算和计算代数有时可以互换使用。一些编程软件和语言专注于数学公式的符号计算。使用符号计算的程序执行诸如多项式因式分解、简化代数函数或表达式、找到多项式的最大公约数等操作。
在本书中,当解决一些现实世界中出现的问题时,我们将使用计算机代数和符号计算。Python 允许我们不仅执行可能需要的数学计算,还可以探索由这些计算产生的图形表示或模型。当我们探索解决现实世界的问题时,我们将需要使用 Python 编程语言的各种库或扩展。在本书的第二部分,应用 Python 和计算思维中,我们将更详细地探索 Python 编程语言。
计算几何
像符号计算一样,计算几何存在于处理计算数学的计算机科学分支中。我们在计算几何中研究的算法是可以用几何表达的。对数据的分析是通过几何图形,几何分析,遵循几何模式的数据结构等进行的。需要计算几何的问题的输入和输出是几何的。
在考虑几何时,我们经常会回到我们最常与数学分支相关联的图形,如多边形、三角形和圆。也就是说,当我们看计算几何时,一些算法是可以用点、线、其他几何图形或遵循几何模式的算法来表达的。三角测量属于计算机科学的这一分支。
数据的三角测量对于光学 3D 测量系统等应用非常重要。我们三角测量 GPS 信号来定位手机,例如,这在执法中使用。
在现代,三角测量有许多用途,其中一些我们将通过本书中提出的真实和相关问题进行探讨。
计算数论
数论是研究整数及其性质的数学分支。计算数论是用于解决数论问题的算法的研究。数论研究的一部分是素性测试。
用于确定输入或输出是否为素数的算法已被用于许多目的。素性测试和数论的一个最重要的用途和应用是用于加密目的。随着我们的生活转移到电子保存,我们最个人的信息,如银行信息、家庭信息,甚至社会安全号码,都存在某种代码或算法中。重要的是加密这样的信息,以便他人无法使用或访问。计算数论和密码学是内在联系的,您稍后将能够探索。
一些理论旨在帮助您了解计算机科学理论是如何交织在一起的,它们的应用以及它们与我们每天所做的工作的相关性。
在这一部分,我们学习了关于理论计算机科学的知识。我们还学习了它的各种理论。
在本书中,我们将使用计算思维(在第二章中进一步讨论,计算思维的要素)来帮助我们解决问题,从最基本的应用到一些复杂的分析,通过定义和设计使用这些理论的适当算法。理论计算机科学用于研究系统软件,我们将在下一节中探讨。
学习系统软件
系统软件用于执行多个功能,并在计算机的操作系统(OS)、外围设备(如键盘和鼠标)和固件之间进行通信。固件是永久保存在设备上并且需要用于其操作的软件,这些是两种主要类型的软件的一部分:系统软件和应用软件。
系统软件允许计算机在硬件和应用程序之间进行通信。想想智能手机。手机在其最基本的形式中由硬件组成,包括电池、相机、内存、屏幕和所有物理组件和外围设备。操作系统允许应用程序使用这些组件。
手机的相机应用程序。系统软件让应用程序与手机通信,使用相机拍照,编辑,保存和分享照片。计算机的操作系统也允许硬件与程序通信。设计程序将使用鼠标或其他外围设备来绘制,创建,使用触摸屏(如果可用)等。
如果我们不了解系统的软件,就无法创建能够有效与我们的硬件通信的应用程序,从而产生从关键错误到使外围设备无用的错误,或者是一些组件可能工作,比如拍照,但其他可能不工作,比如保存或分享照片的次要错误。系统软件是以一种方式创建的,以便为我们提供在硬件和应用程序之间进行最简单、最有效的通信方式。
操作系统
操作系统执行多个任务。如果您回忆一下,错误处理是操作系统的一部分,它检查最常见的可能错误,以便在不造成更大问题或使应用程序变得毫无价值的情况下修复它们。错误处理是操作系统最重要的任务之一。此外,操作系统负责计算机或设备的安全。如果您有智能手机,您会知道许多操作系统的更新是为了解决安全问题或防止安全漏洞。操作系统负责只允许授权用户与设备中存储的内容进行交互。
除了安全和错误处理,操作系统还负责为文件分配内存并对其进行组织。当我们保存和删除文件或程序时,之前使用的内存现在是空闲的。但是,可能会有一些东西在之前和之后立即保存。操作系统通过分配和重新分配内存来维护设备的最佳性能。内存管理不仅指用户保存的文件,还指 RAM。
设备的文件管理也由操作系统运行。操作系统将信息分配为文件系统,将信息分成用户和设备轻松访问的目录。文件系统负责跟踪文件的位置,包括来自操作系统和用户的文件,不断发展的设备访问设置,以及如何访问文件和了解文件状态。近年来,对设备的访问方式发生了变化。
虽然计算机通常使用用户名和密码,但现在许多设备可以通过指纹、数字或字母数字密码、面部识别、图像、路径等进行访问。随着这些主题的发展,操作系统也需要更新或重新创建。操作系统还负责允许应用程序与设备之间的通信。
应用软件
应用软件是指执行特定任务的软件应用程序。想想你可以从移动设备访问的应用程序或应用。有数百种类型的应用程序,例如设备上的静态游戏,允许您远程与他人玩游戏的游戏,新闻应用程序,电子书阅读器,健身训练应用程序,闹钟,时钟,音乐等等!应用程序总是执行某种形式的任务,无论是个人使用、商业使用还是教育使用。
应用软件具有多个功能。您可能会发现用于生产力的套件,例如Microsoft(Office)和Google产品。当我们需要在互联网上进行研究时,我们使用称为浏览器的应用程序,它们允许我们访问信息并对信息进行索引,以便我们可以访问它。这些浏览器包括Google Chrome、Safari、Firefox、Edge、Opera等。浏览器既可用于移动设备,也可用于计算机。请记住,应用程序的目的是为最终用户执行特定任务。
重要提示:
顺便说一句,自从计算机成为家庭工具并且手机开始被用于除了打电话之外的其他用途以来,应用程序已经呈指数级增长。早期的计算机只用于计算数学分析和任务。这就是为什么了解计算机科学的发展和历史如此重要的原因之一。由于我们无法完全预测计算机科学和系统软件的未来用途,我们对它们了解得越多,就越能在技术进步发生时创造和适应。
在本节中,我们了解了系统软件。我们还了解了操作系统软件和应用软件。对于本书的目的,一些应用程序将更为重要,因为我们在解决一些问题时需要排序,例如数据库、生产力软件、企业资源规划和教育软件。
在下一节中,我们将学习计算。
理解计算
在计算机科学中,计算指的是计算机执行以进行通信、管理和处理信息的活动。计算通常分为四个主要领域:算法、架构、编程语言和理论。
由于我们在之前的部分中已经讨论了理论和算法,现在我们将专注于定义架构和编程语言。
架构
计算机架构指的是与计算机系统交互的指令集。更基本的说,架构包括允许软件和硬件交互的指令。计算机架构有三个主要的子类别:指令集架构(ISA)、微架构和系统设计。
指令集架构(ISA)
ISA 是硬件和软件之间存在的边界。它以多种方式分类,但两种常见的是复杂指令集计算机(CISC)和精简指令集计算机(RISC)。它们的定义如下:
-
CISC:这是一种具有许多任务的显式指令的计算机,例如简单的数学运算和从内存加载内容。CISC 包括 RISC 中不包括的所有内容。
-
RISC:这是一种具有减少每条指令周期(CPI)的架构的计算机。
CISC 试图用更少的步骤完成指令,而 RISC 只使用简单的指令。CISC 是多步的,而 RISC 是单步的,一次执行一个任务。CISC 过程包括指令、微码转换、微指令和执行。相比之下,RISC 包括指令和执行。
在 CISC 中,微码转换指的是在较低级别解释语言。它考虑硬件资源来创建微指令。微指令是微码中的单个指令。微码创建微指令后,可以执行微指令。以下图表显示了 RISC 和 CISC 的过程:
图 1.8 - RISC 和 CISC 之间的差异
RISC 和 CISC 对计算机程序员来说都是必不可少的。单步过程(RISC)与多步过程(CISC)相比有优势和劣势。RISC 减少了每条指令的周期,一次只做一件事。CISC 减少了程序中的指令,但以每条指令的周期为代价。根据我们的需求,我们可以选择最佳路径。
编程语言
编程语言是我们为计算机和其他设备编写指令的方式。根据需要、易用性等因素,使用不同的语言。编程语言的例子包括以下内容:
-
Ruby 和 Python:Ruby 是主要用于 Web 应用程序的编程语言。Ruby 稳定且易于使用;然而,许多开发人员选择使用 Python 而不是 Ruby,因为 Python 更快。尽管 Ruby 并不像 Python 那样受欢迎并且存在一些性能问题,但该语言在 2019 年仍然非常活跃并持续增长。另一方面,Python 被广泛用于多种用途,如 Web 应用程序、用户界面应用程序和网站等。我们将在本书的后面更深入地探讨 Python。
-
C:C 语言是计算机科学的一个非常重要的部分,因为 C 是第一种使用的语言,仍然是最广泛使用的语言。C 自 1972 年以来一直存在,当时 Dennis Ritchie 发明了它,但自 1978 年首次发布以来就被其他人使用。尽管其他语言自那时以来变得更受欢迎,但 C 在 2019 年仍在使用。它的一些用途包括操作系统、硬件驱动程序和应用程序等。C 是一种基本级别的语言,这意味着它几乎不需要抽象。
-
C++:C++是由 Bjarne Stroustrup 于 1985 年作为 C 的扩展开发的。该语言的目标是添加面向对象的能力。该语言仍然广泛用于操作系统和其他软件中与 C 语言一起使用。C++是一种中级编程语言。
-
C#:C#(C sharp)是一种高级编程语言。与 C++类似,它具有面向对象的能力,并且是 C 编程语言的扩展。C++和 C#之间的主要区别之一是 C++使用机器代码,而 C#使用字节码。机器代码可以直接由计算机执行。字节码必须被编译,因此被认为是需要解释的低级代码。
-
Swift:Swift 编程语言是由苹果公司于 2014 年开发的。就编程语言而言,Swift 是最新的之一。苹果在 2015 年发布了2.2 版本作为开源编程语言。该语言被认为是一种通用和编译的编程语言。
-
Scratch:Scratch 于 2002 年由麻省理工学院媒体实验室开发为一种视觉编程、块编码语言。作为一种块编程语言,它在学校中被广泛用于教授各个年龄段的学生如何编码。Scratch 现在适用于多种用途,包括一些机器人应用程序,如 Vex Code,结合了机器学习和人工智能等。它与Makey Makey等流行的课堂外设兼容,这是一种与计算机交互并可以完全由 Scratch 程序控制的电路。虽然它在教育目的上很受欢迎,但编程语言的强大和其功能本身仍在不断增长。
-
Java 和 JavaScript:JavaScript 是一种仅在浏览器中使用的脚本语言。它用于制作网站和 Web 应用程序。另一方面,Java 是一种通用编程语言。JavaScript 帮助我们使网站动画化或为其添加交互功能。相比之下,Java 被编译为字节码,并且广泛用于开发 Android 设备和应用程序。
-
PHP:PHP 又称为超文本预处理器。与 Java 类似,它是一种通用编程语言。它是开源的,因此广泛可用。PHP 用于网站设计和应用程序,并被认为易于学习,但具有许多高级功能。PHP 也可以用于编写桌面应用程序。
-
SQL:SQL,或结构化查询语言,是一种用于与数据交互的编程语言。SQL 是特定领域的。它自 1974 年首次出现以来几乎与 C 一样重要。SQL 的主要重要性在于它可以与数据库交互,而其他语言无法做到这一点。
在计算思维中,我们使用许多不同的编程语言,这取决于我们的目标是什么,我们有或需要什么信息,以及我们的应用程序或软件要求是什么。选择一种语言不仅取决于我们对语言的了解,还取决于语言的可能功能。
由于 Python 具有开放源代码的特性、易于使用以及可以用于大量应用程序,我们将在本书中更广泛地使用 Python。然而,Python 并不是唯一的选择。了解其他语言也很重要,特别是对于开发人员来说。
在本节中,我们学习了计算和它的一些领域,即架构和编程语言。我们还学习了 ISA 及其类型,以及对各种编程语言的介绍。在下一节中,我们将看看数据类型和结构。
学习数据类型和结构
在计算机科学中,数据类型和结构是两个不同的东西:
-
数据类型是一种基本分类。一些数据类型包括整数、浮点数和字符串。
-
数据结构使用多种数据类型。它们可以将信息组织到内存中,并确定我们如何访问信息。
让我们在接下来的部分更详细地看一下这些。
数据类型
如前所述,数据类型是基本分类。它们是程序中使用的变量,只能存在一个分类。有不同类的数据类型。我们现在将重点关注原始和抽象数据类型,但随着我们解决问题和设计解决方案,我们将重新讨论这个话题。
原始数据类型包括字节、短整型、整型、长整型、浮点型、双精度浮点型、布尔型和字符型:
-
一个字节可以存储-128 到 127 的数字。虽然这些数字可以存储为整数,或int,但字节使用的存储空间更少,所以如果我们知道数字在这些值之间,我们可以使用字节数据类型。
-
短整型是-32,768 到 32,767 之间的数字。
-
整数int用于存储-2,147,483,648 到 2,147,483,647 之间的数字。
-
长整型用于存储-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 的数字。
-
浮点数允许我们保存小数。
-
十进制数也可以保存为双精度浮点型,它比浮点数具有更高的精度。
-
布尔值是数据类型,要么是
True,要么是False。因此,可以保存变量,以便在打印其值时,结果将保存为真或假。 -
字符用于将变量保存为单个字符。
我们将在下一节中研究数据结构。
数据结构
正如本章前面的编码理论部分所述,数据结构用于以最有效和最有效的方式收集和组织数据。数据结构可以是原始的,例如软件中的内置数据结构,也可以是抽象的。原始数据结构也可以使用编程语言定义,但它们是预定义的。一些原始数据结构包括前一节中列出的数据类型,如字符和布尔结构。
抽象数据类型(ADTs)包括数据类型的结构和设计信息。抽象数据结构包括数组和二维数组、栈、树和二叉树、链表、队列等,正如本章前面的编码理论部分所述。列表可以包含相同数据值的多个实例。这些列表是可计数的,因此我们可以找到列表中有多少元素,重新排序它们,删除项目,添加项目等。列表广泛用作链表、数组或动态数组:
-
链表意味着列表中的每个数据元素都连接或指向下一个数据元素,无论它们存储在内存的何处。
-
数组是有序的。元素按顺序读取以便理解。把数组想象成阅读这个句子。你不会把句子读成“数组一个思考阅读作为这的句子”。我们按顺序从左到右读句子,而不是混乱的顺序。
-
动态数组可以调整大小,这在选择数据类型时很重要。
栈 ADT 是元素的集合,有两个操作 - 推入和弹出。推入用于向集合中添加元素,而弹出则移除最近的元素。
队列 ADT 是一种线性数据结构。与栈类似,我们可以添加或移除元素。但是,在队列 ADT 中,删除和插入的点在两个不同的端点进行。
如前所述,数据结构是数据类型的具体实现。例如,我们如何向集合中添加或移除元素就是数据结构。
这可能有点令人困惑,但我们将通过后面章节中的示例来更多地了解它们。目前,理解定义和简单示例就足够了。
总结
本章我们学习了一些计算机科学的基础知识。我们学习了如何将二进制转换为十进制。我们还探讨了理论计算机科学中的主题和理论。我们学习了关于计算和数据类型以及数据结构的知识。这些部分将帮助我们理解计算思维过程以及如何解决各种问题,从本书的第二章《计算思维要素》开始。
随着我们深入研究计算思维世界和过程,我们需要重新审视本章的一些内容,因为我们要解决问题,寻找最佳解决方案,并决定如何编写算法。
问题可能有无数种使用算法解决的方法。了解过程如何工作以及哪种数据结构最适合我们的问题对于创建最佳解决方案至关重要。确定算法所需的数据类型以及计算机如何读取数据只会帮助我们编写最有效和高效的算法。
在下一章中,我们将学习计算思维过程以及如何分解问题以设计我们的算法解决方案。
第二章:计算思维的元素
上一章提供了关于计算机科学基础的一些一般信息。在本章中,我们将更专注于理解计算思维和构成计算思维的元素:分解、模式识别、模式泛化或抽象以及算法设计。
计算思维过程的重要性不可低估。通过这个过程,我们分析问题,设计基于一组条件的最佳解决方案。虽然可以编写许多算法来回答相同的问题,但使用计算思维过程可以帮助我们确定设计清晰算法的最佳路径。
在本章中,我们将涵盖以下主题:
-
理解计算思维
-
分解问题
-
识别模式
-
泛化模式
-
设计算法
-
额外的问题
为了更多地了解计算思维,我们将通过一个提出的问题来看待这些元素。请记住,在第八章中,Python 简介中,我们将深入研究 Python 编程语言,但在您深入研究本书的本章和其他章节时,您可以随时来回查阅该章节。
技术要求
您需要最新版本的 Python 来运行本章中的代码。您可以在此处找到本章中使用的完整源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter02
理解计算思维
在其最基本的定义中,计算思维是一个解决问题的过程。就像设计思维、科学方法和其他类似的方法一样,我们经历一系列步骤来找到解决方案。例如,科学方法有七个步骤。请记住,关于科学方法存在多种解释,有些在步骤数量上有所不同。在本讨论中,我们将使用以下七个步骤:
-
问题
-
假设
-
材料
-
实验
-
结果
-
结论
-
发现的沟通
科学方法的确立是一个备受争议的话题,但大多数研究人员都同意它可以追溯到 10 世纪。
科学方法使我们能够观察自然界,提出假设来测试观察,并通过一个既定的过程开发测试和结果。这种方法本身在柏拉图和亚里士多德等哲学家的基础上有一定的依据,他们提倡经验研究。然而,他们的方法论不如今天我们所说的科学方法发展得那么成熟。
计算思维的元素类似于科学方法。计算思维使用较少的步骤来解决与编程相关的问题,而科学方法用于实验。通过计算思维,我们泛化算法,而在科学方法中,我们能够重现结果并从样本泛化到总体得出结论。
在现代,我们根据我们追求的研究领域和我们开发的技术,发展了其他方法论。其中两个例子是设计思维过程和计算思维。
设计思维有五个步骤或阶段:
-
共情
-
定义
-
构思
-
原型
-
测试
我们使用上述阶段来理解客户、班级、问题、情况或其他需要解决的情况的需求。与用户的需求产生共鸣有助于我们确定和定义问题。构思和原型阶段是我们创建可能解决方案的地方。测试可能的解决方案是找到最佳解决方案的下一个必要步骤。在所有阶段之后,如果需要,我们可以重新进行循环,因为设计思维过程的目标不是完美,所以总是可以进行额外的工作。设计思维的目标是为明确定义的问题提供一个有效和可行的解决方案。这不是唯一可行的解决方案,也不是完美的解决方案。
在计算思维中,我们使用了一个具有四个元素的类似过程:
-
分解
-
模式识别
-
抽象
-
算法设计
与设计思维过程一样,计算思维中的问题并不清晰。这些问题有时被称为不明确的。我们被呈现了一系列情况,我们在开始构思或根据我们可以看到的模式创建可能的解决方案之前,我们定义或分解该问题。当我们考虑计算思维过程时,我们真正做的是尝试弄清楚如何让计算机按照一系列步骤解决我们所面临的问题。
让我们来看一个简单的计算思维问题。
问题 1 - 条件
让我们想象一下,广播电台的抽奖活动让参赛者选择两种可能的获奖方式:250 美元现金或以 25 美分的高度。
计算思维问题可能像问题 1那样模糊,甚至没有提出问题。你被给定了一系列条件,你的工作是确定问题是什么,并为你自己定义的问题找到解决方案。如果你仔细想想,这个问题没有完美的答案,但你可以创造条件来确定哪个选项确实是最好的,这取决于参赛者的身高。
为了分解这个问题,我们需要看看陈述了什么,并考虑没有陈述的内容。我们需要规则。
简而言之,赢家将选择货币支付:要么是 250 美元现金,要么是相当于他们身高的 25 美分。这些都是陈述的。但未陈述的内容也很重要:
-
抽奖的时间表是什么? 有多少个赢家?
-
我们是否想要在每个参赛者选择后跟踪我们花了多少?
-
我们是否想要使用一个比较基准?
可能会想到其他事情,但现在让我们坚持这些问题。我们将假设抽奖没有固定的开始或结束日期,电台可能会在某一天选择多个赢家,也可能根本不选择。这些是我们在找出模式、概括它们并设计算法时要考虑的一些因素。
关于支付的所有信息,我们仍然没有办法弄清楚何时支付更多。选择 250 美元最好吗? 还是选择以 25 美分的高度最好? 我们能否创建一个算法,告诉我们哪个选项更好? 是的,我们可以创建一个解决整个问题的算法。
这个问题的模式总是相同的:现金价值和 25 美分的高度是固定的,所以我们总是可以使用数学来计算根据某人的身高,25 美分的高度转换成多少钱。
如果我们知道一些东西,我们可以清楚地陈述基于每个选择的赢得。这包括现金选择或以 25 美分的高度选择。如果选择以 25 美分的高度,我们需要以下内容:
-
参赛者的身高
-
一美分的厚度
接下来发生的事情是模式和抽象的一部分。我们不知道每个参赛者的选择,但我们可以提前找出每个四分之一的厚度。这将在我们的算法中稍后需要。每个四分之一大约是 0.069 英寸,或 1.75 毫米厚。
重要提示:
在本书中,我们将使用惯例测量,例如英尺和英寸。然而,我们也会提供公制测量的算法。
观察我们的问题,我们可以用两种方式陈述赢得的方式。以下表达包括了以四分之一赢得的高度的数学算法。它们展示了确定参赛者身高后确定总赢得所需的步骤。
请注意,为了使用惯例算法,身高需要以惯例单位给出。为了使用公制算法,身高需要以公制单位给出。如果参赛者选择现金,那么总赢得就是$250。如果参赛者选择以四分之一身高,那么惯例单位和公制单位的算法如下:
我喜欢不高风险的赌博。所以,我要说我想测试一下,用我的身高。所以我选择找出我的身高是多少个四分之一。我身高 5’10"。让我们算一下这是多少英寸。因为一英尺有 12 英寸,总身高的算法如下所示:
但我说我是 5’10",所以我们需要加上那 10 英寸:
现在,让我们使用我们之前定义的数学算法,https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_02_005.png,来找出我能赢得多少:
我使用了https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_02_007.png符号而不是=,因为https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_02_007.png表示这是一个近似值。由于我四舍五入了,我想确保我显示的是最佳近似值,而不是精确数字。
也许你现在已经解决了这个问题,但在计算思维中,我们必须经过抽象并设计一个适用于所有情况的算法。我们可以创建一个非常简单的程序,从用户那里获得简单的输入,或者我们可以创建一个更复杂的程序,不仅提供基本的总数,还可能提供总和、图表或我们发现与我们的情景相关的其他任何内容,并适用于所有情况。
一旦我们更深入地了解了计算思维过程的每个部分,我们将更多地设计这些算法。我们甚至会回到这个问题,展示如何为计算机创建运行的算法。我们可以创建一个算法,让我们使用某人的身高来决定使用哪种赢得。
或者,如前所述,我们可以写一个以$250 作为每个参赛者的赢得基准的基线,然后输入每个参赛者选择的内容,以便查看我们是否低于或高于$250 的基准。我们可以汇总这些结果,也就是继续将它们相加,以查看当电台停止抽奖时我们最终会得到什么结果。如果参赛者在电台进行抽奖的时间越长选择不同,我们甚至可以有一个图表显示我们的情况,等等。
在本节中,我们学习了计算思维的要素。我们还研究了一个计算思维问题。
然而,现在让我们继续深入研究计算思维的要素——即分解、模式识别、模式概括和抽象以及算法设计。
分解问题
分解是分解数据的过程。它可以包括解决问题所需的一系列过程或步骤。通过分解问题,我们可以在概括模式之前识别问题的组成部分或较小部分。
通过分解,我们可以识别和解决一个案例,然后将这些步骤概括到问题的所有可能实例中。为了真正理解分解,我们需要回到我们之前提出的问题,简单地说,就是问:*如果我用硬币换取我的身高,会得到更多的钱吗,还是应该选择 250 美元?*我们可以说我们想知道一个实例,并且数学上解决这个问题一次,比如只解决我的身高的问题。然而,我们可能需要其他实例的信息。我们可以创建一个程序,只是确定哪个选项,250 美元还是你的身高的硬币,会更好。或者我们可以考虑以下一些情况,这将意味着不同的算法:
-
我们可以检查给定身高的选项,但也将每个项目添加到列表中,以便跟踪所有决策。
-
我们还可能需要数组和该列表中元素的总和来跟踪比赛中的支出。
-
我们还可以将总和与基线进行比较,以 250 美元作为每个个体的基准。
-
我们还可以使用所有的元素,比如列表、总和、比较和可视化图形显示来更好地理解和跟踪结果。
正如你所看到的,算法将取决于我们确切想要跟踪或回答这个问题。我们的算法可以是一个简单的是或否类型的问题,我们只需检查哪个选项最好,或者它可以是一个更健壮的算法,带有数据和数据跟踪的可视化表示。现在让我们来看看我们如何在问题中寻找模式。
识别模式
模式识别是在我们分解问题后找到相似之处或模式的过程。在问题 1中,我们展示了一个问题,其中参赛者可以赢得 250 美元,或选择用他们的身高来换取硬币。对于每个参赛者来说都是一样的。唯一的区别是总价值取决于个人的身高。
在本节中,让我们看一个不同的问题,以更好地理解模式识别。
问题 2 - 数学算法和概括
想象一下,你正在为一个足球队准备聚会。这是一个社区球队,所以总是有 12 到 15 个孩子过来。你想要订购你需要的食物。你知道你将从你将使用的餐饮公司那里支付 12 美元每个孩子。现在,让我们分解这个问题:
-
分解:我知道我们有 12 到 15 个孩子。我们也知道每个孩子的费用是 12 美元。我们的问题可以被看作是一个问题:我们如何估算费用?
-
模式识别:你知道孩子的数量k在 12 到 15 之间。你知道每个孩子的费用是 12 美元。例如,如果我有 5 个孩子,费用将是https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_02_009.png
-
模式概括:孩子的数量是未知的,所以我们将使用变量k表示未知值。这样,无论我们有多少孩子,我们都可以找出总费用。我们正在从一个案例,5 个孩子,概括到所有案例,k个孩子。
-
算法设计:我们现在将编写数学算法。我们将在第三章中更多地使用可编程算法,理解算法和算法思维。我们也将在那里重新讨论这些数学算法。总费用将由方程https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_02_010.png给出,其中T是总费用,k是孩子的数量。
正如您从前面的问题中所看到的,模式识别对于找到一个概括的模式并编写我们的算法是很重要的。现在,让我们更仔细地看一下模式概括。
概括模式
一旦我们认识到我们的模式,我们需要进行模式概括和抽象。也就是说,我们希望确保我们提出的解决方案可以用于我们已经确定的问题的多个实例。模式概括可以是一些简单的东西,比如编写一个基本的线性数学算法,就像我们为派对的成本所做的那样,其中每个孩子的成本是 12 美元。因此,任何数量k的孩子的成本将由 12k给出。但模式概括可能远不止于此。
如果我们回到问题 1,在那里你可以选择 250 美元,或者你可以选择用 25 美分的硬币来换算你的身高,我们的模式概括将允许我们检查任何人的身高与 250 美元的对比,以确定选择现金选项还是选择硬币哪个能让你得到更多的钱。
抽象让我们专注于我们需要的东西,并丢弃我们不需要的东西,以便为我们的问题创建最佳算法。现在,根据我们决定需要什么,我们可以添加或删除一些条件。
例如,如果我是一个参赛者,我只想知道哪个选项给了我更多的钱。我不关心总胜利,谁选择了 250 美元,谁选择了身高等等。但如果我是广播电台,我可能想知道总和,与基准的比较,以及更多。我将不得不选择基准,甚至可能以图形方式显示随时间发生了什么。这都是抽象过程的一部分。当您解决计算思维问题时,您还在确定对您的解决方案和算法重要和不重要的事情。
在这个问题的最简单形式中,如果你是一个参赛者,你想知道你赢得奖金的最佳可能情况是什么。如果你选择了 250 美元,但你的身高使得你的身高以 25 美分的硬币来换算的金额超过了 250 美元,你会想知道。如果你在广播电台工作,你可能想追踪不仅仅是每个获胜者的情况。抽象允许您适应所有情况,从解决一个数学问题到创建一个可以跟踪所有参赛者的所有选择的算法。现在让我们看看如何创建这些算法。
设计算法
正如本章中先前提到的,算法是一组指令。当我们编写计算机程序时,算法是一组指令,提供给计算机,以便它可以为所提出的问题提供解决方案。到目前为止,我们一直坚持数学算法,只是因为我们还没有深入研究 Python。然而,我们现在将一些算法转化为 Python 编程。
首先,让我们来看一下问题 1。在这里,我们有一个情况,你可以赢得 250 美元,或者你的身高以 25 美分的硬币来换算。假设是你在竞争,你会想知道哪个选项能让你赢得最多的奖金。
让我们再次看一下本章前面的数学算法:
-
总获胜金额(习惯单位):https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_02_011.png
-
总获胜金额(度量单位):https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/Formula_B15413_02_012.png
请记住,如果您使用的是习惯单位的身高,您将使用第一个算法。如果您使用的是公制单位,您将需要相应地调整程序。
当我们编程时,我们需要定义我们的变量。在这种情况下,h是我们用于身高的变量。但想想看;如果你是一个成年人,你的身高可能不会改变,但为了论证的目的,我们将假设它不会总是相同。因此,我们需要想知道哪个选项更好,250 美元还是用 25 美分的硬币来换算他们的身高的人输入他们的身高,以便程序会为他们提供答案。
输入是用户可以输入的内容。因此,当我们定义变量时,我们将要求输入。在 Python 和任何其他语言中,一个好的做法不仅仅是要求输入而没有任何指导。也就是说,我们希望告诉用户他们正在回答的问题。例如,我可以编写以下代码来要求用户输入他们的身高:
h = input("Enter your height in inches: ")
上述代码将要求用户输入一些内容。它还要求用户以英寸输入信息。如果您使用的是公制单位,您将相应说明。
我们还将信息保存为变量h。但我们还没有对该变量做任何操作。
我们可以进行基本的数学运算,并根据身高打印出我们得到的值:
h = input("Enter your height in inches: ")
total = (int(h)/0.069)*0.25
print(total)
请注意,在上面的片段中,我们在total变量的定义中使用了int(h)。我们将h值转换为整数,以便可以使用该变量进行数学运算。当我们要求输入时,变量被保存为字符串,这就是为什么我们需要转换它以便使用它。
使用我的身高运行上述代码,即 70 英寸,得到以下结果:
253.62318840579707
如果我们将打印代码调整如下所示,我们的答案将是253.62,看起来会好得多:
h=input("Enter your height in inches: ")
total = (int(h)/0.069)*0.25
print(round(total,2))
当我运行这个程序时,窗口看起来是这样的:
图 2.1 – Python 程序输出
但有时我们希望代码做更多的事情。让我们删除那个print命令并创建一些条件。在接下来的几行中,我们将使用提供的值进行一些比较。例如,我们可以要求计算机为我们检查一些事情。有三种可能性:
-
我们的身高可能会产生与 250 美元完全相同的结果。
-
我们的身高可能会产生少于 250 美元的结果。
-
我们的身高可能会产生超过 250 美元的结果。
现在,我将要求计算机根据这些条件告诉我该做什么。我们将需要一个if-elif,else语句。这些是我们将测试的条件,以便获得更好的输出。我们将测试总额是否与 250 美元相同。否则,如果总额少于 250 美元,我们将希望计算机执行某些操作(这是我们的elif语句)。最后,在所有其他情况下,我们将使用else命令:
h=input("Enter your height in inches: ")
total = (int(h)/0.069)*0.25
total = round(total,2)
if total == 250:
print("Your height in quarters is the same as $250.")
elif total > 250:
total = str(total)
print("Your height in quarters is more than $250\. It is $" + total)
else:
print("You're short, so choose the $250.")
让我们看看一些测试案例是什么样子的。
首先,让我们使用69英寸的身高:
图 2.2 – 情况 1:身高产生 250 美元
因此,任何身高为 5’9"的人无论做出哪种选择都不会错,因为他们最终都会赢得 250 美元。
现在,让我们看看我的身高,70英寸:
图 2.3 – 情况 2:身高产生的金额超过 250 美元
最后,让我们看一下小于 69 英寸的身高,所以让我们使用55英寸:
图 2.4 – 情况 3:身高产生的金额少于 250 美元
请注意,我们可以调整代码以表达我们想要的内容。我选择使用完整的句子来阐明结果,但您可以根据需要调整代码以满足您的需求或偏好。此时,一些代码可能具有挑战性,但我们将在第八章中深入学习 Python 编程语言,Python 简介。
正如您所看到的,我们有三种算法,它们为我们提供了相同类型的信息。其中一种比其他两种更加健壮,但我们的算法是复杂还是简单取决于我们从中需要什么。如果您稍后再次举行这次抽奖,您可能会忘记算法是什么,您是如何编写的,或者一切意味着什么。然而,通过最后的代码,您只需运行它就可以获得大量信息,这比前两种更有帮助。您还可以将所有这些信息作为注释添加到代码中,但我们将在第八章中讨论这些内容,Python 简介。
还要记住,我们运行这个程序就好像我们是参赛者一样。虽然这很有帮助,但您可能要考虑如果您是广播电台会做出什么改变。您可以编写一些代码来保存所有运行的实例,以便随后检查和添加所有的奖金。您甚至可以通过代码计算出总和。由于该代码稍微复杂,我们将在整本书中更详细地讨论它,特别是在第八章中,Python 简介。
现在,让我们看一些更多的问题和相应的算法,以便更加熟悉计算思维过程。
其他问题
在本节中,我们将继续研究其他问题。对于问题 2,我们将直接进入算法,因为我们在本章的早些时候已经完成了其他步骤。下一个问题也将包括整个计算思维过程。
问题 2 - 儿童足球派对
在本章的早些时候,我们正在为一个足球队筹办派对,每个孩子的费用是 12 美元。我们说孩子的数量是未知的,所以我们将使用变量k来表示未知数量。我们还说我们有一个数学算法,T = 12k,它给出了k个孩子的总费用 T。让我们在这里添加一个条件。如果我们的预算是 200 美元,我们想知道我们是超支、不足还是正好符合预算。
我们也可以使用 Python 为这种情况编写算法:
k = int(input("How many children are coming to the party? "))
T = 12 * k
if T == 200:
print("You are right on budget, at " + str(T))
elif T <= 200:
print("You are under budget, at " + str(T))
else:
print("You are over budget, at " + str(T))
让我们测试一些情况,以验证我们的代码是否有效:
图 2.5–案例 1:有 12 个孩子参加派对
*太好了!如果有 12 个孩子参加,我们就不会超支。那么如果有 20 个孩子参加呢?*让我们看一下:
图 2.6–案例 2:有 20 个孩子参加派对
我们没有足够的钱供 20 个孩子!
正如您所看到的,程序为我们提供了有关总数以及我们是否超支或不足的一些信息。与任何算法一样,这并不是我们获取此信息的唯一方式。尝试使用不同的算法来解决这个简单的问题,或者添加一些自己的条件并编写代码。练习和添加条件将使您更加熟悉设计和编写算法。
问题 3 - 储蓄和利息
现在我们有一个新问题。一家银行以每月x%的复利率支付复利。如果您存入任意金额,经过多年后将会支付多少?
让我们分解一下这个问题。首先,我们知道利息是按月复利的。让我们谈谈复利。投资的利息是在一段时间内支付的百分比。复利意味着利息每次都是在初始金额加上利息支付的。复利是一种模式。实际上,它有一个公式。
我不知道的是银行支付的百分比、存款金额或存款年限。因此,我们需要编写一个程序来处理所有可能性。这就是模式概括。我们知道的是利息是按月复利的。实际上,这有一个数学公式:
让我们谈一下上述方程中的术语:
-
A是总金额。
-
P是本金,即初始存款。
-
r是利率(请记住,对于 3%,利率写作 0.03,例如)。
-
n是每年复利的次数。
-
t是存款未动的年数。
因为有一个数学算法,我们现在可以使用公式为此创建一个程序。但是,我们需要确保运行程序的人知道我们要求关于所有输入的内容。我们需要询问一些事情:
-
存入多少金额?
-
银行支付的利率是多少?
-
存款多少年?
我们知道公式中的n。这里n等于 12,因为这是按月复利。这意味着每年会复利 12 次。所以,n = 12。
现在是时候编写这个程序了:
图 2.7 - 复利样本程序
上面的截图显示了复利的 Python 程序。请注意注释前面有#符号。它说明我们需要将利率转换为公式中使用的形式。否则,我们将得到一个不正确的总额。此外,我们在这里使用了浮点数,因为我们需要使用小数。整数或int不会给我们所需的信息。此外,我们将总额四舍五入到两位小数。这是因为我们在谈论金钱时使用两位小数。图 2.7中显示的算法文本如下:
P = float(input("How much are you planning on depositing? "))
r = float(input("At what monthly compound rate will it be paid out? "))
t = float(input("How many years will the money be deposited? "))
#Convert the rate to a decimal for the formula by dividing by 100
r = r/100
A = P * (1 + r/12)**(12*t)
A = round(A, 2)
print("Total after " + str(t) + " years: ")
print(A)
使用复利算法中的代码,如果我们有初始金额、利率和存款年限,我们可以运行任何可能的复利实例。给定初始存款$1,000,利率 4.5%,存款 10 年的程序输出如下:
图 2.8 - 复利输出示例
正如您所看到的,按月复利存入$1,000,10 年后的总额为$1,566.99。
让我们再次测试程序。这一次,我们将以 3.5%的利率按月复利存入5,000美元,存款期为20年:
图 2.9 - 复利输出示例
20 年后我们的总额将是$10,058.51。这意味着我们的钱翻了一番!
有了这个计算器程序,我们只能计算按月复利的利息。我们可以创建一个新程序来计算以任何利率复利的利息:每月、每年、每两个月等等。您可以尝试使用代码来创建自己的计算器。如果您想知道投资或存款在未来会有什么预期,这些都是很有帮助的。例如,您可以确定需要多长时间才能将存款达到一定金额。比如,您想要为孩子的大学教育存入 50,000 美元。在这种情况下,您可以计算出在 18 年内需要存入多少金额,到那时他们很可能已经准备好上大学了。
总结
在这一章中,我们已经经历了计算思维的过程。我们了解了计算思维的四个主要元素:分解、模式识别、模式概括和算法设计。我们还了解到,在计算思维中的问题并不总是清晰定义的。我们需要解释和分解信息,以便找到模式。一旦我们找到模式并定义了我们想要概括的内容,我们就可以设计算法来解决问题。
我们还了解到,算法和解决方案并不是唯一的。对于我们遇到的每个问题,都有多种编写算法的方式。计算思维过程使我们能够以多种方式探索问题,并创建符合我们自己解释和需求的解决方案。
在下一章中,我们将更深入地学习算法和算法思维,因为它们是所有计算思维问题的产物。为了更好地理解算法,我们将研究布尔运算符,并学习如何识别和解决逻辑处理中的错误。我们将为所提出的问题编写算法,并分析给定算法中可能存在的处理错误。
第三章:理解算法和算法思维
在本章中,我们将更加专注于理解算法和算法思维。虽然这是计算思维过程中的最后一步,但我们必须理解算法思维如何帮助我们更好地规划和理解问题。也就是说,我们练习算法设计和算法思维的次数越多,就越容易理解、分解和识别问题呈现给我们时的模式。
在本章中,我们将涵盖以下主题:
-
深入定义算法
-
设计算法
-
分析算法
阅读完本章后,你会更好地理解算法。因此,我们将重新分析算法的定义,这是我们之前在第二章中涵盖的,计算思维的要素,以及如何设计数学和计算算法。
技术要求
你需要最新版本的 Python 来运行本章中的代码。你可以在这里找到本章使用的完整源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter03
深入定义算法
正如我们在第二章中提到的,计算思维的要素,算法只是一组指令。我们在日常生活中使用指令,有时是有意识的,有时是无意识的。例如,想想你早上遵循的例行程序。闹钟响了。接下来你会做什么? 你会准备咖啡吗? 洗澡? 先刷牙吗?
大多数人每天早上都会按照相同的步骤。你可以说我们已经编程自己遵循这些步骤。现在想想一个你的日程变化,你的例行程序不同了的时候。我知道我不得不停下来重新调整很多次,因为我的“程序”不再起作用了。例如,我不能在早上 6 点起床赶 5 点的飞机。
计算机的算法类似,如果一组条件发生了变化,我们需要重新编写一组指令。程序只能执行我们已经规定的参数。大多数程序无法调整或适应任何未事先编码的新信息。也就是说,机器学习和人工学习正在发展。我们不是在谈论那些类型的程序,但即使在这些情况下,我们仍然需要调整这些程序以满足我们的需求。
为了设计算法,我们需要确保它们满足一些特定的特征:
-
它们是清晰和明确的。
-
它们有明确定义的输入。
-
它们有明确定义的输出。
-
它们是有限的。
-
它们是可行的。
-
它们与语言无关。
让我们逐个查看前面列表中的每个特征并对其进行定义。
算法应该是清晰和明确的
当每个步骤都能轻松理解、容易定义,并且输入和输出也清晰明确时,算法就是清晰和明确的。算法的每个组成部分应该只有一个意思。
算法应该有明确定义的输入和输出
算法的输入可以是用户提供的,这意味着程序的用户输入数据。输入也可以指程序内部定义的内容。这意味着我可以包含一个已经提供了固定值的变量。
例如,如果我需要用户告诉我他们购买的票数,我可以编写算法来询问输入。我也可以将该输入作为一个已定义的变量,并赋予一个给定的值。算法并不总是需要输入 - 零输入算法是存在的 - 但是当算法需要输入时,定义该输入是很重要的。一个输入的例子是在程序中要求用户的姓名。想想现代视频游戏。其中许多游戏会提示用户输入姓名,比如,“你好,旅行者。你叫什么名字?”
作为用户,当给出提示时,我会输入Sofia,这给了我以下结果:
"Hello Sofia. Welcome to the adventure!"
正如你所看到的,游戏将产生一个输出,并在该输出中使用我的名字。
这最后一行是程序的输出。我也可以用 Python 编写一个简单的程序来问这个问题:
ch3_nameprompt.py
name = input("Hello traveler. What is your name? ")
salutation = "Hello %s. Welcome to the adventure!" % name
print(salutation)
请注意,我们在这里使用了%s和%符号。这里的语法是我们所谓的 f-string。我们使用%s语法来让程序知道我们想要插入信息的位置,然后我们通过使用%符号来调用该信息。在这种情况下,我们将输入保存到name变量中,然后在salutation变量中调用它。
运行时,程序看起来是这样的:
Hello traveler. What is your name? Sofia
Hello Sofia. Welcome to the adventure!
这个简单的算法允许我们将名字保存为一个变量。该变量仅在这个简单代码的输出中使用了一次。然而,在游戏中,该名字变量可能会在多个实例中使用,比如在与游戏中的角色对话时等。
程序的输出是离开系统的信息,也就是你的程序的产物。根据一些信息或代码,输出是从程序中的指令产生的东西。
算法应该具有有限性
算法必须具有有限性。这意味着算法必须结束。让我们看一个算法不会结束的情况。**我不建议编写或运行这个!**尽管如此,让我们看看创建这个算法的步骤:
- 定义一个变量
i,并将其设置为等于0:
i = 0
- 增加值
1。我们可以以几种不同的方式来做到这一点:
i = i + 1
i += 1
前面两行代码都会将i的值增加1。
- *添加一个错误!*我们即将在有限性中创建一个错误。再次强调,我只是为了证明一个观点,但这是一个你要避免的错误:
i = 0
while i >= 0:
i += 1
print(i)
在这个算法中,我告诉程序继续将i增加1,只要它大于0,然后计算机应该打印这个值。这将永远继续下去,永远不会停止,因为条件将始终成立。因此,程序的输出将从 1 开始,但将继续打印序列中的下一个项目,如 2、3、4、5 等。程序根本没有结束的方法。
现在,可以根据几种不同的条件来完成类似的程序。假设我们想打印所有加法的值,但只要i小于 15:
ch3_finiteness.py
i = 0
while i < 15:
i += 1
print(i)
前面的程序是一个终止程序。现在它只适用于i小于15的所有值(不包括 15)。我们将得到如下所示的输出:
图 3.1 - 有限性代码的输出
我知道我说过这个程序不包括 15。确实不包括。因为这发生在i小于 15 的情况下,它将评估的最后一个值是 14。然而,它说当值小于 15 时,我们将其增加 1(i += 1)。所以,当i为 14 时,打印的值是 14 + 1,即 15。有限性允许程序终止。
算法必须是可行的
算法还必须是可行的。为了可行,算法需要使用可用的内容和资源。在编写算法时,我们有约束或条件可以写入步骤中。如果没有办法满足所有约束,那么算法就不可行。考虑以下给出的两个条件:
-
现在是下午 3 点。
-
现在是下午 5 点。
如果我们对一个变量设置这两个约束,比如,它不可能。它不能同时是下午 3 点和下午 5 点。这就是我们所说的不可行。虽然算法可以继续,但我们通过同时使这两个事情成为真实的问题。有些约束永远不会被满足,因此算法被认为是不可行的。算法必须有一种方法来满足所有约束才能被认为是可行的。此外,如果算法是依赖未来技术编写的,也被认为是不可行的。
算法是与语言无关的
最后,算法必须是与语言无关的。算法中的一组指令应该尽可能简单地编写。一个好的算法将是这样的,它可以很容易地用任何语言编写,并产生相同的输出。
在这一部分,我们学习了算法以及设计算法所需的特征。牢记一个好算法的特征将使我们能够避免错误,并为我们所面临的任何问题创建可行的算法,现在让我们看看如何设计一些算法。
设计算法
设计算法时,顺序很重要。在使用编程语言时,存在重要的层次结构。这包括在使用 Python 时。把这看作是数学中的运算顺序。如果你还记得,我们使用 PEMDAS 来记住数学中的运算顺序。PEMDAS 代表括号、指数、乘法/除法和加法/减法。
我把乘法/除法写在一起是因为乘法和除法具有相同的权重。也就是说,乘法不一定需要在除法之前发生。如果我先进行除法,然后从左到右进行乘法,那么除法先发生。加法和减法也是如此。两者都没有更多的权重,所以我们按照从左到右的出现顺序执行它们。
让我们为一个问题编写一个数学算法。我们将看一个食物环境中的算法。是的,我知道我经常写关于食物和食物算法。我几乎和我喜欢代码一样喜欢食物。
问题 1 - 办公室午餐
办公室正在为员工订餐。员工可以选择三明治或沙拉两种午餐选项。每份三明治餐的成本为 8.50 美元,而每份沙拉餐的成本为 7.95 美元。
办公室午餐数学算法
选择每个选项的员工数量是未知的。让我们使用一些变量来帮助我们设计数学算法。让我们用 s 表示三明治的数量,用 b 表示沙拉碗的数量。我知道你在想什么,如果你过一段时间再回到这个问题,这两个变量并不是很有用。但我们马上就会谈到这个问题。现在,让我们写出我们的总成本 c 会是什么样子:
这是一个简单的数学问题,需要两个未知的变量输入 s 和 b,以获得我们的总成本 c。现在让我们看看同样的午餐场景的另一个版本。
办公室午餐 Python 算法
现在让我们在编写程序时考虑一些更多的考虑因素。在为这个问题设计 Python 算法时,我们需要考虑两个角度:程序员和用户。
有时我们既是程序员/开发人员,也是程序的最终用户,但很多时候,我们会为其他人编写或开发内容。重要的是我们要记住这些考虑因素,因为这可能会影响我们编写程序和定义变量的方式。此外,如果我们正在为公司编写程序,其他人可能需要在某个时候去编辑我们的程序。
这意味着我们需要以其他人能够理解的方式编写程序。我们的变量应该易于理解,因此使用简单的单个字母变量可能会使另一个程序员或用户更难理解。让我们看看问题 1的程序。请记住,在这个问题中,我们试图确定给员工提供两种可能选择的办公午餐的最终成本:
-
$8.50 三明治套餐
-
$7.95 沙拉套餐
让我们使用 Python 为这个问题创建程序。首先让我们澄清一些变量。我们将使用完整的单词或用*_*分隔的一系列单词来定义这些变量。在开始之前,您可能需要回想一下,对于Python 变量,需要遵循一些规则,以免引起错误:
-
变量必须以字母或下划线(_)开头。
-
变量只能包含字母,数字和下划线。
-
变量不能以数字开头。
-
变量是区分大小写的(alpha不是与Alpha或ALPHA相同的变量)。
对于问题 1,我们需要三个变量:
-
午餐的总成本
-
三明治午餐的数量
-
沙拉午餐的数量
现在我们需要给它们命名:
-
total_cost= 所有午餐的总成本 -
number_of_sandwiches= 订购的三明治套餐总数 -
number_of_salads= 订购的沙拉午餐的总数
这里重要的是这些变量易于阅读和理解。我应该注意到,我在编程时偏爱小写变量。我确实有一些例外情况,我喜欢使用大写字母,但您会看到许多只有小写字母和下划线的示例。很久以前,我发现即使在编写程序时大写字母对我来说是有意义的,但后来我会忘记哪些字母是大写的,这只是一个额外的头痛,如果我只是使用小写字母,就可以避免。
此外,一些程序员会消除下划线,并使用变量,例如numberofsandwiches或简单的sandwiches。当然,这两种都是可以接受的,而简单的sandwiches会使编写一些代码变得更容易。然而,这样做有利有弊。如果其他人正在查看程序,可读性将很重要。就像我说的,我偏爱清晰的小写变量和使用下划线,但每个程序员都可以自行选择。
现在我已经定义了我的变量,我可以开始编写我的程序了。我需要向用户询问什么? 我需要用户的输入,包括三明治和沙拉的数量。我想要作为输出的是午餐的总成本。在 Python 中向用户询问输入,我们需要使用input命令。然而,我们还需要记住,由于我们在使用float数字的算法中使用了这个数字(小数点是浮点字符),我们需要将提供的数字转换为integer或float。员工不会点半份沙拉,所以我们可以安全地将它们保存为整数,或int。作为提醒,在 Python 中,注释以#符号开头。在 IDLE 中编写代码,如下所示:
ch3_officelunch.py
#Ask the user for the number of sandwich meals ordered and save as variable.
number_of_sandwiches = int(input("How many sandwich lunches were ordered? "))
#Ask the user for the number of salad meals ordered and save as variable.
number_of_salads = int(input("How many salad lunches were ordered? "))
#Create total_cost variable and save the algorithm for total the new variables.
total_cost = 8.50 * number_of_sandwiches + 7.95 * number_of_salads
#Print the total cost. Don't forget to convert the total_cost to string.
print("The total cost for the employee lunch is $" + str(total_cost) + ".")
运行代码时,用户可以输入办公午餐的各种选项的数量。代码首先询问用户三明治的数量,如下所示:
How many sandwich lunches were ordered?
然后代码将要求输入沙拉午餐的数量,并提供总费用。以下示例输入 12 份三明治午餐和 23 份沙拉午餐,总费用为 284.85 美元:
How many sandwich lunches were ordered? 12
How many salad lunches were ordered? 23
The total cost for the employee lunch is $284.85.
现在让我们从不同的角度来看一个类似的问题。
问题 2 - 一个餐饮公司
假设你开始了一个简单的餐饮公司。你开始只销售两种选择,一份 8.50 美元的三明治套餐和一份 7.95 美元的沙拉套餐。你可以创建一个使用 Python 字典存储这些选项的程序。
你可以在第八章中找到有关 Python 编程语言和字典的更多信息,Python 简介,但我们也会在这里定义一个 Python 字典。当我们想要无序、可更改且有索引的项目时,我们使用字典。以下是 Python 中我们餐饮公司的一个字典的示例:
ch3_cateringdict.py
catering_menu = {
"sandwiches": 8.50,
"salads": 7.95
}
print(catering_menu)
现在,字典是常见的,而且非常有用,原因有很多:主要是它们易于阅读,并且它们提供了根据需要更改数据的方法。
当打印时,字典代码看起来像这样:
{'salads': 7.95, 'sandwiches': 8.5}
现在你有了一个字典,让我们谈谈它对你的餐饮公司的用处。假设你的沙拉配料成本增加了,你想通过改变沙拉的价格来进行核算。你可以通过几种不同的方式来做到这一点。你可以在原始程序中进行更改,因为它非常简短,或者你可以告诉程序你想要根据键值来进行更改。这很重要,因为现在你可能有两种出售的物品,但是当你的菜单选项变得更加广泛时会发生什么? 每次更改价格时,你是否想要搜索每个项目? Python 使得识别你想要更改的内容然后进行更改变得很容易。
为此,你可以使用以下代码:
catering_menu["salads"] = 9.50
你的新 Python 代码看起来像这样:
ch3_cateringdict2.py
catering_menu = {
"sandwiches": 8.50,
"salads": 7.95
}
catering_menu["salads"] = 9.50
print(catering_menu)
当打印时,沙拉的新价值将被显示:
{'salads': 9.5, 'sandwiches': 8.5}
但是,如果你想添加一个菜单项目会发生什么? 比如说你想添加一个售价为 3.75 美元的汤选项。在这种情况下,你可以通过一行简单的代码将菜单选项添加到你的字典中,如下所示:
catering_menu["soup"] = 3.75
当你把所有东西放在一起时,初始代码和更改将看起来像下面的代码块。请注意,你有初始字典,然后是下面的两个更改。当你打印字典时,它将包括所有更改以及添加汤选项:
ch3_cateringdict3.py
catering_menu = {
"sandwiches": 8.50,
"salads": 7.95
}
catering_menu["salads"] = 9.50
catering_menu["soup"] = 3.75
print(catering_menu)
现在你已经添加了soup项目,你可以打印你的字典来查看你的完整菜单:
{'soup': 3.75, 'salads': 9.5, 'sandwiches': 8.5}
我们可以使用字典中的信息来创建更健壮的程序,比如在线菜单、点餐菜单选项等等。在本节中,我们学习了如何设计算法,并解决了两个问题。
我们将在本书的后续章节中更多地使用 Python 进行开发,特别是在第三部分,使用计算思维和 Python 进行数据处理、分析和应用。现在,让我们继续分析一些算法。
分析算法
正如本章前面提到的,当我们设计算法时,它们应该具备以下特点:
-
它们是清晰和明确的。
-
它们有明确定义的输入。
-
它们有明确定义的输出。
-
它们具有有限性。
-
它们是可行的。
-
它们是与语言无关的。
除了这些特点之外,当我们看算法并分析它们时,我们希望确保自己问一些问题:
-
算法是否做我们想要的?
-
输出是否合理?
-
是否有其他更清晰地获取相同信息的方法?
在分析算法时,我们可以问自己许多更多的问题,但现在让我们看一些算法解决方案,并根据上述特点和问题进行分析。
算法分析 1 - 州和首府
一个学生创建了一个算法,其中包括美国各州和每个州的首都的列表,但只包括她已经学过的州。她的算法如下所示:
ch3_statecapitals1.py
Ohio = "Columbus"
Alabama = "Montgomery"
Arkansas = "Little Rock"
print(Ohio)
这个程序很简单,但使用起来并不容易,也不在运行时提供帮助。*它包含所需的信息吗?*是的。*我们可以以不同的方式组织它,以便以其他方式调用信息吗?*可以。
把州和首都看作键对。我们可以使用字典来存储这些信息。你可能还记得本章前面提到过,字典可以很容易地进行调整和适应,只需一行简单的代码就可以添加一个新的键。让我们先把前面代码中的信息转换成字典:
ch3_statecapitals2.py
state_capitals = {
"Ohio" : "Columbus",
"Alabama" : "Montgomery",
"Arkansas" : "Little Rock"
}
print(state_capitals["Ohio"])
注意,现在我们可以通过简单地给出州名来访问州首都的信息。这段代码的输出只是哥伦布。*但是如果你只想运行程序并要求用户输入他们选择的州呢?*我们也可以用现有的字典在一行代码中编写。看一下以下代码:
ch3_statecapitals3.py
state_capitals = {
"Ohio" : "Columbus",
"Alabama" : "Montgomery",
"Arkansas" : "Little Rock"
}
state = input("What state's capital are you looking for today? ")
capital = state_capitals[state]
print("The capital of " + state + " is " + capital + ".")
在这段代码中,用户输入他们想要查找首都的州。这很有帮助,因为你可以每次运行代码而不必进入代码中去更改要打印的代码行,这是我们在ch3_statecapitals2.py文件中的算法中不得不做的。运行时,代码看起来像这样:
What state's capital are you looking for today? Alabama
The capital of Alabama is Montgomery.
现在让我们看一下首先需要这个算法的原因。学生想要继续向程序中添加州。由于这个程序是基于字典的,她只需在需要添加另一个州时添加一行代码。例如,如果她想要添加爱荷华州,其首都是得梅因,她需要使用以下代码:
state_capitals["Iowa"] = "Des Moines"
看一下以下代码块。注意代码在程序中的放置位置。很重要的是我们把新代码放在新变量之前,否则,如果你尝试运行程序并输入爱荷华州,代码将返回一个错误而不是提供爱荷华州的首都。
在算法中,逻辑非常重要。我们不能在已经使用过的变量中使用我们尚未定义的值。也就是说,如果在为爱荷华州标识新值之前就使用了州和首都变量,那么当输入爱荷华州时,代码会以错误结束。然而,如果我们在运行这两个变量之前添加键对值,代码就会如预期般运行:
ch3_statecapitals4.py
state_capitals = {
"Ohio" : "Columbus",
"Alabama" : "Montgomery",
"Arkansas" : "Little Rock"
}
state_capitals["Iowa"] = "Des Moines"
state = input("What state's capital are you looking for today? ")
capital = state_capitals[state]
print("The capital of " + state + " is " + capital + ".")
正如你所看到的,我们可以调整和修改代码以更好地满足我们的需求。现在让我们看一下一些算法,确定它们是否会运行;也就是说,它们是否会产生错误或正确运行。
算法分析 2 - 终止还是不终止?
正如我们在本章前面讨论过的那样,算法应该是终止的。也就是说,它们必须有一种结束的方式,否则会导致许多错误。让我们看一个算法并分析它,确定它是否会终止:
x = 0
while x >= 3:
x += 1
print(x)
首先,让我们看一下x变量的值。x变量以0的值开始程序。while循环规定了x值将发生变化的条件,当x值大于3时,它会增加1的值。
这个算法终止,因为它将打印变量0的原始值。然而,这个算法实际上并不执行任何操作,因为条件永远不会满足。另外,请注意print命令没有缩进。如果缩进了,对于这个算法将不会给出任何输出,因为print命令永远不会被调用,因为变量永远不会满足while循环的条件。
现在让我们看一下以下算法:
j = 0
while j >= 0:
j -= 1
print(j)
在这种情况下,变量条件得到满足,因为j必须大于或等于 0 才能使程序运行。一旦条件得到满足,变量的值就会减少 1,因此print命令将产生一个输出为-1。代码不会再次运行,因为变量的值不再大于或等于 0。这个算法是终止的,产生了输出,并且是可行的。
最后,让我们来看一下以下具有改变条件的算法:
j = 0
while j <= 0:
j -= 1
print(j)
在这种情况下,算法是不终止的。因为我们将while循环改为小于或等于 0,这个算法现在将永远继续运行。
分析算法可能非常复杂。我们只是开始涉及算法的一些组成部分。随着我们在本书中深入研究其他计算思维问题,我们需要牢记良好算法的特性,以便有效地分析我们自己的代码。同样重要的是,我们继续考虑计算思维过程的元素:分解,模式识别,模式概括和算法设计。
当我们设计算法并测试它时,使用良好算法的特性将使我们能够观察错误,调整我们的算法以便使用,提供更好的输入和输出,并确保我们不会创建不可行和非终止的算法。
总结
在本章中,我们讨论了算法的定义,即一组步骤,允许计算机完成一个过程并提供一些输出。我们了解了算法的特性。
我们设计了基于问题场景的算法,然后分析算法,以确定它们是否满足正确运行所需的特性。理解算法的特性以及算法的工作原理将使我们能够创建比不了解这些特性时错误要少得多的算法。请注意,我说的是更少的错误。
在编写代码时,错误是生活中不可避免的事实。我们将不可避免地犯错误,我们将无意中引入错误或使一些代码无限。了解良好算法的特性使我们能够减少这些错误,即使我们无法完全将它们从我们的日常生活中消除。
在下一章中,我们将学习更多关于逻辑推理的知识。在整个章节中,我们将讨论逻辑的定义,学习归纳和演绎推理,增加我们对运算符和布尔逻辑的了解,并更多地了解逻辑错误。我们将使用计算思维的元素和算法的特性来进一步增进我们对逻辑推理的了解。
第四章:理解逻辑推理
在本章中,我们将探讨条件语句、算法推理和布尔逻辑等逻辑推理过程。在深入研究一些逻辑运算符之前,我们将探讨归纳和演绎推理。我们还将学习逻辑错误,如何识别它们以及如何纠正它们。
此外,我们将研究使用逻辑来编写算法以解决计算思维问题。
在本章中,我们将涵盖以下主题:
-
理解逻辑推理的重要性
-
使用布尔逻辑和运算符
-
识别逻辑错误
在解决计算思维问题时,逻辑推理是必要的。我们都知道编程代码有按顺序执行的步骤。想象一下我们有 10 行代码。如果我们不应用逻辑推理,代码会一行一行地读取——读取第一行,读取第二行,依此类推,直到最后一行。使用逻辑推理允许我们在继续之前比较事物,返回到以前的代码行,等等。在本章中,我们将学习逻辑推理,以便使用逻辑运算符以高效的方式解决问题并创建算法。
为了理解逻辑推理,我们将首先概括逻辑的一般定义,然后讨论在设计和编写算法时如何使用逻辑。
技术要求
您需要最新版本的 Python 来运行本章中的代码。您可以在此处找到本章使用的完整源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter04
理解逻辑推理的重要性
当我们通过第三章 理解算法和算法思维时,我们学会了如何在解决计算思维问题时设计一些基本算法。
在本节中,我们将了解逻辑推理的重要性以及如何通过示例应用逻辑推理的类型。
在编写算法时,我们需要使用逻辑推理来创建这些算法。
简而言之,逻辑推理是达成结论所遵循的一系列步骤。在计算思维中,当我们设计算法时,我们遵循的系统步骤是算法的一部分。计算机读取这些算法的方式取决于我们编写算法的方式。逻辑推理论证有两种类型,如下所示:
-
归纳推理
-
演绎推理
在更深入地定义这些之前,让我们看看为什么逻辑推理如此重要,以及在创建算法时为什么顺序很重要。
为了分析问题并提供帮助我们解决问题的算法,我们首先需要了解什么是逻辑推理。对于一些人来说,逻辑可能令人望而生畏,但我们每天都在下意识地使用它。
让我们看一个简单的例子。假设您每天早上洗澡然后去上班。*那么,您会在洗澡之前穿上工作服吗?*不会,因为那完全没有意义。从逻辑上讲,您必须先洗澡,然后再穿上工作服。现在,我在这里跳过了很多步骤,但这些步骤是逻辑步骤。其他逻辑的例子包括按照食谱制作食物,下雨时使用伞(或不使用),等等。
在本章中,我们将交替讨论逻辑推理和使用逻辑运算符设计算法。逻辑运算符允许程序做出决策。我们在日常生活中也会使用它们,只是没有意识到而已。例如,如果天气晴朗而温暖,我们可能想去骑自行车,但如果天气晴朗但寒冷就不想去。这里的and是一个逻辑运算符。
在做决定时,我们考虑了很多因素。在计算思维中,特别是在算法设计中,我们需要考虑这些因素,并为程序提供一种测试这些条件的方法。我们将在本章后面更深入地探讨逻辑运算符。现在,让我们更仔细地看看逻辑推理的类型以及如何应用它们。
应用归纳推理
当我们谈论归纳推理时,我们实际上是在向后工作。归纳推理从一个可能为真或假的结论开始,然后向后使用现有数据创建代码。让我们先看一个简单的问题。
解决归纳推理样本问题
我们有 150 美元的预算用于购买美术用铅笔和橡皮擦。美术用铅笔每支 1.75 美元,橡皮擦每个 1.50 美元。
记住,在计算思维中,我们首先分解问题,然后识别模式,然后概括该模式,然后创建算法。因此,让我们认识到这种模式。
让我们看看到目前为止我们知道的内容并命名一些变量:
-
总预算是 150 美元。
-
铅笔的成本是每支 1.75 美元。
-
橡皮擦的成本是每个 1.50 美元。
-
现在让我们用p表示铅笔的数量。
-
让我们用n表示橡皮擦的数量。
请记住,当我们到达算法时,我们可能想要重新命名这些变量。但现在,因为我们首先要看数学算法,所以我们会保留简单的变量。
我们可以用一个不等式来表示。*为什么是不等式而不是方程?*因为我们的总额可能不会正好是 150 美元。但它不能超过 150 美元,因为那是我们所有的钱。
因为这是一个简单的问题,我们一举识别和概括了这个模式。
因此,铅笔的数量乘以成本加上橡皮擦的数量乘以成本小于或等于 150 美元:
现在让我们谈谈算法设计。也许这是我定期购买的东西,因为我开美术课。我会根据这种情况进行。也许我的雇主最多给我 150 美元,但根据以前使用的情况,我可能需要更多的铅笔或橡皮擦,反之亦然。因此,我需要一个可以在每学期开始时使用和重复使用的程序。*这是我的问题的一部分吗?*不,这是一个定义不清晰的问题。因此,我正在根据一组特定需求调整问题。简而言之,我正在定义我想要解决的问题。
重要提示:
作为归纳和演绎推理困境的一则旁注,重要的是要理解,条件语句(例如我们在编程中经常使用的if/then语句)通常与演绎推理相关联。我们可以就它们是否可以是归纳的进行辩论,但事实是,归纳推理问题将使用演绎推理。我们将在本章的下一节深入研究演绎推理陈述。
因此,我希望程序问我想要多少支铅笔,或者问我想要多少个橡皮擦。这完全取决于情况!让我们看看程序应该为我们做些什么。以下步骤向我们展示了这一点:
-
询问您的输入是铅笔还是橡皮擦。
-
根据提供的输入选择要使用的不等式。
-
确定可能的铅笔或橡皮擦的数量(根据输入)。
-
给出铅笔和橡皮擦的总成本。
请注意,像往常一样,有很多种方法可以在 Python 中得出相同的答案。虽然其中一些程序比我通常呈现的要长,但由于我们正在学习计算思维和 Python 编程,展示易于理解的步骤非常重要。
对于这个特定的程序,我们需要导入数学函数,以便我们可以向下取整。*为什么我们需要这样做?*嗯,我们不能购买部分橡皮擦和铅笔,只能购买整支铅笔和整个橡皮擦。因此,如果程序说我们可以购买 19.5 支铅笔,那实际上意味着我们只能购买 19 支铅笔。
math.floor() 函数允许我们使用简单的函数将该数字向下舍入到 19。在本书中,我们将更多地探讨 math 函数。在我们离开这个快速主题之前,你应该知道 Python 中的 math 模块具有与 C 语言函数相匹配的内置函数。
让我们回到问题。看一下下面的编程:
ch4_inductiveP1.py
#We need the math module, so don't forget to import it.
import math
#Ask the user if they will input pencils or erasers first.
item1 = input("Will you be entering pencils or erasers? ")
if item1 == "pencils":
pencils = int(input("How many pencils will you purchase? "))
if pencils * 1.75 < 150:
pencilstotal = pencils * 1.75
total = 150 - pencilstotal
total = total / 1.50
erasers = math.floor(total)
total2 = pencilstotal + erasers * 1.50
print("You will be able to purchase " + str(pencils) + " pencils and " + str(erasers) + " erasers for a total cost of $" + str(total2) + ".")
else:
print("That's too many pencils.")
elif item1 == "erasers":
erasers = int(input("How many erasers will you purchase? "))
if erasers * 1.50 < 150:
eraserstotal = erasers * 1.50
total = 150 - eraserstotal
total = total / 1.75
pencils = math.floor(total)
total2 = pencils * 1.75 + eraserstotal
print("You will be able to purchase " + str(pencils) + " pencils and " + str(erasers) + " erasers for a total cost of $" + str(total2) + ".")
#If the input given is too large based on the budget, this line of code alerts the user.
else:
print("That's too many erasers.")
#If the input is incorrect, the program will print a statement to alert the person that they need to use pencils and erasers as input first.
else:
print("Please run the program again and enter erasers or pencils as your input.")
请记住,上述程序将按顺序运行代码行(顺序)。因此,如果用户首先输入 橡皮擦,那么第一个 if 语句和嵌套的 if 语句将被忽略。如果用户首先输入 铅笔,那么算法将从第一个 if 语句正常运行,并通过剩余条件。以下是程序的顺序操作:
-
要求用户输入他们是在买铅笔还是橡皮擦。
-
如果用户输入铅笔,那么程序会询问他们要购买多少支铅笔。然后,它会计算他们能够购买多少个橡皮擦。
-
如果用户输入的铅笔数量太多,他们将收到一条消息,说明他们买不起那么多。
-
如果用户输入橡皮擦,那么程序会询问他们要购买多少个橡皮擦,然后计算用户能够购买多少支铅笔。
-
如果用户输入的橡皮擦数量太多,他们将收到一条消息,说明他们买不起那么多。
-
如果用户既不输入铅笔也不输入橡皮擦,他们将收到一条消息,要求重新运行程序并输入这两个选项中的一个。
上面是一个过于简化的归纳推理问题。一些归纳推理问题会要求你查看数据,得出一些可能的结论,然后编写一个程序来测试这些结论。在学习逻辑推理的过程中,我们基本上是在训练自己如何看待决策以及如何以程序可以返回我们所期望的输出的方式来处理它们。
在这里需要注意的是,有多种方法可以看待问题并准备解决方案。虽然我更喜欢决策树和流程图,其他程序员和开发人员更倾向于更数学化的方法。还有一些人喜欢用简单的句子和/或段落写下程序需要做的事情。这个过程的目的是让我们创建一个能够产生必要输出并且逻辑上易于程序员、开发人员和运行它的计算机理解的程序。
现在,让我们来看看演绎推理。
应用演绎推理
现在我们来到本章重点讨论演绎推理的部分。即使我是一名数学学生,我也发现演绎推理很迷人。我很快就学会了数学如何教导我们如何在几何中逻辑地跟随论点,我爱上了所有逻辑和真值表的事物。
逻辑是通过证明和归纳和演绎推理来教授的。真值表帮助我们分析条件。在真值表中,有一些假设。例如,一个陈述要么是真的,要么是假的。另一个陈述也是真的或假的。这些陈述的组合取决于这些陈述是真还是假。
好的,这有点复杂。在我继续解释演绎推理之前,让我们先看一个快速的真值表和其中包含的逻辑过程。
真值表在我刚开始编码时非常重要。它们帮助我理解编码过程以及如何处理条件。并不是每个程序员或编码人员都使用这些表,但我发现它们很有帮助,即使在决策过程中没有明确使用。现在让我们来看一个。
假设我们有一个陈述或条件p,并且该条件为True。假设我们有另一个陈述或条件q,并且它也为True。在真值表中,我们使用符号¬来表示NOT。所以,¬p¬是False,¬q也是False。这是因为如果p是True,那么NOTp就是NOTTrue,换句话说,就是False。符号https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/AND_symbol.png用于AND,所以pANDq写作p <https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/AND_symbol1.png> q。符号<https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/OR_symbol.png>用于OR,所以pORq写作p <https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/app-cmp-thk-py/img/OR_symbol.png> q。在表格格式中,我们的真值表如下:
图 4.1 - 真值表
分析真值表并理解所有可能的条件可能需要时间,但这个过程类似于我们在编写问题的逻辑推理时经历的过程。现在,让我们更仔细地看看演绎推理。
让我们首先定义什么是演绎推理。演绎推理是从一个陈述或假设到结论的过程。因为演绎推理是我们在算法设计中使用的,所以在很大程度上,我们需要定义一些与之相关的术语。
让我们从条件陈述开始。
学习条件陈述
条件陈述是 if/then 语句。以下是一些使用条件陈述的逻辑论据:
-
如果下雨,我会用伞。
-
如果我喝水,那么我就不会渴了。
-
如果我的狗需要出去,那么它会站在门边。
-
如果一个四边形有四个直角,那么它是一个矩形。
所有前述陈述都是条件陈述的例子。陈述的第一部分称为假设。陈述的第二部分是结论。在陈述如果下雨,我会用伞中,假设是下雨,结论是用伞。我们在假设和结论中不包括if或then。
在 Python 中,正如您在应用归纳推理部分的示例中看到的,我们在编写算法时经常使用if/then语句。以下是我们在 Python 中使用的一些逻辑陈述:
-
if:在使用if语句时,我们询问条件是否满足,然后根据真假条件执行某些操作。 -
if-else:在使用if-else语句时,我们测试一个条件并执行某些操作,但如果条件不满足,那么我们执行其他操作。 -
if-elif-else:在使用if-elif-else语句时,我们有一个条件;如果条件不满足,我们测试另一个条件,即else if(elif)条件,否则,我们执行其他操作。
所有前述陈述都可以嵌套。我可以测试一个条件,然后另一个条件,然后另一个条件。我们可以在if和else之间有多个elif语句,依此类推。让我们看一些例子。
if 语句
让我们看一个只使用一个if语句的程序:
ch4_ifStatement.py
number = int(input("What's your favorite number? "))
if number < 100:
print("That's not a very large number.")
现在,上述代码是一个简单的程序,只检查一个条件。我们可以添加条件来测试number是否等于100。如果number大于100,我们可以添加另一个条件,依此类推。在这种情况下,我们只关心number是否小于100。
如果我们输入数字53,我们将得到以下输出:
What's your favorite number? 53
That's not a very large number.
如果我们输入数字100,我们将得不到任何消息,程序将结束:
What's your favorite number? 100
正如您所看到的,程序没有任何要添加的内容。条件没有满足,所以它结束了。这就是为什么if-else语句会派上用场的原因。
if-else 语句
让我们看看以前的算法,并添加一个else语句。以前的程序只检查提供的数字是否小于100。如果我们添加一个else语句,我们可以在屏幕上打印其他所有大于或等于100的数字。看看以下程序:
ch4_if-elseStatement.py
number = int(input("What's your favorite number? "))
if number < 100:
print("That's not a very large number.")
else:
print("I guess you like large numbers.")
前面的程序现在无论用户输入什么数字,都会打印出一条消息。让我们再次测试100:
What's your favorite number? 100
I guess you like large numbers.
如您所见,100包括在大数字类别中,因为我们的条件是数字小于100。这意味着100不包括在条件中。测试条件是我们在 Python 中得出结论的方式。我们编写算法从程序本身或用户输入中收集信息,然后测试条件以做出决定。
以下图表显示了if-else决策的流程图。在查看if-elif-else语句和嵌套语句时,我们将查看其他流程图:
图 4.2 - if-else 语句决策流程图
如前图所示,这是一个二进制决策。语句将被测试以检查它是True还是False。如果True,则会发生一个动作;否则,会发生另一个动作。对于我们的数字程序,如果数字小于100,则打印一条消息;否则,在屏幕上打印另一条消息“我猜你喜欢大数字”。现在,让我们添加多个条件。
if-elif-else 语句
if-elif-else语句是多条件语句的简化形式 - 也就是说,您可以有多个elif语句。如前所述,elif代表else if。让我们稍微改变一下我们的程序。我们将允许用户输入一个介于1和20之间的数字。这是我们将编程算法的方式:
-
要求输入一个介于
1和20之间的数字。 -
测试数字是否在
1和10之间,并打印消息。 -
测试数字是否在
11和20之间,并打印消息。 -
打印错误消息。
让我们看看我们如何编写这个程序。在编写此算法之前,我们需要记住一些事情。要轻松检查介于1和10之间的数字,我们需要检查数字是否小于10。这意味着10不包括在内。
我们的elif语句将检查小于21的数字,因为它只包括我们尚未测试过的数字。也就是说,如果用户输入12,第一个条件不满足,所以它会转到第二个条件。是的,这将包括所有小于21的数字,但请记住,如果数字小于10,它将已经满足了一个条件,并且程序将打印正确的消息。
最后,如果条件不满足,我们需要让用户知道他们写了一个不在1和20之间的数字。以下程序演示了这一点:
ch4_if-elif-elseStatement.py
number = int(input("Pick a number between 1 and 20\. "))
if number < 10:
print("That's less than 10.")
elif number < 21:
print("That's between 10 and 20.")
else:
print("That number isn't between 0 and 20\. Run the program and try again.")
让我们尝试用一个小于10的数字进行测试。如果我们用数字8运行程序,我们会看到以下输出:
Pick a number between 1 and 20\. 8
That's less than 10.
如果我们用数字10运行程序,我们会看到以下输出:
Pick a number between 1 and 20\. 10
That's between 10 and 20.
最后,如果我们用数字21运行程序,我们会看到:
Pick a number between 1 and 20\. 21
That number isn't between 0 and 20\. Run the program and try again.
如您所见,每个条件都为我们提供了该条件的答案。这是if-elif-else语句的流程图:
图 4.3 - if-elif-else 语句决策流程图
如前图所示,elif只是提出了一个新的测试。如果True,我们就会按照算法的行动。如果False,我们就会转到else语句。也就是说,我们可以有多个elif条件。这意味着我们可以继续一次又一次地测试条件,直到达到else语句为止。
理解嵌套语句
Python 中我们使用的另一种逻辑语句类型涉及嵌套语句。在嵌套条件中,嵌套的if语句只有在前一个if语句为True时才会执行。通过一个例子更容易理解。让我们回到我们的if-elif-else语句,并添加一些嵌套条件。我们之前要求用户输入一个在1到20之间的数字。现在,让我们使用以下代码进一步细分条件:
ch4_nestedStatements.py
number = int(input("Pick a number between 1 and 20\. "))
if number < 10:
if number < 6:
print("Why such a small number?")
else:
print("Well, less than 10 but greater than 5\. I'll take it.")
elif number < 21:
if number < 16:
print("You like values that are greater than 10, but not too much greater. I guess that's fine.")
else:
print("I like larger numbers myself too.")
else:
#Sometimes we make mistakes when providing input in programs. If you choose a number that's not between 0 and 20, the program will print this message.
print("That number isn't between 0 and 20\. Run the program and try again.")
在上面的代码片段中,代码对于我们输入不符合规定的数字时有一条消息。例如,要求输入的数字在1到20之间。*但是如果用户输入 0 或 21,或者其他不在这个范围内的数字会发生什么?*然后,print()语句会提供一条消息,要求用户重新运行程序。
在这种情况下,你可以看到我们有if语句、elif语句、嵌套的if和else语句等。让我们看一些测试案例,测试一些条件,看看我们的程序会说些什么:
- 当我们输入
4时,我们看到以下输出:
Pick a number between 1 and 20\. 4
Why such a small number?
- 当我们输入
6时,我们看到以下内容:
Pick a number between 1 and 20\. 6
Well, less than 10 but greater than 5\. I'll take it.
- 当我们输入
11时,我们得到如下结果:
Pick a number between 1 and 20\. 11
You like values that are greater than 10, but not too much greater. I guess that's fine.
- 当我们输入
18时,我们得到以下输出:
Pick a number between 1 and 20\. 18
I like larger numbers myself too.
从上面的测试案例中可以看出,我们根据程序中给定的条件有更多的输出。虽然这是一个简单的数字程序,但在解决更复杂的问题时,我们可以使用类似的逻辑。
假设你经营一个在线商店。用户选择的商品将会在类似的算法中使用,尽管这些算法要复杂得多。算法会测试条件,比如选择的商品、选择的数量等,以应用总额、优惠券等。这就是为什么逻辑和逻辑推理在编程中如此重要。
现在,正如之前提到的,我们使用的逻辑处理对于每个程序员可能会有所不同。然而,无论偏好如何,逻辑推理和逻辑处理在我们编写算法时绝对是必不可少的。我们在编写算法时,不是直接开始写,而是处理问题,看决策和需要发生哪些步骤,然后设计算法。这种逻辑过程对于创建有效的算法至关重要。在本书中,我们将继续在分析问题时关注逻辑推理,即使我们没有明确说明。
在本节中,你学习了逻辑推理及其两种类型——归纳推理和演绎推理。我们还学习了在编码时会派上用场的条件语句。
我们编写的一些算法可以使用布尔逻辑和运算符进行简化,这就是我们将在下一节中看到的内容。
使用布尔逻辑和运算符
布尔逻辑指的是 Python 中的and、or和not等运算符。你可能还记得在本章早些时候对真值表的简要讨论中看到过这些内容。接下来我们将看到,即使没有明确说明或使用这些表,我们在编写算法时仍然使用相同的逻辑处理。在解决计算思维问题时,我们有时需要同时满足多个条件。让我们现在用语言来看一下这个问题。
让我们分类一些水果。如果水果是圆形且橙色、绿色或黄色,它将被分类到组 1。如果水果不是圆形,但是橙色、绿色或黄色,它将被分类到组 2。如果水果不符合这些要求,它将被分类到组 3。让我们简化这些组:
-
组 1:圆形和(橙色或绿色或黄色)
-
组 2:非圆形和(橙色或绿色或黄色)
-
组 3:所有其他水果
我知道我先前提到了圆形条件。但是如果你看一下组 1和2,水果需要针对这些颜色进行测试,无论是哪种条件——也就是说,如果颜色条件不满足,水果是否圆形并不重要,它都会被放入组 3。所以,这是我会为算法写的内容:
-
测试水果是橙色、绿色还是黄色。
-
如果是,测试是否圆形,并分类为第 1 组或第 2 组。
-
如果不是,分类为第 3 组。
因此,如果我们有一颗柑橘,那就属于第 1 组。如果我们有一根香蕉,它就属于第 2 组。如果我们有草莓,它们就属于第 3 组。
现在,如果我们要编写这个,我们需要确保已经添加了水果的特征,以便我们可以对其进行测试。我们将在本书的后续章节中看到类似的内容,但现在,为了简化一些学习,我们将创建一个类似的算法,但使用数字。
在我们继续之前,让我们快速看一下 Python 中的基本运算符:
图 4.4 - 基本的 Python 运算符
当我们到达第二部分,应用 Python 和计算思维时,我们将更深入地了解这些运算符,并深入研究 Python 编程语言。然而,我们需要在下一个算法中使用其中一些。首先,让我们看看and运算符。
and 运算符
为了更好地理解and运算符,最好看一个数学算法。让我们输入一个数字,并测试该数字是否大于100且是2的倍数。要测试一个数字是否是2的倍数,我们使用取模运算符(mod)。在 Python 中,mod的符号是%。
因此,从代码中可以看出,如果number % 2 == 0,那么这个数字是2的倍数。如果number % 2 == 1,那么它就不是2的倍数。我们使用等于(==)运算符或不等于(!=)运算符来完成这些条件:
ch4_andOperator.py
number = int(input("Give a number between 1 and 200\. "))
if number > 99 and number % 2 == 0:
print("That's a large, even number.")
elif number > 99 and number % 2 != 0:
print("That's a large, odd number.")
elif number < 100 and number % 2 == 0:
print("That's a small, even number.")
else:
print("That's a small, odd number.")
现在,我知道我们已经讨论了编写算法的不同方法。我需要为这个使用 AND 运算符吗? 可能不需要。我本可以只将其写成嵌套语句,if-elif-else语句等。一些测试用例和算法的结果如下所示:
- 当我们输入
104时,我们看到以下输出:
Give a number between 1 and 200\. 104
That's a large, even number.
- 当我们输入
80时,我们看到以下输出:
Give a number between 1 and 200\. 80
That's a small, even number.
- 当我们输入
31时,我们得到以下输出:
Give a number between 1 and 200\. 31
That's a small, odd number.
从前面的测试用例中可以看出,程序测试了我们的情况,并根据满足的条件提供了打印消息。现在,让我们来看一下or运算符。
或运算符
正如我们在本章前面的水果示例中看到的,我们检查水果的颜色是橙色、绿色还是黄色。这就是or运算符的工作原理。我们检查某种情况或另一种情况。这次,我们将看一些True和False的陈述。假设变量A是True,变量B是False。如果我们使用or运算符来检查A或B的结果,那么我们的答案将是True。
为什么呢?因为无论如何,结果要么是True,要么是False,这是一个True的陈述。困惑吗? 逻辑可能会让人困惑。让我们继续测试以下程序中的A and B以及A or B,以帮助您形象化:
ch4_orOperator.py
A = True
B = False
C = A and B
D = A or B
if C == True:
print("A and B is True.")
else:
print("A and B is False.")
if D == True:
print("A or B is True.")
else:
print("A or B is False.")
现在,我添加了一些条件,以便我们得到输出,并且您可以看到我所说的逻辑是正确的,但我们不需要做所有这些。我们本可以只打印C和D。
当我们运行这个程序时,结果如下:
A and B is False.
A or B is True.
正如您所看到的,A and B是False,因为其中一个陈述是False,这意味着整个事情是False。A or B是True,因为其中一个是True,所以条件是True。现在,让我们看看最后一个运算符(暂时),not运算符。
not 运算符
not运算符让我们测试事物的相反情况。因此,如果A设置为True,那么not A就是False。就是这么简单。让我们通过以下代码看一些例子:
ch4_notOperator.py
A = True
B = False
print(not A)
print(not B)
print(not (A and B))
print(not (A or B))
从前面的代码中,我们已经讨论了这里的第一个打印语句。由于A是True,not A是False。对于第二个print语句,我们期望结果是True,因为B是False。现在,我们之前做了A and B和A or B语句。我们知道A and B是False,所以not (A and B)是True。我们也知道A or B是True,所以not (A or B)是False。
让我们看看程序打印了什么:
- 对于
not A,它打印如下内容:
False
- 同样,对于
not B,它打印如下内容:
True
- 此外,对于
not (A and B),它打印如下内容:
True
- 最后,对于
not (A or B),它打印如下内容:
False
在本节中,您已经了解了一些布尔运算符。使用布尔运算符,我们可以编写测试用例的算法,并根据这些情况提供输出。如前所述,程序将根据我们在算法中编写的指令运行。
通过使用这些运算符编写我们的算法,我们可以确保条件仅在我们希望它们适用的情况下应用。我们可以包括语句和提示来帮助产生正确的结果,而不是让程序在不正确的条件下运行。例如,如果距离的输入被意外输入为负数,布尔语句可以检查条件并在程序内为人们提供反馈,然后再次运行。使用布尔运算符提供清晰的逻辑过程,并允许更好和更清晰的算法。
现在我们已经看过基本运算符,很重要的是我们也要看看错误。识别逻辑错误可以帮助我们避免算法中的陷阱。
识别逻辑错误
在我们谈论太多逻辑错误之前,让我们谈谈为什么牢记它们的重要性。在 Python 中,并非所有错误都会导致程序失败或崩溃。一些逻辑错误将允许程序完全运行,而不会崩溃或警告用户发生错误。这些错误很难识别。
以下是一些可能让我们陷入麻烦的逻辑错误,但请记住,有许多方法可以无意中将逻辑错误纳入我们的程序中:
-
在等式或语句中使用错误的变量
-
使用错误的运算符来测试条件
-
在检查条件时使用错误的缩进
我最内疚的是交换我的变量,但我也经常在缩进上犯错误。通常,当我尝试运行程序时,这些错误更容易被识别,因为在某些情况下程序可能无法运行。
让我们来看一个包含公式错误的简单算法。在第一个算法中,目标是在餐厅以每份 1.50 美元的价格购买一定数量的薯条后获得总费用:
ch4_Error1.py
number = int(input("Type the number of fries you are ordering: "))
cost = 1.50
total = number * number
print("Your total cost is $" + str(total) + ".")
如果我们运行上述程序,程序将无问题/错误地运行,并为12份薯条显示以下输出:
Type the number of fries you are ordering: 12
Your total cost is $144.
现在,如果我们注意到,我们会意识到 12 份薯条的费用为 144 美元太高了。这是因为我们的算法中存在错误。算法应该包含total = cost * number公式,如下所示:
ch4_Error1_fixed.py
number = int(input("Type the number of fries you are ordering: "))
cost = 1.50
total = cost * number
print("Your total cost is $" + str(total) + ".")
现在我们已经修复了该公式,输出是正确的:
Type the number of fries you are ordering: 12
Your total cost is $18.0.
如您所见,12 份薯条每份 1.50 美元,总共 18.0 美元更合理。
公式错误可能很难找到,特别是如果程序在不警告错误的情况下运行。如果我们有一个包含多个公式的大型算法,找到这些错误可能会变得繁琐和冗长。对此的最佳建议是在您能够的每个步骤中测试您的算法。这样,找到错误就变得更简单了。
现在让我们来看一下在测试条件时出现的错误。与公式错误类似,条件测试中的错误可能很难发现,因为程序可能仍然会运行:
ch4_Error2.py
number = int(input("Give a number between 1 and 200\. "))
if number > 99 and number % 2 == 0:
print("That's a large, even number.")
elif number > 99 and number % 2 != 0:
print("That's a large, odd number.")
elif number < 100 or number % 2 == 0:
print("That's a small, even number.")
else:
print("That's a small, odd number.")
在上面的代码中,算法中存在一个错误,导致我们在输入一些奇数时得到错误的反馈。看看第二个elif语句。那个or将产生一个错误。
如果我们运行这个程序,我们会得到一个输出。让我们用数字99来运行它:
Give a number between 1 and 200\. 99
That's a small, even number.
现在,问题在于99不是一个偶数。在算法的某个地方,我们在条件中引入了一个错误。在这种情况下,我们使用了or而不是and运算符:
elif number < 100 or number % 2 == 0:
print("That's a small, even number.")
一旦我们用and替换or,我们就可以再次运行程序:
ch4_Error2_fixed.py
number = int(input("Give a number between 1 and 200\. "))
if number > 99 and number % 2 == 0:
print("That's a large, even number.")
elif number > 99 and number % 2 != 0:
print("That's a large, odd number.")
elif number < 100 and number % 2 == 0:
print("That's a small, even number.")
else:
print("That's a small, odd number.")
使用99作为输入,我们得到以下输出:
Give a number between 1 and 200\. 99
That's a small, odd number.
使用98作为输入运行程序,我们得到以下结果:
Give a number between 1 and 200\. 98
That's a small, even number.
如您所见,除非我们注意,否则我们可能会忽略条件和逻辑运算符中的错误。因为程序能够在我们的算法中运行这些错误,所以要捕捉到我们犯了错误的地方比起纳入会导致程序停止运行的错误要困难得多。
最后,让我们看一下使用相同的条件测试代码的缩进错误。这次,加入了一个缩进错误,我们得到了以下结果:
ch4_Error3.py
number = int(input("Give a number between 1 and 200\. "))
if number > 99 and number % 2 == 0:
print("That's a large, even number.")
elif number > 99 and number % 2 != 0:
print("That's a large, odd number.")
elif number < 100 and number % 2 == 0:
print("That's a small, even number.")
else:
print("That's a small, odd number.")
在这种情况下,我们无法运行程序。第二个elif语句的缩进不正确。当我们尝试运行程序时,我们会收到一个“无效语法”错误消息。单击消息上的确定将带我们到代码,缩进错误会被突出显示,如下面的屏幕截图所示:
图 4.5 - 缩进错误
请注意elif语句下面的print()代码也缩进错误。一旦我们修复了这两个错误,我们就可以运行代码,就像我们在本章中之前做的那样。
将错误纳入我们的算法是一个常见的错误。正如您从前面的例子中看到的,识别一些错误可能很难,因为程序可能会像没有问题一样运行。
我可能没有在我的算法中捕捉到许多条件错误,但这可能只是因为我从未意识到一开始就有错误。这就是为什么运行我们的程序的各种实例以确保我们得到的结果是有意义的非常重要的原因之一。在本书中,我们将在查看程序和计算思维问题时讨论更多错误。与此同时,测试您的程序并经常测试。三次检查您的数学、缩进和逻辑。
总结
在本章中,我们讨论了归纳和演绎推理、逻辑推理、逻辑运算符和布尔逻辑。正如我们讨论的那样,大多数算法设计都属于演绎推理。我们学会了如何使用语句,如if、if-else、if-elif-else和嵌套语句,来编写测试条件的程序。
此外,我们了解到一些错误很难识别,因此验证我们的程序并经常测试它们是很重要的。
经过本章的学习,您现在更有能力使用逻辑推理编写算法。您还具备了在设计和规划算法时应用归纳和演绎推理的理解,并在算法中使用布尔逻辑和运算符的能力。您现在还能够通过识别可能的错误来测试您的算法,例如缩进错误、条件错误和公式错误。
在下一章中,我们将更深入地分析问题,使用计算思维元素来分解问题,以便我们可以创建有意义和有用的算法。
第五章:探索问题分析
在这一章中,我们将深入探讨问题分析,同时运用我们所学的一些知识,比如逻辑推理、布尔逻辑和算法设计。在这一章中,我们将通过问题定义、分解和分析来解决问题。
在本章中,我们将涵盖以下主题:
-
理解问题定义
-
学习分解问题
-
分析问题
为了进一步理解问题,我们需要看一个更复杂的问题,并定义它,以便开始算法设计过程。在本章中,您将学习如何定义问题并分解问题,以便设计算法。在这个过程中,您还将学习 Python 中的字典。阅读完本章后,您将能够使用计算思维过程来设计和创建解决复杂问题的算法。
技术要求
您需要最新版本的 Python 来运行本章的代码。您可以在此处找到本章使用的完整源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter05
理解问题定义
正如我们在第二章中讨论的,计算思维的要素,计算思维使用四个要素来解决问题:
-
问题分解:这是将数据分解的过程。
-
模式识别:这是找到相似性或模式的过程。
-
抽象:这个元素处理泛化模式。
-
算法设计:这是我们为解决问题定义一组指令的地方。
在本节中,为了更多地了解如何分析问题,我们将分析一个更大的问题,并经过需要创建算法的步骤。为了能够创建算法,我们必须分析问题,并清楚地确定我们要解决什么问题。也就是说,我们的算法是为了什么? 为什么我们需要构建它? 查看问题的分解,然后定义我们需要的东西,将在最后为我们提供更好的算法。
我们将在下一节中解决一个问题。
问题 5A – 创建一个在线商店
让我们来看看以下问题。您正在开设一家在线商店。它还处于起步阶段,但您将有三种不同类型的商品可供选择。它们是钥匙扣、水瓶和 T 恤。对于这个特定的问题,我们将经历一个三步过程:
-
做出假设
-
需要考虑的事项
-
构建字典
我们将在接下来的部分中看到前面的步骤。
做出假设
让我陈述一些关于我们将要使用的这家商店的假设:
-
这是一家为客户提供商品的公司,供客户与他们的客户分享。
-
每个项目都可以有标志和/或个性化信息,比如姓名、电子邮件和电话号码。
现在我们将进入下一节,讨论需要考虑的事项。
需要考虑的事项
现在让我们在开始制定算法之前,看看您需要考虑的一些事项:
-
商品是否个性化?
-
个性化是否按字符、行或项目收费?
-
价格是固定的,还是当客户批量购买时会发生变化?
-
如果客户订购多种类型的商品,是否会有折扣?
-
每个项目的基准价格是多少?
前面的观点并不是我们可以讨论的唯一问题。但当我们分解问题时,这些是我们将开始研究的问题。
在那之前,让我们讨论一下如何在程序中为每个项目包含信息。如果你还记得[第三章](B15413_03_Final_SK_ePub.xhtml#_idTextAnchor056),理解算法和算法思维,我们可以使用Python中的字典来保存我们的商品菜单。在这种情况下,我们有钥匙扣、水瓶和 T 恤。
建立一个字典
在我们看这个问题所提出的复杂性并分解信息之前,我们可以建立自己的字典。我们可以使字典中每个项目的价格都是基础价格(不包含任何定制或折扣的价格),如下所示:
-
每个钥匙扣的成本:$0.75
-
T 恤成本:$8.50
-
每个水瓶的成本:$10.00
现在让我们建立字典。记住,你可以在没有字典的情况下做到这一点,但是创建一个字典可以让你在以后有必要时更新定价。你也可以创建函数来解决这个问题。我们正在使用逻辑和字典来解决这个问题。以下代码向你展示了如何建立一个字典:
ch5_storeDictionary.py
online_store = {
'keychain': 0.75,
'tshirt': 8.50,
'bottle': 10.00
}
print(online_store)
从前面的代码片段中,请记住这里不需要print()函数,但我经常使用它来确保代码在我继续构建算法的同时能正常工作。还要注意变量的名称——keychain、tshirt和bottle——都是简化的。
这是输出的样子:
{'keychain': 0.75, 'tshirt': 8.5, 'bottle': 10.0}
输出显示给我的是每个变量的价格都被正确保存了。我使用print函数来测试我的字典,并确保它在我开始从字典中获取所需内容之前能够正确运行。
这有助于我们在编写代码时重复使用变量。拥有这些简单易识别的变量将允许我们在不添加错误的情况下更改和添加算法。
在本节中,我们了解到问题分析和定义帮助我们确定如何最好地设计我们的解决方案。记住,当我们面对问题时,我们在编写算法之前和算法中使用的定义对于我们的设计和最终产品至关重要。现在让我们来看看问题的分解。
学习分解问题
当我们分解问题时,我们正在确定算法需要为我们提供什么。最终用户需要看到一些无缝的东西。看看图 5.1中的流程图;这是一个基本的决策流程图,帮助我们设计我们的算法。
首先让我们做另一个假设,即,如果用户输入超过 10,价格将会更低。在这种情况下,我们只会做少于 10 或大于或等于 10。然而,如果你需要进一步细分,你可以添加更多情况,比如以下情况:
-
少于或等于 10
-
超过 10 但小于或等于 50
-
50 或更多
你可以有尽可能多的情况。对于这个算法,我们将保持两种情况,因为我们还必须包括个性化成本,我们不想创建一个过于复杂的算法。
下图向你展示了算法的流程图:
图 5.1 – 初始决策流程图
正如你在前面的图表中所看到的,这不是一个完成的流程图。在我们做出关于 T 恤的决定之后,我们需要转向瓶子。我们如何编写算法将取决于我们想要输出什么。现在,我们提供给用户的是当他们结账离开你创建的在线商店时会得到的信息。
在下一节中,让我们使用前面的流程图来创建一个算法。
将流程图转换为算法
图 5.1中的图表使我们能够查看我们正在编写的算法的决策过程。在编写算法时,我们将要关注以下关键点:
-
字典和输入:输入可以在算法内部或由用户输入;字典是在算法内部构建的。这意味着,要使用字典,我们必须在算法中定义它,然后才能使用它。
-
成本:这是每件物品的基本成本。
-
个性化成本:这是添加到基本成本中的成本。
我们现在将在接下来的部分详细查看前面的要点。
构建字典并提供输入
在添加任何复杂性之前,让我们看看如何获取每件物品的价格并将其用作基本价格。我们需要计算每种物品的数量。以下代码向您展示了这一点:
ch5_storeQuantities.py
online_store = {
'keychain': 0.75,
'tshirt': 8.50,
'bottle': 10.00
}
keychain = online_store['keychain']
tshirt = online_store['tshirt']
bottle = online_store['bottle']
choicekey = int(input('How many keychains will you be purchasing? If not purchasing keychains, enter 0\. '))
choicetshirt = int(input('How many t-shirts will you be purchasing? If not purchasing t-shirts, enter 0\. '))
choicebottle = int(input('How many t-shirts will you be purchasing? If not purchasing water bottles, enter 0\. '))
print('You are purchasing ' + str(choicekey) + ' keychains, ' + str(choicetshirt) + ' t-shirts, and ' + str(choicebottle) + ' water bottles.')
从前面的代码片段中可以看到,我们在字典下添加了变量。这将在以后很有用。这些变量被命名为choicekey、choicetshirt和choicebottle。命名变量使我们能够返回并根据需要更改代码。在这种情况下,每个变量都要求从运行程序的人那里获取输入,以获取他们订购的钥匙扣、T 恤和水瓶的数量。再次强调,解决这个问题有多种方法,但我们正在利用到目前为止学到的知识来创建一个算法解决方案。
当我们对3个钥匙扣、0件 T 恤和10个水瓶运行前面的代码时,这是我们的输出:
How many keychains will you be purchasing? If not purchasing keychains, enter 0\. 3
How many t-shirts will you be purchasing? If not purchasing t-shirts, enter 0\. 0
How many t-shirts will you be purchasing? If not purchasing water bottles, enter 0\. 10
You are purchasing 3 keychains, 0 t-shirts, and 10 water bottles.
正如您所看到的,我们有一个接受用户输入的程序,然后向用户确认他们为每件物品所做的选择。
让我们看看关于成本的下一节。
对成本进行更改
现在让我们添加成本的变化。假设如果顾客购买超过 10 件物品,那么更新后的成本如下:
-
钥匙扣:$0.65
-
T 恤:$8.00
-
水瓶:$8.75
为了进行前述更改,我们可以让程序更新成本差异,如下所示:
ch5_storeCost.py
online_store = {
'keychain': 0.75,
'tshirt': 8.50,
'bottle': 10.00
}
choicekey = int(input('How many keychains will you be purchasing? If not purchasing keychains, enter 0\. '))
choicetshirt = int(input('How many t-shirts will you be purchasing? If not purchasing t-shirts, enter 0\. '))
choicebottle = int(input('How many t-shirts will you be purchasing? If not purchasing water bottles, enter 0\. '))
print('You are purchasing ' + str(choicekey) + ' keychains, ' + str(choicetshirt) + ' t-shirts, and ' + str(choicebottle) + ' water bottles.')
if choicekey > 9:
online_store['keychain'] = 0.65
if choicetshirt > 9:
online_store['tshirt'] = 8.00
if choicebottle > 9:
online_store['bottle'] = 8.75
keychain = online_store['keychain']
tshirt = online_store['tshirt']
bottle = online_store['bottle']
print(online_store)
现在我们已经更新了代码,我想打印出我的进展,以确保代码正常工作并进行更改。在这种情况下,我想确保如果总数大于 10,成本会更新。(也就是说,当顾客订购超过 10 件物品时,它会将每件物品的成本更新为更低的成本。)前面代码的输出如下:
How many keychains will you be purchasing? If not purchasing keychains, enter 0\. 10
How many t-shirts will you be purchasing? If not purchasing t-shirts, enter 0\. 14
How many t-shirts will you be purchasing? If not purchasing water bottles, enter 0\. 10
You are purchasing 10 keychains, 14 t-shirts, and 10 water bottles.
{'keychain': 0.65, 'tshirt': 8.0, 'bottle': 8.75}
您现在可以从前面的输出中看到,字典已根据用户提供的总数进行了更新。
现在我们需要继续并提供成本。我们可以提供总物品成本或整个购买的总成本,或两者(让我们两者都做)。看一下以下代码片段:
ch5_storeTotals.py
keychain = online_store['keychain']
tshirt = online_store['tshirt']
bottle = online_store['bottle']
print('You are purchasing ' + str(choicekey) + ' keychains, ' + str(choicetshirt) + ' t-shirts, and ' + str(choicebottle) + ' water bottles.')
前面的代码片段被添加,以便我们可以有一个print语句来确认用户的输入。通过在代码末尾打印该语句,我们正在与用户核对程序是否能正确读取数字,以及用户是否输入了正确的数字。
我们可以继续编写代码以添加每件物品的成本:
ch5_storeTotals.py
totalkey = choicekey * keychain
totaltshirt = choicetshirt * tshirt
totalbottle = choicebottle * bottle
grandtotal = totalkey + totaltshirt + totalbottle
print('Keychain total: $' + str(totalkey))
print('T-shirt total: $' + str(totaltshirt))
print('Water bottle total: $' + str(totalbottle))
print('Your order total: $' + str(grandtotal))
在前面的代码片段的末尾的print语句提供了每件物品的总成本以及整个订单的总成本的细目。在询问所有物品的输入后,代码然后打印每件物品成本的小计。前面代码的结果如下:
How many keychains will you be purchasing? If not purchasing keychains, enter 0\. 10
How many t-shirts will you be purchasing? If not purchasing t-shirts, enter 0\. 7
How many t-shirts will you be purchasing? If not purchasing water bottles, enter 0\. 14
You are purchasing 10 keychains, 7 t-shirts, and 14 water bottles.
Keychain total: $6.5
T-shirt total: $59.5
Water bottle total: $122.5
Your order total: $188.5
现在我们已经有了没有个性化的物品总数,我们需要考虑如果订购了个性化,需要考虑个性化的成本。
在下一节中,让我们看看个性化成本是多少,以及在继续之前我们需要做出的决定。
添加个性化
目前,让我们将钥匙扣、T 恤和水瓶的个性化限制为二进制问题,即用户要么想要个性化,要么不想要。我们不考虑个性化的分层成本,这可能是您见过的。如果您想要添加分层,您需要做出更多决策,比如选择字体的成本,个性化的长度等等。目前我们不考虑这些,但随时可以添加到这段代码中以解决这些类型的自定义。让我们为个性化添加另一个假设:
-
1 美元的钥匙扣
-
T 恤 5 美元
-
水瓶 7.50 美元
我们需要创建前述条件,然后将它们实施到我们的变量中。让我们逐部分查看代码。以下文件包含我们将分解的每个部分。
回想一下,我们的算法首先要求输入购买物品的数量。以下代码片段获取用户输入,以便考虑个性化:
ch5_storePersonalize.py
perskey = input('Will you personalize the keychains for an additional $1.00 each? Type yes or no. ')
perstshirt = input('Will you personalize the t-shirts for an additional $5.00 each? Type yes or no. ')
persbottle = input('Will you personalize the water bottles for an additional $7.50 each? Type yes or no. ')
if perskey == ('yes' or 'Yes'):
online_store['keychain'] = online_store['keychain'] + 1.00
if perstshirt == ('yes' or 'Yes'):
online_store['tshirt'] = online_store['tshirt'] + 5.00
if persbottle == ('yes' or 'Yes'):
online_store['bottle'] = online_store['bottle'] + 7.50
keychain = online_store['keychain']
tshirt = online_store['tshirt']
bottle = online_store['bottle']
totalkey = choicekey * keychain
totaltshirt = choicetshirt * tshirt
totalbottle = choicebottle * bottle
grandtotal = totalkey + totaltshirt + totalbottle
前面的代码片段询问用户关于个性化的二进制问题。在获取输入后,代码根据用户输入做出一些决策,并定义keychain、tshirt和bottle变量以及选择的总数。接下来的代码片段使用总数打印出每个购买物品的信息以及最终总数:
print('Keychain total: $' + str(totalkey))
print('T-shirt total: $' + str(totaltshirt))
print('Water bottle total: $' + str(totalbottle))
print('Your order total: $' + str(grandtotal))
从前面的代码中可以注意到,keychain、tshirt和bottle变量是在基于总数和个性化的自定义之后定义的。记住,在算法设计中,顺序很重要。如果我们在程序中较早地定位这些变量,随后的个性化等条件将不会影响这些变量。
因此,为了能够获得我们变量所需的一切,我们需要在定义一些影响它们的条件之后定义它们,比如自定义。看一下前面的代码,注意变量的定义位置。随时可以通过更改变量定义的位置来玩弄代码,看看最终结果是否会改变。
这是一个具有钥匙扣决策过程的视觉流程图:
图 5.2 - 钥匙扣决策流程
正如您从前面的图表中所看到的,这仅适用于钥匙扣。我们需要为另外两个变量重复这个过程。在图表中,您可以看到物品的决策过程。首先,用户指示购买的物品数量,然后指示他们是否会个性化。
根据每个答案,程序计算总数。例如,如果没有个性化,总数会在决策树中更早地计算出来。我们可以使用函数(如我之前提到的)重写这个程序,以简化一些过程。目前,我们专注于学习如何分解问题,分析条件,以及如何设计算法以考虑多个决策。记得完成其他物品的图表,以便在设计算法时决策过程更容易编码。
在本节中,我们学习了如何使用流程图创建算法。我们还学习了为我们的算法构建字典。
在我们继续之前,让我们看看分析问题的过程。在创建这个算法时,我们分解了问题。然而,在我们下一章之前,有一些问题分析的关键组成部分值得我们考虑。
分析问题
在分析问题时,有一些步骤可以帮助我们确保我们正在创建最佳算法:
-
清楚地阅读和理解问题。
-
确定解决方案的主要目的。
-
确定问题的约束。
-
确定决策流程。
-
建立可能解决问题的算法。
-
为问题确定最佳的算法工具。
-
经常测试算法片段。
-
验证算法是否提供了已确定问题的解决方案。
如果我们回到我们的问题,我们在整个章节中都经历了这个过程:
-
我们有一个有三种商品的在线商店。
-
商品成本取决于购买数量。
-
商品价格也取决于个性化定制。
-
我们创建了流程图来帮助我们确定决策过程以及如何编写代码。
-
我们通过代码行验证了我们的代码,这些代码行允许我们多次检查算法是否产生了正确的响应。
-
根据需要重新访问和重新排序代码片段。
-
我们验证了算法的输出是否符合我们所确定的问题。
前面的过程需要重复,也就是说,这不是一个线性过程。有时我们会编写一个算法,然后重新访问决策流程图,进行调整,然后再次处理算法。
在处理更大的问题时,分析我们的问题在多个停止点变得更加清晰。我们应该在测试之前写上几百行代码吗? 不!想象一下有 300 行代码,只在第 20 行发现一个错误,这个错误会一直传递到算法的其余部分。
在每个可能的进度点进行测试将使我们能够发现长期成本的小错误。记住,第一次几乎不可能编写完美的算法。我们都会犯错误,无论大小,所以继续测试和分析我们的进展非常重要。
在离开本章之前,让我们再看一个问题并再次经历这个过程。
问题 5B - 分析一个简单的游戏问题
你想设计一个猜数字游戏。用户必须猜一个随机数。
让我们从定义我们的问题开始,这种情况下是一个游戏。让我们确定已知信息:
-
计算机需要随机选择一个数字。
-
用户需要输入一个数字。
-
计算机将不得不检查用户输入是否与随机生成的数字匹配。
现在,这还不够!如果我第一次猜不中,我会输吗?我有多少次机会?随机数是 1 到 10 之间还是 1 到 500 之间? 在我们开始编码之前,我们需要做一些决定。让我们添加一些参数:
-
数字在 1 到 100 之间。
-
用户将有 5 次猜测的机会。
-
计算机会告诉用户答案是太高还是太低。
现在我们有了这些参数,我们可以创建一个决策流程图:
图 5.3 - 猜数字游戏的决策流程图
从前面的图表中可以看出,图表并不完整。这是因为我们将使用一些逻辑使过程重复 5 次。我们稍后会详细介绍。现在,请注意决策。首先,程序生成一个数字(但不会显示)。然后用户输入一个猜测,要么正确,要么错误。如果正确,用户就赢得了游戏。如果不正确,程序会告诉用户答案是太低还是太高,并要求新的猜测。然后过程将根据需要重复。现在,让我们编写算法。
首先,让我们生成随机数并让用户猜测。为随机生成的数字和用户输入添加print()函数,以便您可以看到信息是否正常工作。记住,我们以后会把它们拿掉,但是反复检查我们的代码作为问题分析过程的一部分非常重要。以下代码将执行相同的操作:
ch5_guess1.py
import random as rand
compnumber = rand.randint(1, 100)
print(compnumber)
usernumber = int(input('Choose a number between 1 and 100\. You'll get 5 guesses or you lose! '))
print(usernumber)
你会注意到前面的代码中导入了random模块。我们还将其导入为rand。这只是为了节省时间和空间。在 Python 中,当你导入一个模块时,你可以给它重命名。random模块给了我们一个在我们选择的范围内生成数字的方法。
rand.randint(1, 100)代码行包括1和100。这些是随机数生成器的端点或限制。rand函数是指模块,如前所述,而randint(a, b)指的是a和b之间的随机整数(包括a和b)。
多次运行代码,看看计算机生成的数字每次都会发生变化。以下是三个测试案例:
- 以下是前面代码的测试案例 1:
27
Choose a number between 1 and 100\. You'll get 5 guesses or you lose! 10
10
如你从前面的输出中看到的,27是计算机生成的随机数,而10是用户输入的数字。
- 以下是前面代码的测试案例 2 的结果:
68
Choose a number between 1 and 100\. You'll get 5 guesses or you lose! 65
65
如你从前面代码的输出中看到的,68是compnumber变量的值,而用户(我)输入的数字是65。如此接近,但又如此遥远!
- 以下是测试案例 3 的输出:
50
Choose a number between 1 and 100\. You'll get 5 guesses or you lose! 23
23
正如你从前面的输出中看到的,计算机选择了数字50,而用户输入了23。
对于我们游戏的最终版本,我们不会打印出计算机的数字。那是作弊!现在,我们只是在测试。
让我们再添加一个条件,即第一次猜测是否正确。为此,我们将必须验证compnumber == usernumber。在进入额外的重复和逻辑之前,我们将再次测试这一点,所以我们只需说如果是真的,那么你赢了;如果是假的,那么你输了:
ch5_guess2.py
import random as rand
compnumber = rand.randint(1, 100)
usernumber = int(input('Choose a number between 1 and 100\. You'll get 5 guesses or you lose! '))
if compnumber == usernumber:
print('You win!')
else:
print('You lose!')
让我们说我在第一次尝试时失败了。但是我不会放弃,因为这可能需要 100 次或更多次。当你运行程序时,它看起来像这样:
Choose a number between 1 and 100\. You'll get 5 guesses or you lose! 35
You lose!
现在让我们谈谈重复一行代码。我们给用户 5 次猜测。我们如何在 Python 中做到这一点?
在 Python 中,我们可以使用for循环来迭代代码。我们知道我们有 5 次猜测,所以我们将使用类似for number in range(5):这样的东西来开始我们的逻辑,如下面的代码所示:
ch5_guess3.py
import random as rand
compnumber = rand.randint(1, 100)
i = 5
for number in range(5):
usernumber = int(input('Choose a number between 1 and 100\. You have ' + str(i) + ' guesses left. '))
if compnumber == usernumber:
print('You win!')
else:
i = i - 1
print('You're out of guesses! You lose! ')
从前面的代码中,你注意到i*变量了吗?*我们使用这个变量,这样用户就知道他们还剩下多少次猜测。所以如果我们有 5 次猜测,代码将从i = 5开始;然后,如果用户猜错了,它将使用i = i - 1这一行,提醒用户他们现在还剩下 4 次猜测,依此类推。看看我们运行该程序时会发生什么:
Choose a number between 1 and 100\. You have 5 guesses left. 14
Choose a number between 1 and 100\. You have 4 guesses left. 98
Choose a number between 1 and 100\. You have 3 guesses left. 48
Choose a number between 1 and 100\. You have 2 guesses left. 12
Choose a number between 1 and 100\. You have 1 guesses left. 54
You're out of guesses! You lose!
现在,我们并不是真的很公平。如前所述,我们希望每次用户尝试猜测时都给出一个提示。现在我们有了检查它们是否相等的条件,我们可以添加一个elif条件来检查它是大还是小。下面的代码显示了这一点:
ch5_guess4.py
import random as rand
compnumber = rand.randint(1, 100)
i = 5
for number in range(5):
usernumber = int(input('Choose a number between 1 and 100\. You have ' + str(i) + ' guesses left. '))
if compnumber == usernumber:
print('You win!')
exit()
elif compnumber > usernumber:
print('Your number is too small!')
i = i - 1
elif compnumber < usernumber:
print('Your number is too large!')
i = i - 1
print('You're out of guesses! You lose! ')
前面的代码现在为用户提供了一些反馈。如果数字大于计算机生成的数字,用户会收到反馈'你的数字太大了!',如果用户的数字小于计算机生成的数字,那么他们会收到反馈'你的数字太小了!'。如果用户赢了,我们还使用了exit()代码。这是因为我们希望游戏在我们赢了时停止。
这给了我们一个战胜这个游戏的机会,看看现在的输出是什么样子:
Choose a number between 1 and 100\. You have 5 guesses left. 50
Your number is too small!
Choose a number between 1 and 100\. You have 4 guesses left. 75
Your number is too large!
Choose a number between 1 and 100\. You have 3 guesses left. 65
Your number is too small!
Choose a number between 1 and 100\. You have 2 guesses left. 70
Your number is too large!
Choose a number between 1 and 100\. You have 1 guesses left. 68
You win!
现在看看当我们输掉游戏时会发生什么:
Choose a number between 1 and 100\. You have 5 guesses left. 10
Your number is too small!
Choose a number between 1 and 100\. You have 4 guesses left. 40
Your number is too large!
Choose a number between 1 and 100\. You have 3 guesses left. 20
Your number is too small!
Choose a number between 1 and 100\. You have 2 guesses left. 30
Your number is too small!
Choose a number between 1 and 100\. You have 1 guesses left. 35
Your number is too large!
You're out of guesses! You lose!
正如你所看到的,你得到了一个不同的最终消息。我承认我尝试了很多次才赢得了一场比赛,所以我才得到了下面的输出,但你可以看到第二次猜测是正确的游戏:
Choose a number between 1 and 100\. You have 5 guesses left. 10
Your number is too small!
Choose a number between 1 and 100\. You have 4 guesses left. 90
You win!
我们将用最后一个算法来停止这个游戏。如果我们愿意,我们实际上可以让这个游戏变得更好,但它完成了我们需要它完成的工作。你可以考虑对你的游戏进行一些改变,如下所示:
-
添加一个选项,提醒用户已经猜过的数字。
-
增加一个选项,提醒用户忽略了先前的提示(所以如果用户给出一个太小的数字,然后给出一个更小的数字,电脑会提醒他们)。
我相信你可以尝试更多的定制。但现在,我们经历了那个问题,并遵循了我们在分析问题时应该考虑的要点:
-
我们阅读并理解了问题。
-
我们确定了目的——创建一个电脑玩家对用户玩家的猜数字游戏。
-
我们确定了问题的约束条件——数字范围、猜测次数和提供提示。
-
我们创建了一个决策流程图。
-
我们编写并建立了解决问题的算法。
-
我们看了如何创建一个简单的算法,可以迭代而不必为每个条件单独编写。
-
我们在多个点测试了算法。
-
我们验证了算法对于胜利和失败的运行是否准确。
在这里你看不到的是我在展示算法之前经历了多少错误。在写作过程中,我不得不使用前面的步骤来帮助我识别错误,检查最佳算法,并迭代程序。这是一个我们将继续使用的过程。
总结
在本章中,我们讨论了问题定义、分解和分析。我们使用问题来帮助我们经历识别问题、将其分解为相关部分和确定约束条件的过程,并分析我们的算法。我们使用流程图来帮助我们学习在设计算法时的决策过程以及如何组织思路。
我们学会了经常测试我们的算法。这为我们提供了识别错误的技能和理解,而不是等到我们有太多行代码时再等待。我们使用了一个在线商店和一个猜数字游戏来帮助我们了解 Python 中的一些功能。在整个过程中,我们使用布尔代码来验证输入,我们使用嵌套的if语句,我们学会了如何在解决所提出的问题时使用字典。
此外,我们有机会使用字典来处理使用用户输入和变量的算法。使用该算法使我们能够灵活定义一些变量,并在运行算法时或在算法内部编辑变量。
在下一章中,我们将深入探讨解决方案的过程和设计,更深入地研究更复杂的问题和 Python 语言。
3067

被折叠的 条评论
为什么被折叠?



