1695_week2_算法与函数(MIT使用Python编程学习手记)

news/2024/10/30 1:48:09/

全部学习汇总: GreyZhang/python_basic: My learning notes about python. (github.com)

首先说明一下,这部分信息的整理只是我个人的理解。由于自己的知识功底以及英语水准,很可能会有大量的疏漏。再此,我只想把自己学习时候的一点收获整理分享一下。如果能够给别人一点点帮助,荣幸之至。如果有错误,也请予以斧正。如果感觉我有描述不清的,当然也可以跟我邮件交流。由于直接复制代码排版上会有很多问题,稳文中代码全都截取VIM编辑器的界面展示。如果需要代码可以给我发邮件索取,以下是我的联系方式:

Email : greyzhang@126.com

这部分学习需要有一定的Python学习基础了,需要掌握的概念大致有以下几个:

Python的语法、Python的数据类型(整形、浮点、元组、列表、字典等)。有一个数据类型需要单独拿出来强调一下,那就是布尔量。接下来的很多判断或者程序分支的概念会建立在最一个布尔量的判断基础上。

在之前的学习手记中已经总结了一部分Python的语句操作,我们可以通过在Python shell中年输入一系列的语句来实现某个功能。看起来,我们是让计算机运行了我们的软件功能,但是功能肯定是远远不够。

迭代

在程序设计中,一个很重要的算法概念便是迭代。通过迭代,我们可以反复重用同一段代码来解决我们的问题。通过程序语言对我们需要处理的信息进行描述,然后再迭代的程序段中反复执行我们写好的一段程序,在执行的过程中通过对布尔量的查看判断来确认我们是否找到一个合适的结果。这是对“猜想——验证”的计算机思考方法的一种实践尝试。

接下来通过一个简单的小例子来说明一下:通过Python实现求解一个正整数点额平方。

代码如下:

这里我们用到了while循环,while循环最简单的结构如下:

While <判断条件>:

循环体

以上程序的执行结果:

>>> ================================ RESTART ================================

>>>

3*3 = 9

如果改变x的数值,可以得到你想要的不同结果。比如改成12345,执行结果如下:

>>> ================================ RESTART ================================

>>>

12345*12345 = 152399025

虽说此处还谈不上代码的优化,使用Python可以有更好的实现方法。但是可以看出的式程序执行速度还是很快的,一瞬间就可以得出你想要的结果。

猜想——验证

“猜想——验证”是计算机思维的一种常用的方法,我们假定一系列的可能性然后一次次验证,最终判断是否有我们想要的结果。

通过一个简单的例子来说明一下:寻找一个整数是否存在整数立方根。

我们可以从0开始,直到这个数本身为止一次次查看猜测数值的三次方是否等于给出的整数。如果找到那就寻找到了这个数的整数立方根,如果找不到,那这个数肯定是没有整数立方根。

设计代码如下:

执行结果如下:

>>> ================================ RESTART ================================

>>>

input a number:123

can't find %d's cube root

>>> ================================ RESTART ================================

>>>

input a number:27

cube root of 27 is: 3

通过以上的例子可以看出“猜想——验证”方法的大致步骤:

1,给出初始的猜想

2,在循环的结构中不断验证,如果满足条件则终止,因为找到了我们要的答案。如果不满足验证条件,那么在循环结构中改变猜想再次验证。

常用迭代的特点:

通过以上几个例子大致可以总结出常用迭代程序的特点:

1,有一个状态变量

a,在循环体外进行初始化

b,在循环体中改变状态量

c,判断验证的依据来自于状态量的变化

2,状态量递减:

A,初始化的数值为整数

B,执行的时候整数非负

C,<=0的时候结束

D,在循环中递减

以上特点并不是所有迭代程序的特征,但是是常用迭代程序一般都有的特点。如果满足以上特点的迭代程序,我们可以通过看状态量的数值来弄清楚我们程序的执行度。由于规律递减的存在,我们很清楚就能够知道到底还有多少没有执行完。

For循环:

While循环通常可以用来对一系列信息进行验证,如同我们前面看到的数字。在Python中,对这种情况的处理还有另外一种特殊的方式叫做for循环。

For循环的结构入下:

For <判断条件> :

循环体

在for循环中,循环的状态量首先被绑定为序列中的第一个元素,然后执行循环体程序。循环体执行结束后,状态量绑定为第二个元素,执行循环体程序,一直这样执行下去。如果遇到break,循环会直接终止。

如果想使用一系列连续的数字序列,可能会用到Python的内置函数range(n)。这个函数可以用来产生0到n-1的连续数字序列。函数是重载的,针对不同的需要也可以有更加方便的使用方式。

下面使用for循环实现上面的cube root功能:

执行结果如下:

>>> ================================ RESTART ================================

>>>

input a number:27

cube root of 27 is 3

>>> ================================ RESTART ================================

>>>

input a number:-27

cube root of -27 is -3

>>> ================================ RESTART ================================

>>>

input a number:123

123 is not a perfect cube

以上代码不仅完成了与之前while循环相同的功能,而且同时支持负数的判断。

浮点数的处理;

首先看一下十进制的表示:

302 = 3*10**2 + 0*10**1 + 2*10**0

为什么我们的祖先首先采用的是十进制的计数方式呢?那是因为我们有十根手指头,但是计算机没这个。计算机知道的就只有开或者关两种状态,只知道0或者1。

这样,用二进制表示一个十进制的例子如下:

10011 = 1*2**4 + 0*2**3 + 0*2**2 + 1*2**1 + 1*2**0

(用十进制表示是19 = 16 + 2 + 1)

在计算机的内部,数字是以二进制的形式才存储的。考虑一下10011除以2后的表达方式,实际上可以表示为:1*2**3 + 0*2**2 + 0*2**1 + 1*2**0。如果在计算机中进行除以2的操作,实际上是把二进制的数值向右移动了一位。

把这思考的一切用Python处理展示出来,可以设计如下的代码:

执行效果如下:

>>> ================================ RESTART ================================

>>>

input a number:19

10011

>>> ================================ RESTART ================================

>>>

input a number:-19

-10011

如果考虑到分数会是怎么样呢?

3/8 = 0.375 = 3*10**(--1) + 7*10**(-2)+5*10**(-3)

0.375 * (2 ** 3) = 3(十进制)

转换为二进制:11

除以(2**3),也就是右移三位:0.011(二进制)

设计代码如下:

代码执行结果如下:

>>> ================================ RESTART ================================

>>>

Enter a decimal number between 0 and 1: .375

Remainder = 0.375

Remainder = 0.75

Remainder = 0.5

011

.011

The binary representation of the decimal 0.375 is .011

>>> ================================ RESTART ================================

>>>

Enter a decimal number between 0 and 1: .1

Remainder = 0.1

Remainder = 0.2

Remainder = 0.4

Remainder = 0.8

Remainder = 0.6

Remainder = 0.2

Remainder = 0.4

Remainder = 0.8

Remainder = 0.6

Remainder = 0.2

Remainder = 0.4

Remainder = 0.8

Remainder = 0.6

Remainder = 0.2

Remainder = 0.4

Remainder = 0.8

Remainder = 0.6

Remainder = 0.200000000001

Remainder = 0.400000000001

Remainder = 0.800000000003

Remainder = 0.600000000006

Remainder = 0.200000000012

Remainder = 0.400000000023

Remainder = 0.800000000047

Remainder = 0.600000000093

Remainder = 0.200000000186

Remainder = 0.400000000373

Remainder = 0.800000000745

Remainder = 0.60000000149

Remainder = 0.20000000298

Remainder = 0.40000000596

Remainder = 0.800000011921

Remainder = 0.600000023842

Remainder = 0.200000047684

Remainder = 0.400000095367

Remainder = 0.800000190735

Remainder = 0.60000038147

Remainder = 0.200000762939

Remainder = 0.400001525879

Remainder = 0.800003051758

Remainder = 0.600006103516

Remainder = 0.200012207031

Remainder = 0.400024414062

Remainder = 0.800048828125

Remainder = 0.60009765625

Remainder = 0.2001953125

Remainder = 0.400390625

Remainder = 0.80078125

Remainder = 0.6015625

Remainder = 0.203125

Remainder = 0.40625

Remainder = 0.8125

Remainder = 0.625

Remainder = 0.25

Remainder = 0.5

0001100110011001100110011001100110011001100110011001101

.0001100110011001100110011001100110011001100110011001101

The binary representation of the decimal 0.1 is .0001100110011001100110011001100110011001100110011001101

关于不能够表示为num/(2 ** n)的这种形式,判断结束条件当然不能够以能够整除为条件。因为数据的存储在计算机中的位数是有限制的。在一定精度内,这种判断可以使用一个比较小的精度值与之比较作为结束的判断条件。

常用的算法:

逐次逼近

1,选择合适的步长

太小计算时间长

太大可能计算不出结果

2,给出一个最小精度作为判断条件

二分查找

1,选择中间数

判断

根据判断决定是否再次二分查找

结束条件:达到允许精度

2,特点

缩短周期

更加智能化

适合处理有序序列

牛顿-莱布尼茨

1,g - p(g)/p’(g)是多项式方程的猜想更优解

2,特点:

高效

需要数学处理作为前提

求解平方根:

逐次逼近法:

执行结果:

>>> ================================ RESTART ================================

>>>

root of number 25.000000 is 4.999000

>>> ================================ RESTART ================================

>>>

root of number 24.000000 is 4.897959

结果还算是精确,由于步长选择比较小运算时间较长。

二分查找:

执行结果:

>>> ================================ RESTART ================================

>>>

12.5

6.25

3.125

4.6875

5.46875

5.078125

4.8828125

4.98046875

5.029296875

5.0048828125

4.99267578125

4.99877929688

5.00183105469

5.00030517578

5.00030517578

牛顿-莱布尼茨:

执行结果:

>>> ================================ RESTART ================================

>>>

13.0

7.46153846154

5.40602696273

5.01524760194

5.00002317825

5.00002317825

尝试做一个复杂运算,求12345的平方根:

关于迭代的概括:

1,用相同的方法反复验证>>> ================================ RESTART ================================

>>>

6173.0

3087.499919

1545.74914984

776.867784296

396.37925959

213.761837095

135.756512451

113.345688237

111.130142813

111.108057708

111.108057708

11次迭代便得出一个很好的解,足以看出算法优秀的重要性。如果采用逐次逼近,这将会是很大的工作量。

2,猜测的方式可以灵活使用多种算法。

函数:

关于函数,解决的问题不仅仅是功能的重用还有一个功能的抽象。函数可以把一个功能抽象出来,让其他人在不需要考虑函数的实现细节的基础上使用它。这种抽象可以称之为黑盒抽象,这不仅让代码重用变得更加简单也让代码的调试变得更加容易。

能够开始学习函数的设计,需要有基本的Python语言知识以及循环结构的了解,这些之前已经整理。之前的循环结构的确是能够在一定程度上解决代码重用的问题,但是解决的不够完美,如果我们需要重复多个循环那我们需要把整个循环体代码拷贝多次。这也带来了另外一个问题——变量的管理以及代码修改的困难。很可能我们使用的多个拷贝代码段中的变量会相互影响,当需要改变某一个语句的时候我们也可能需要修改多次。函数在使用上则要比这方便的多。

下面在举例演示一下函数的使用,通过一个简单的函数实现求两个数值中的最大值。

代码如下:

在IDLE中load之后,运行效果如下:

>>> ================================ RESTART ================================

>>>

>>> MaxNum(223,112)

223

>>> MaxNum(-6,-10)

-6

>>> x = 1

>>> y = 2

>>> MaxNum(123,456)

456

>>> MaxNum(x,y)

2

上面不仅能够看到代码可以重用,结果不受上次调用的影响,而且能够看到在IDLE环境中的x,y的赋值不会影响到函数中的x和y的数值。这是因为函数有着自己独立的变量空间,而且互不影响。关于每次调用的互不影响,递归的算法可以很好地说明,这个后面有时间在做整理。关于独立的变量空间,可以把上面的代码修改一下,如下:

LOAD到IDLE中测试使用如下:

>>> ================================ RESTART ================================

>>>

>>> MaxNum(123,321)

321

>>> MinNum(123,456)

123

>>> x = 1

>>> AddX(x)

3

通过以上例子,可以很好地说明函数的参数的特点。在其他的语言中,参数可能会更加准确的描述为形参。

函数运行的机理与步骤总结:

1,获取参数值

2,把参数值传入表达式中并执行运算

3,返回。如果存在return语句,那么函数执行结束前会返回return语句操作的对象。如果没有,那么函数返回none。

4,形参与传入的参数值脱离关系。

形参相关总结:

1,每次函数调用都会创建自己独立的局部变量

2,全局变量与局部变量不会冲突

3,同样的形参名字可以存在于不同的函数而且彼此不会冲突

关于函数的调试:

以函数的方式进行程序的设计时,输入输出得到了很好地控制。如果函数最终的执行不是我们想要的结果,通常比较好的一种方式是在函数中间加入print语句来查看中间部分变量的变化。这部分具体的描述在之前也已经整理,在博客中也可以找得到。

另一个非常重要的注意点是:在你不知道到底是什么原因导致错误之前,不要随便改动代码。

函数的引用:

如果把一个或者几个函数写到了一个文件,那么这个文件就被称为一个模块。引用这个模块的函数的方法是使用import表达式,通常有三种使用的方式。

方式一: import block

这种方式在使用block模块中的函数fun时需要以以下方式调用:

Block.fun(参数)

方式二:from block import fun as my_fun

这样,我们在自己的代码中可以使用my_fun(参数)的方式来完成方式一的功能。

方式三:from block import *

使用这种方式,可以加模块名直接使用fun(参数)的形式。

需要注意的是,import不仅仅引入模块中的函数同时也引入了模块中的全局量。如果在之前的环境中有与之重名的全局量会被其覆盖,这是需要注意的事情。

想做一个简单的学习总结,没想到看似简单的东西内容确实不少。好的是我这是在整理自己的学习手记,要是真的做为提供给别人的一份教程,那我的工作量真得能够把自己吓住了。最后,祝过路人学习进步、事业有成!


http://www.ppmy.cn/news/60363.html

相关文章

从零实现深度学习框架——常见学习率调整策略原理与实现

引言 本着“凡我不能创造的&#xff0c;我就不能理解”的思想&#xff0c;本系列文章会基于纯Python以及NumPy从零创建自己的深度学习框架&#xff0c;该框架类似PyTorch能实现自动求导。 &#x1f4a1;系列文章完整目录&#xff1a; &#x1f449;点此&#x1f448; 要深入理解…

动态gif图片如何在线做?轻松实现图片在线生成gif

常见的jpg、png格式的静态图片想要变成gif格式的动态图片时&#xff0c;要怎么办呢&#xff1f;有没有什么简单实用的gif制作工具呢&#xff1f; 一、什么工具能够在线制作gif&#xff1f; GIF中文网作为一款专业的gif制作&#xff08;https://www.gif.cn/&#xff09;工具&a…

Snmputil和Snmputilg工具的下载和基本使用 SNMP协议 Windows系统SNMP服务的安装教程

⬜⬜⬜ ---&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; (*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;---⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &#x1f381;欢迎各位→…

某程序员哀叹:二本计算机,4年开发,年包才40多万。二本真的不如985/211吗?

前段时间&#xff0c;某职场论坛上有人发了一个帖子&#xff0c;发帖人问&#xff1a;为什么大家工资那么高&#xff0c;三五年都六七十万了&#xff1f;我二本计算机专业&#xff0c;四年前端开发&#xff0c;找个年包40万多点就顶头了。 原贴如下&#xff1a; 有网友表示楼主…

第四章 数据关联分析方法

基本概念和方法 关联规则和算法应用 基本概念和术语 关联规则算法应用&#xff1a; 一个关联规则分析的例子—————超市购物篮分析 不要看 后面数字看不懂 项集&#xff1a;是指项的集合。包含k个项的项集称为k-项集 支持度&#xff1a;若A是一个项集&#xff0c;则A的…

Golang中接口类型详解与最佳实践(二)

之前的文章《Golang中的interface(接口)详解与最佳实践》详细介绍了接口类型的定义、使用方法和最佳实践。接口类型使得编写可扩展、可维护和可复用的高质量代码变得更加容易。 如何判断是否实现了某个接口&#xff1f; 还是使用之前文章的例子&#xff0c;例如声明了如下一个…

JS基础知识

1.延迟加载JS 在script标签上添加async或者defer <script defer type"text/javascript" src"script.js"></script> defer:等html全都解析完成&#xff0c;顺次执行js脚本 async:js谁先加载完谁先运行 2.JS数据类型有哪些&#xff1f; 基本数…

建筑专业可以转行学云计算吗?

当然可行。 在过去的几年中&#xff0c;我们已经帮助很多建筑土木工程专业的同学转行学习云计算技术&#xff0c;尤其是在建筑信息化编程方向。近年来&#xff0c;云计算行业持续发展&#xff0c;涉及到众多领域&#xff0c;如云数据中心、云安全、云存储、云计算机服务等。云…