MIT 6.00 导论课程笔记(二)

Lecture 05

这节课主要讲述浮点数和一个根据二分法计算平方根的例子。

Python支持任意精度的整数。
如:

2 ** 1000
#注意到输出末尾有一个L标志,表示这是Long int型的数据
Out[27]: 10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376L
###########
a = 2**1000
b = 2**999
a
Out[37]: 10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376L
b
Out[38]: 5357543035931336604742125245300009052807024058527668037218751941851755255624680612465991894078479290637973364587765734125935726428461570217992288787349287401967283887412115492710537302531185570938977091076523237491790970633699383779582771973038531457285598238843271083830214915826312193418602834034688L
#注意到a/b是一个较小的整数2,然而它末尾仍有L
#这是一个“get L,stay L”的机制
a/b
Out[39]: 2L

浮点规则:IEEE 754 浮点规则

这是科学计数法的一种变体。

mantissa 翻译为尾数,在2进制中, 1mantissa<2
exponent 翻译为指数,在2进制中, 1022exponent<1023
在64bit的机器中,浮点数记为如下

symbol exponent mantissa
1 11 52

转变为10进制,大概可表述17位数的数。

课程中Johh使用的python版本和我现在使用的不一样,所以有些东西不会显示出来,在下方注释说明。

0.1
Out[28]: 0.1
#在John版本的python中0.1的输出是0.10000000000000001,原因是浮点数本身是一个近似的值,在输出时python内部调用repr()函数,该函数截取到浮点数的第17位。
#如果print 的话,结果还是0.1,因为python会自动取整
#现在的python应该是改正了这个问题
a=0.1
a
Out[30]: 0.1
a = 0.01
a
Out[32]: 0.01
1/10
Out[33]: 0
1.0/10.0
Out[34]: 0.1
#误差的累加
d = 0.0
for i in range(10):
    d += 0.1    
d
Out[44]: 0.9999999999999999
#print 自动取整
print d
1.0

对于浮点数的“==”的警告

import math
a = math.sqrt(2)
a
Out[48]: 1.4142135623730951
a*a == 2
Out[49]: False

二分法进行平方根的求解

'''这个函数有一个bug,下一讲纠正'''
def squareRootBi(x, epsilon):
    #断言语句的意思是先对输入的参数的值进行判断,如果不符合要求,报错。
    assert x >= 0, 'x must be non-negative, not '+ str(x)
    assert epsilon > 0, 'epsilon must > 0 '
    #二分法解决
    low = 0
    high = x
    gueess = ( high + low )/ 2.0
    cnt = 1
    while abs(gueess**2 - x ) > epsilon and cnt <= 100:
        if gueess**2 < x:
            low = gueess
        else:
            high = gueess
        gueess = (high+low)/2.0
        cnt += 1
    assert cnt <= 100, 'iteration count exceeded'
    print cnt, gueess
    return gueess

Lecture 06

上节课的代码其实存在着一个bug,当欲求平方根的数 x1 时,它会报错

In: squareRootBi(0.25,0.01)
Out: Traceback (most recent call last):

  File "<ipython-input-23-04d61399450e>", line 1, in <module>
    squareRootBi(0.25,0.01)

  File "C:/Users/lenovo/.spyder/temp.py", line 25, in squareRootBi
    assert cnt <= 100, 'iteration count exceeded'

AssertionError: iteration count exceeded

原因是:二分法所设定的解值区间是 0x ,而0.25的平方根是0.5,在解值区间之外,我们可以修改一行小小的代码来修正这个错误

high = max(x, 1.0)

回归性测试

regression testing,占坑,后期更新

NR法

有另一种方法计算平方根,是由牛顿/拉普森共同提出的,利用切线和导数
方法是

  • 寻找一个初始猜想点, guess
  • 找到这个 guess 关于提出等式的切线与x轴的交点值
  • 更新 guess 值。

sqrt(x)==>f(guess)=guess2x

f(guess)=0 时的解。

f(guess)=guess×2

guessi+1=guessif(guessi)2×guess

例如:
假设 guessi=3 ,那么 guessi+1=376=4.16667

注意:切线斜率为0时,与x轴无交点
代码如下:

def squareRootNR(x, epsilon):
    assert x >= 0, 'x must be non-negative, not '+ str(x)
    assert epsilon > 0, 'epsilon must > 0 '+str(epsilon)
    x = float(x)
    #guess的初始化方式可自行选择
    gueess = x/2.0
    diff = gueess**2 - x
    cnt = 1
    while abs(diff) > epsilon and cnt <= 100:
        gueess  = gueess - diff/( 2*gueess )
        diff = gueess**2 - x
        cnt += 1
    assert cnt <= 100, 'iteration count exceeded'
    print cnt, diff, gueess
    return gueess

当然,这里还有一个问题:浮点数溢出

squareRootNR(123456789,0.00001)    
squareRootBi(123456789,0.00001)
Out:
18 -1.49011611938e-08 11111.1110606
53 11111.1110606

answer can be wrong!!!

list

list(列表)和tuple(元组)的区别是

  • tuple是不可变类型,list是可变类型
    表现为list有append,remove方法,而tuple没有

John还提到了list元素的任意性,但是在我的实验中,tuple的元素也有任意性,所以python应该是改进了这方面的表示。

Lecture 07

List

关于列表由以下代码进行讲解

#创建一个列表
ivys = [1,2,3,4]
ivys
Out[25]: [1, 2, 3, 4]
#列表的元素是可以修改的,但是不能超出范围
ivys[4] = 5
Traceback (most recent call last):

  File "<ipython-input-26-b33ff9804b22>", line 1, in <module>
    ivys[4] = 5

IndexError: list assignment index out of range
#修改某元素
ivys[3]=5
#列表对象
l1=[1,2,3]
#l2指向l1指向的列表对象
l2 = l1
l2
Out[30]: [1, 2, 3]
#修改列表对象的某值,可看到l1和l2都修改了
l1[2]=4
l1
Out[32]: [1, 2, 4]
l2
Out[33]: [1, 2, 4]
ivys
Out[34]: [1, 2, 3, 5]
#这是对于基本类型的一个引用变换例子
a=1
b=a
a=2

b
Out[38]: 1
a
Out[39]: 2
#对于列表类型的引用变化类似
l1
Out[40]: [1, 2, 4]
l2
Out[41]: [1, 2, 4]
l1=[]
l1
Out[43]: []
l2
Out[44]: [1, 2, 4]

Dictionaries

字典对象如下

dic = {'a':'LuHan','b':'Kris','c':'Lay','d':'Tao'}

dic
Out[46]: {'a': 'LuHan', 'b': 'Kris', 'c': 'Lay', 'd': 'Tao'}

使用字典对象的好处是

  • 字典类型使用哈希算法进行检索,速度比列表快(列表使用线性检索方法)。

    pseudo code伪代码

    使用伪代码来理顺逻辑思路

seperate impletation from use.

效率

  • best case
  • worst case
  • expected case
    我们一般关注worst case

    Lecture 08

    本节课主要讲述算法的复杂度。随着问题规模的成长,算法的复杂度如何随之增长是我们真正关心的。

ab

以下有三种方法计算 ab

  1. ab=a×a×a×...×a
def exp1(a,b):
    ans = 1
    while(b>0):
        ans *= a
        b -= 1
    return ans

其中共有 T(b)=2+3b 次操作, T(b)O(b) ,即线性复杂度。
2. ab=a×ab1

def exp2(a,b):
    '''a recursive function'''
    if b == 1:
        return a
    else:
        print 'yyyy'
        return a*exp2(a, b-1)

其中 T(b)=3+T(b1) ,化简计算

T(b)=3k+T(bk),k{0,b}

bk=1
T(b)=3(b1)+T(1)=3b3+2=3b1

仍是线性复杂度。
3.
ab={(a2)b/2,a×(ab1), if b is even  if b is odd 

代码如下

def exp3(a,b):
    '''规模减半'''
    if b == 1:
        return a
    if (b/2)*2 == b:
        print 'yyyy'
        return exp3(a*a,b/2)
    else:
        print 'nnnn'
        return a*exp3(a,b-1)

程序的复杂度计算如下:
b 为偶数时, t(b)=6+t(b/2)
b 为奇数时,

t(b)=6+t(b1)=6+(6+t(b12))=12+t(b12)

用一个并不严谨且完全正确的等价处理,我们得到
t(b)=12+t(b/2)=12k+t(b2k)

b=2k
t(b)=12×log2b+t(1)

所以复杂度是对数级的

汉诺塔问题

有一个很著名的汉诺塔问题,问题描述是这样的

大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

这个问题用递归思想可以很容易地解决

def Towers(size,fromStack,toStack,spareStack):
    if size == 1:
        print 'put element form '+fromStack+' to '+toStack
    else:
        Towers(size-1,fromStack,spareStack,toStack)
        Towers(1,fromStack,toStack,spareStack)
        Towers(size-1,spareStack,toStack,fromStack)

那么这个问题的复杂度呢?

T(n)=1+T(1)+2T(n1)=3+2T(n1)=3(1+2+4+...+2k1)+2kT(nk)

我们发现 T(n)O(2n) ,是一个指数级问题。

有序数组检索

我们有两种方法进行数组的检索,一种是线性查找,一种是二分法查找。

def search(s,e):
    answer = None
    i = 0
    numCompared = 0
    while i < len[s] and answer == None:
        numCompared += 1
        if e == s[i]:
            answer = True
        elif e < s[i]:
            answer = False
        i += 1
    print answer, numCompared

def bisearch(s,e,first,last):
    print first, last
    if (last - first) < 2:
        return s[first] == e or s[last] == e
        mid = first + (last-first)/2
        if s[mid] == e:
            return True
        elif s[mid] > e:
            return bisearch(s,e,first,mid-1)
        else:
            return bisearch(s,e,mid+1,last)

二分法检索需要对数级时间,然而Eric提到了一个很重要的问题,就是数组在内存中的存储形式。

我们知道在内存中数组有两种存储形式,一种是线性存储,一种是链表式存储,如果是前者,那么访问到第i个元素的时间是常量的,但如果是链表,那么就是线性的访问复杂度。

猜你喜欢

转载自blog.csdn.net/sjz_hahalala479/article/details/54896869