python开三次方_python小结3

继续开根号:

>>> def findRoot1(x, power, epsilon):

low = 0

high = x

ans = (high + low) / 2.0

while abs(ans**power - x) > epsilon:

if ans**power < x:

low = ans

else:

high = ans

ans = (high + low) / 2.0

return ans

>>> findRoot1(25.0, 2, 0.001)

4.9999237060546875

>>> findRoot1(27, 3, 0.001)

2.999988555908203

>>> findRoot1(-27.0, 3, 0.001)

KeyboardInterrupt

注意这里开-27的立方根的时候,程序陷入无限循环。这里如果在while下面增加一个print (ans)语句,可以看到程序运行的过程:

>>> findRoot1(-27.0, 3, 0.001)

-13.5

-20.25

-23.625

-25.3125

-26.15625

-26.578125

-26.7890625

-26.89453125

-26.947265625

可以看到,程序用二分法选取中间值猜测后,发现中间值明显小于-27,根据程序,此时,low = ans,但这里的low原本为0,在负数区域中,0并非最低值,反而是最高值。而这里的high = x,反而是最低值。也就是说,本来因为猜测的中间值小于-27,因此通过二分法选择更大的数值,结果却选择了更小的一段数值,在这个区间内不断二分,自然无法找到近似值。因此这个程序只能用于正数的开根号,而不能用于负数。

修复这个bug其实很简单。这个bug的根源在于,我们先入为主的认为

equation?tex=x 为high值,0为low值。但

equation?tex=x 可能是负数,因此需要判定哪个是最高值:

>>> def findRoot2(x, power, epsilon):

low = min(0, x)

high = max(0,x)

ans = (high + low) / 2.0

while abs(ans**power - x) > epsilon:

if ans**power < x:

low = ans

else:

high = ans

ans = (high + low) / 2.0

return ans

>>> findRoot2(-27.0, 3, 0.001)

-2.999988555908203

>>> findRoot2(0.25, 2, 0.001)

KeyboardInterrupt

但是这个改良版在求0.25的平方根的时候,又出现了新的问题。

equation?tex=%5Csqrt%7B0.25%7D%3D0.5 ,然而0.5是在这个二分法算法搜寻范围之外,因为程序确定的范围是

equation?tex=%280%2C0.25%29 。所以,程序陷入无限循环。正确的做法是,应当将搜寻范围确定在

equation?tex=%280%2C1%29 之间。但是且慢,如果是开

equation?tex=-0.27 的三次方根呢?那就是在

equation?tex=%28-1%2C0%29 之间了。

二次优化版本其实也很简单且巧妙,只需将边界从0扩展到1或者-1即可:

>>> def findRoot3(x, power, epsilon):

low = min(-1, x)

high = max(1,x)

ans = (high + low) / 2.0

while abs(ans**power - x) > epsilon:

if ans**power < x:

low = ans

else:

high = ans

ans = (high + low) / 2.0

return ans

>>> findRoot3(0.25, 2, 0.001)

0.5

>>> findRoot3(-27, 3, 0.001)

-2.9999847412109375

>>> findRoot3(25, 2, 0.001)

4.9999237060546875

Using functions in modules:Modularity suggests grouping functions together that share common theme

Place i a single .py file

Use import command to access

将如下代码集成在一个名为circle.py文件中:

pi = 3.14159

def area(radius):

return pi * (radius**2)

def circumference(radius):

return 2 * pi * radius

在python中,import这个文件:

>>> import circle

>>> circle.pi

3.14159

>>> circle.area(1)

3.14159

>>> circle.circumference(1)

6.28318

也可以这样进入:

>>> from circle import *

>>> pi

3.14159

>>> area(1)

3.14159

>>> circumference(1)

6.28318

这里需要注意的是:

>>> pi = 0

>>> pi

0

>>> area(1)

3.14159

从“python小结2”的loop开始,就可以有iteration,还可以有recursion。

>>> def iterMul(a,b):

result = 0

while b>0:

result += a

b -= 1

return result

>>> iterMul(3,4)

12

>>> def recurMul(a,b):

if b == 1: # base case

return a

else:

return a + recurMul(a, b-1)

>>> recurMul(3,4)

12

对比这两个代码,就可以知道,为什么iteration用的是while-loop,而recursion用的是for-loop。recursion需要用到base case,因此每次recursion,都需要判定是否属于base case,那么用if...else...就很合适。而iteration并不需要额外的判定,因此用while更简洁明了。

recursion的背后是数学里面的一种证明方法,就是proof by induction。Base case: we can show that recurMul() must return correct answer.

For recursive case, we can assume that recurMul() correctly returns an answer for problems of size smaller than

equation?tex=b , then by the addition step, it must also return a correct answer for problems of size

equation?tex=b .

对第二点的inductive step需要进一步说明,原文有点拗口。这里的意思是,假设

equation?tex=P%28n%29 为:recurMul(a, b-1)得到的答案是正确的。那么需要证明的是,

equation?tex=P%28n%2B1%29 是正确的。但这里的

equation?tex=P%28n%2B1%29 其实就是这一步:

return a + recurMul(a, b-1)

equation?tex=P%28n%29 的基础上,即在假设recurMul(a, b-1)会返回正确值后,显然

equation?tex=P%28n%2B1%29 是正确的。

经典recursive problems:Factorial (

equation?tex=n%21 )

以下是iteration和recursion两个版本的对比:

>>> recurMul(3,4)

12

>>> def facIter(n):

res = 1

while n>1:

res = res*n

n -= 1

return res

>>> facIter(5)

120

>>> def facRecur(n):

if n == 1:

return n

return n*facRecur(n-1)

>>> facRecur(5)

120

说明:对iteration,最好的理解就是牛顿近似法,两者都是在原有的基础上不断逼近目标值,因此,牛顿近似法自然选择iteration的程序设计。而对于牛顿近似法的理解,最为直观的就是看图(见“python小结2”),从

equation?tex=a_1%2Ca_2%2C... 不断逼近

equation?tex=x_0

对于recursion,深入理解的话就是背后的数学思想,proof by induction。而直观的把握的话,一个是base case,一个是把问题简化一步,然后不断的recurse,直到base case。

汉诺塔

def Towers(size,fromStack,toStack,spareStack):

if size == 1:

print ('Move disk from ', fromStack, 'to ', toStack)

else:

Towers(size-1,fromStack,spareStack,toStack)

Towers(1,fromStack,toStack,spareStack)

Towers(size-1,spareStack,toStack,fromStack)

汉诺塔只能用recursion,无法用iteration。直观的理解这个代码,if语句下的代码,就是base case;而else语句下,就是把问题简化一步,然后不断递归。具体的说,就是先把size-1个盘子移到spareStack上,然后把最下面的大盘子移到toStack,然后再把size-1个盘子移到toStack上。非常的直观。

但按照我第一次的做法,试图去拆开理解程序每一个步骤,那就非常复杂了。原因是,汉诺塔不像阶乘,每次递归都只是返回一个数字。汉诺塔每次递归,返回的是一系列的操作步骤。而且这个操作步骤,随着盘子数量的增加,步骤数量几乎是指数级上升的。这也正是递归思想独特的魅力所在,通过汉诺塔展现的淋漓尽致。

也就是说,对于汉诺塔这个程序的理解和把握,首先要充分理解递归的思想,建立起一种新的直觉式理解,就是我前面说的,把问题缩小一步,之后不断循环。那么为什么这种循环可以实现呢?那就需要通过数学上的proof by induction方法来证明,这样才算搞明白。而试图通过拆分每个程序运行步骤,反而是不理解递归的表现。在汉诺塔代码中,实际上也不可能做到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值