l MIT6.00 1x (麻省理工:计算机科学和Python编程导论)
Lecture 3 - Simple Algorithms 简单算法
3.1 iteration 迭代
Iteration 迭代
• Need one more concept to be able to writeprograms of arbitrary complexity 需要、更多的概念,能够写任意复杂的程序
– Start with a test 从测试开始
– If evaluates to True, then execute loop body once, and go back toreevaluate the test 如果是真,就向下执行一些指令,循环体,接着回去
– Repeat until test evaluates to False, after which code followingiteration statement is executed 重复,直到测试结果为假,跳过循环体,直接跳转到地方的运算
An example 一个例子
This code squares the value of x by repetitiveaddition. 此代码通过重复加法对x的值进行平方。
Stepping through this code 执行代码
Some properties of iteration loops: 循环迭代的一些性质
• need to set an iteration variable outsidethe loop 在循环外需要设置迭代变量
• need to test that variable to determinewhen done 需要测试变量,以便决定在什么时候结束
• need to change that variable within theloop, in addition to other work 需要改变循环中的变量,为了导向其他工作
Iterative code 迭代代码
• Branching structures (conditionals) letus jump to different pieces of code based on a test 分支结构(条件句)让我们跳到基于测试不同的代码块
– Programs are constant time 每个程序只执行一次
• Looping structures (e.g., while) let usrepeat pieces of code until a condition is satisfied 循环结构(例如,while)让我们重复代码,直到条件满足
– Programs now take time that depends on values of variables, aswell as length of program
现在程序需要时间取决于变量的值,以及程序的长度
注:+=, -=, *=, /=代表什么?
is equivalent to 意思是等于
a += b is equivalent to a = a + b
a -= b is equivalent to a = a - b
a *= b is equivalent to a = a * b
a /= b is equivalent to a = a / b
提示:如果命令break在一个循环中被执行,它会在这个位置停止计算这个循环,然后传递控制权到下一个表达式。
3.2Guess and check algorithms 猜测和检查算法
Classes of algorithms 算法的种类
• Iterative algorithms allow us to do morecomplex things than simple arithmetic 迭代算法使我们能够比简单的算术做更复杂的事情
• We can repeat a sequence of stepsmultiple 0mes based on some decision; leads to new classes of algorithms 我们可以根据决定多次重复一些步骤,这开启了一个通往全新算法类别的大门
• One useful example are “guess and check”methods 一个非常有用例子是叫做“猜测检验的”方法
Guess and check 猜测检验
• Remember our “declarative” definition ofsquare root of x 记得我们之前的平方根的描述性的定义
• If we could guess possible values forsquare root (call it g), then can use definition to check if g*g = x 如果有人给出数字平方根的精确性猜测,我们可以用定义去检验他是否正确
• We just need a good way to generateguesses 我们所需的就是产生一个好猜测的方式
Finding a cube root of an integer 寻找整数的立方根
• One way to use this idea of generatingguesses in order to find a cube root of x is to first try 0**3, then 1**3, then2**3, and so on 我们可以利用猜测的方式,找到某数x的立方根,我们可以求1的立方根,2的立方根,3的立方根等等
• Can stop when reach k such that k**3 >x 直到k的立方比x大
• Only a finite number of cases to try 只有有限个情况需要尝试
Some code 一些代码
Extending scope 观察现象
• Only works for positive integers 这仅对正整数有效
• Easy to fix by keeping track of sign,looking for solu0on to positive case 我们可以回去并修复,然后我们可以相当直接的完成它。
Some code 一些代码
注:abs()函数表示这个数字的绝对值
Loop characteristics 循环特性
• Need a loop variable 需要一个循环变量
– Initialized outside loop 在循环的外部有一个循环变量的初始化
– Changes within loop 在循环内变化
– Test for termination depends on variable 根据该变量决定测试终止
• Useful to think about a decrementing function 对于递减函数十分有用
– Maps set of program variables into an integer 将程序变量映射到一个整数
– When loop is entered, value is non-negative 当循环输入一个值,这个值是非负的
– When value is <= 0, loop terminates, and 当这个值小于等于0时,这个循环就要停止
– Value is decreased every time through loop 每次循环中,数值都会下降
• Here we use abs(x) – ans**3 这里我们使用的是x的绝对值减ans的立方
What happens if we miss a condition? 如果我们做错了一个特性会发生什么
• Suppose we don’t initialize the variable? 假设我们没有外部初始化
• Suppose we don’t change the variableinside the loop? 假设我们在内部不变化这个变量
结论:第一个错误会导致程序不能往下执行,第二个错误会导致程序无限执行
Exhaustive enumeration 穷举法
• Guess and check methods can work onproblems with a finite number of possibilities 猜测和检验方法对有限可能的问题有效
• Exhaustive enumeration is a good way togenerate guesses in an organized manner 详尽的枚举是一个有效的方式来生成猜测的好方法
3.3 LOOP MECHANISMS 循环机制
For loops for循环
• While loops generally iterate over asequence of choices (ints in cases we have seen) 当型循环结构通常在一系列选择中进行迭代(通常,尤其是在穷举法中,一系列选择是一个按照顺序的完整序列
• Python has a specialized mechanism forthis case, called a for loop Python将会提供一种非常有用的特殊循环机制来处理这一问题,它被称为for循环
for <identifier>in<sequence> 它的形式 for<标识符>in<序列(选择的集合)>:(以冒号结尾)
:<code block> <缩进的代码块>
• Identifier bound to first value insequence 标识符最初会绑定到序列里的第一个值(即使前面绑定了别的数)
• Code block executed 运行代码块
• Identifier bound to next value 标识符会绑定到序列里的下一个值
• Code block executed 运行代码块
• Continues until sequence exhausted or a break statement is executed 重复上述执行过程,知道执行完所有的值或者遇到break语句
• To generate a sequence of integers, use 生成一个整数数列,用如下的函数
– range(n) = [0, 1, 2, 3, …, n-1]
– range(m,n) = [m, m+1, …, n-1]
注:range()函数
使用range函数的标准方法是给出一个终止值,然后range函数会从0开始直到终止值减一为止,给出一个数值列表。例如,调用range(stop)会生成如下:
>>> range(5)
[0, 1, 2, 3, 4]
然而,我们可以用一些额外的可选参数——一个初始值和一个步长——来调用range函数。你可以指定一个初始值来调用range(start,stop),像这样:
>>> range(2, 5)
[2, 3, 4]
定义一个步长,你必须知道那个一个初始值——调用命令为range(start,stop,stepsize),像这样:
>>> range(2, 10, 2)
[2, 4, 6, 8]
注:range()最后一个参数也可以使用负数,此时表示每次都减相应的步长,这时三个参数中第一个数应该比第二个数大
A cleaned up cube root finder 整理后的求立方根代码
3.4 FLOATING POINT ACCURACY 浮点精度
Dealing with floats 浮点数
• Floats approximate real numbers, butuseful to understand how 浮点数近似于真实的数,但是我们需要理解他是啥样的
• Decimal number: 十进制数
– 302 = 3*10**2 + 0*10**1 + 2*10**0
• Binary number 二进制数
– 10011 = 1*2**4 + 0*2**3 + 0*2**2 + 1*2**1 + 1*2**0
– (which in decimal is 16 + 2 + 1 = 19) 十进制数就是19
• Internally, computer represents numbersin binary 计算机内部使用2进制数来表示数
Remember: ** is Python’s exponentiationoperator **在Python中是乘方的意思
Converting decimal integer to binary 将十进制数转化为二进制
• Consider example of 举个例子
– x = 1*2**4 + 0*2**3 + 0*2**2 + 1*2**1 + 1*2**0
• If we take remainder relative to 2 (x%2)of this number, that gives us the last binary bit 如果我们得到x除2的余数,它将告诉我们作为2进制的最后一位
• If we then divide x by 2 (x/2), all thebits get shifted left 再除以2,得到的余数是再往前一位的数值
– x/2 = 1*2**3 + 0*2**2 + 0*2**1 + 1*2**0 = 1001
• Keep doing successive divisions; nowremainder gets next bit, and so on 一步步往下除
• Let’s convert to binary form 我们就可以把十进制数转化为二进制
Doing this in Python 在python中写一下
注:Python3中整数相除得出来的是浮点数!!!!!!!!
要记得再用一下int()函数!
So what about fractions? 那么小数呢
• 3/8 = 0.375 = 3*10**(-1) + 7*10**(-2) +5*10**(-3)
• So if we multiply by a power of 2 bigenough to convert into a whole number, can then convert to binary, then divideby the same power of 2 假设我们找到一个足够大的2的次幂,我们用这个小数乘它,会得到一个整数。然后我们就可以用刚才的方法了
• 0.375 * (2**3) = 3 (decimal) (十进制)
• Convert 3 to binary (now 11) 把3转化成二进制11
• Divide by 2**3 (shift left) to get 0.011(binary) 11除以2**3或者移位就可以得到0.011
倒数第三句 我用的是result=”0.”+result[-p:]
Some implications 启示
• If there is no integer p such thatx*(2**p) is a whole number, then internal representation is always anapproximation 如果没有整数p使得x*(2**p)是个是个整数,那么内部代表将一直是一个近似值
• Suggest that testing equality of floatsis not exact 结论是浮点数的相等时不精确的
– Use abs(x-y) < 0.0001, rather than x == y
• Why does print(0.1) return 0.1, if notexact? 为什么输入0.1就返回0.1,它不是不精确的么
– Because Python designers set it up this way to automatically round因为Python设计师是这样设计的
注:在机器内部!0.1不一定就是精确地0.1
3.5 APPROXIMATION METHODS 近似方法(穷举法)
Approximate solutions 近似解
• Suppose we now want to find the squareroot of any non-negative number? 假设我们想找到任何非负数的平方根
• Can’t guarantee exact answer, but justlook for something close enough 不需要确保得到准确的答案,只求近似解
• Start with exhaustive enumeration 从详尽的枚举开始
– Take small steps to generate guesses in order 采取小步骤,按顺序产生所有猜测
– Check to see if close enough 检查是否足够接近
Example code 举例代码
Some observations 一些观察
• Step could be any small number 步长可以是任意小的数
– If too small, takes a long time to find square root 如果他太小的话,我们将会花费较长的时间找到平方根
– If make too large, might skip over answer without getting closeenough 如果太大的话,我们可能回错过接近的解,(步数太大精度太小)
• In general, will take x/step timesthrough code to find solution 总的来说,它将花费x/步长 的时间在寻找答案
• Need a more efficient way to do this 需要一个更有效的方法来做这件事
3.6 BISECTION SEARCH 二分法查找
Bisection search 二分法查找
• We know that the square root of x liesbetween 0 and x, from mathematics 从数学上我们知道x的平方根介于0和x之间
• Rather than exhaustively trying thingsstarting at 0, suppose instead we pick a number in the middle of this range 不同于穷举法我们从0开始穷举,二分法让我们从范围的中间中选择一个数
• If we are lucky, this answer is closeenough 如果我们幸运,g就足够接近平方根
• If not close enough, is guess too big ortoo small? 如果不接近的话,猜测是大是小啊
• If g**2 > x, then know g is too big;but now search 如果g方大于x,则g偏大
• And if this new g is, for example, g**2< x, then know too small; so now search new g方小于x
• At each stage, reduce range of values tosearch by half 每个阶段,范围都减小一半
Example of square root 求平方根的例子
Some observations 一些观察
• Bisection search radically reducescomputation time 二分查找从根本上减少了计算的次数
– being smart about generating guesses is important 聪明的猜测是很智能的
• Should work well on problems with“ordering” property 它非常适用于拥有排序属性的问题
– value of function being solved varies monotonically with inputvalue 待求得数值仅随输入值变化
– Here function is g**2; which grows as g grows 这里g的平方仅随g的增长而增长
3.7 NEWTON-RAPHSON ROOT FINDING牛顿-拉夫逊多项式求根
Newton-Raphson 牛顿-拉夫逊
• General approximation algorithm to findroots of a polynomial in one variable 在一个变量中找到多项式的根的一般逼近算法
p(x)= anxn + an-1xn-1 + … + a1x+ a0
• Want to find r such that p(r) = 0 我们希望找到一个r是p(r)=0
• For example, to find the square root of24, find the root of p(x) = x2 – 24 举个例子,我们想找到24的平方根仅需使p(x) = x2 – 24
• Newton showed that if g is an approximationto the root, then 牛顿发现 如果g是一个近似的根,则
g –p(g)/p’(g)
is a better approximation; where p’ isderivative of p 是一个更好的近似值,p’是p的导数
• Simple case: cx2 + k 简单的案例
• First derivative: 2cx 一阶导数
• So if polynomial is x2 + k,then derivative is 2x 所以如果多项式是x2+k,导数是2x
• Newton-Raphson says given a guess g forroot, a better guess is g – (g2 –k)/2g 这个算法说给你要给出猜测的解g,则g – (g2 –k)/2g是更近似的解
• This gives us another way of generatingguesses, which we can check; very efficient 这给你我们一种新的产生猜测的方式,而且这个更有效
Iterative algorithms 迭代算法
• Guess and check methods build on reusingsame code 我们使用相同重复的代码建立猜测和检查方法
– Use a looping construct to generate guesses, then check and continue 我们使用循环的方法产生猜测,然后检查,然后继续
• Generating guesses 产生猜测(三种方法)
– Exhaustive enumeration 穷举法
– Bisection search 二分法
– Newton-Raphson (for root finding) 牛顿-拉夫逊法(找多项式的根)
注:Python中 / 这个符号可以用来把太长的一行分割成两行
总结
介绍了迭代的概念,,学习了猜测检验方法,引出了循环结构,还介绍了产生猜测的三种方法,穷举法,二分法,牛顿-拉夫逊法