您的位置:首页 > 其它

MIT 6.00 导论课程笔记(三)

2017-02-14 20:29 316 查看

Lecture 09

二分法

实际上是重复了 Lecture 8 的内容。

Lisp语言存储列表是使用了box pointer diagram,使用的是链表,每一个盒子有两个指针,一个指向下一个位置,一个指向值对象。这种存储链表的方式如果要找到第i个列表元素的话,需要按顺序一个一个的来,即线性查找时间。

Python和Fortran语言改进了这一问题,采用了另一种方式来存储列表。使用一整块来存储数据,每一块中每一个格子都有一个指针,指针指向值对象。

这两种的区别在于Python的方法省去了指向下一个指针,直接分配一整块内存给链表。这两种方法各有优劣,都是对访问时间和空间权衡之后得出的决策。

二分法隐藏的通用思想

Pick the mid point

Check to see if this is answer

If not, reduce to small problem repeat

我们应该在search之前sort吗?

搜索无序表时,复杂度为n。

搜索有序表时,复杂度最快为nlogn。

然而,如果我们要搜索 k 次呢?

搜索无序表时,复杂度为kn

4000

搜索有序表时,复杂度为nlogn+klogn

代码

#冒泡排序
def bubbleSort1(L):
for j in range(len(L)):
for i in range(len(L)-1):
if L[i] > L[i+1]:
temp = L[i]
L[i] = L[i+1]
L[i+1] = temp
print L
print '*******'

def bubbleSort(L):
swapped = True
while swapped:
swapped = False
for i in range(len(L)-1):
if L[i]>L[i+1]:
temp = L[i]
L[i] = L[i+1]
L[i+1] = temp
swapped = True
print L
print '********'
#选择排序
def selectionSort(L):
for i in range(len(L)):
minIndex = i
minVal = L[i]
for j in range(i,len(L)):
if L[j] < minVal:
minIndex = j
minVal = L[j]
L[minIndex] = L[i]
L[i] = minVal
print L


Lecture 10

分治法

分治算法概要步骤

将问题分割为同类型的子问题

单独解决这些子问题

联合这些解决办法

归并排序

归并排序是分治法的一个重要例子

将列表分割成一半

继续分割直到每个列表都有一个元素

归并这些子列表

def merge(left, right):
result = []
i,j = 0,0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i = i+1
else:
result.append(right[j])
j = j + 1
while i < len(left):
result.append(left[i])
i = i + 1
while j < len(right):
result.append(right[j])
j = j+1
return result
def mergeSort(L):
if len(L)<2:
return L[:]
else:
middle = len(L)/2
left = mergeSort(L[:middle])
right = mergeSort(L[middle:])
together = merge(left,right)
print 'merged',together
return together


哈希

哈希算法的意思是给列表中的每个元素一个对应的索引值。

给时间以空间

要创建一个完全平均的哈希算法是困难的(完全平均的意思是列表中每个元素被检索到的时间都是一样的)

代码示例

#这是一个整数对应整数索引的哈希算法,哈希表为small-large
def create(smallest,largest):
intSet = []
for i in range(smallest,largest+1):
intSet.append(None)
return intSet

def insert(intSet,e):
intSet[e] = 1

def member(intSet,e):
return intSet[e] == 1
#这是一个字母对应整数的哈希算法,利用计算机内部的ascii创建
def hashChar(e):
return ord(e)
def cSetCreate():
cSet = []
for i in range(0,255):
cSet.append(None)
return cSet
def cSetInsert(cSet,e):
cSet[hashChar(e)] = 1
def cSetMember(cSet,e):
return cSet[hashChar(e)] == 1


Exception异常

这是python另一个语法机制。

可以分为

Unhandled exception

Handled exception

代码示例

#try...except被叫做tri-accept block
def readFloat(requestMsg,errMsg):
'''test exception'''
while True:
val = raw_input(requestMsg)
try:
val = float(val)
return val
except:
print errMsg


区分异常(Exception)和断言(Assert)

我的理解是:

断言是为了防止用户输出一些不符要求的值而强行检查退出;

异常则是主要防止程序真正运行时一些乱七八糟的错误。

Lecture 11

这节课几乎是纯理论的东西,讲述的是测试和调试。

验证(Validation):是隐藏问题和增加自信心的过程,是测试(testing)和寻因(reasoning)的集合。

调试:定位并解决程序失败的问题。

防卫性程式设计

Testing

测试:主要是考察输入/输出的表现

主要分为单元测试集成测试

测试集的选取要合理,要大到可以给予我们信心,也要小到时间合理。

bug

关于bug有两条西式的谬论

bugs crawl in programs

bugs breed

John认为有两条非常好的调试工具,一个是print语句,另一个是代码阅读。

关于调试,还有一个重要思想:系统化的思想。

如何系统化?

研究程序文本

研究如何挽回

寻找生成此项错误的最简输入

二分法查找可能出错的地方

代码示例:

def silly():
res=[]
done=False
while not done:
elem = raw_input('Enter element, return when done')
if elem == '':
done = True
else:
res.append(elem)
#二分法第一步:选择中间部分输出查看,发现前面没问题
#print 'res  ',res
#定位问题,‘=’将前后引用指向了统一个对象
tmp = res
#如下代码进行解决
#tmp = res[:]
tmp.reverse()
print 'res  ',res,'  tmp  ',tmp
#第二步,在后面的中间进行print,发现问题
#查到bug所在,bug的原因是tmp和res指向同一个对象
isPal = (res == tmp)
if isPal:
print 'is palindrome'
else:
print 'not palindrome'


Lecture 12

关于调试

编程者经常犯的小错误有:

自变量的顺序错误

拼写错误

初始化错误

Object vs Values

假名错误

副作用

我们需要建立一个自己的犯错模型。

John对于编程者的建议有:

记录下你所有的曾经的尝试,不要重复犯错

重新考虑代码思路

调试代码而非注释

寻求他人帮助

休息一会再来看

慢一点,欲速不达

控制代码的行数

保存旧的版本

优化问题

简而言之就是两个部分:

一个需要求极值的函数

函数上的约束

经典的优化问题有:最短路径问题,旅行商问题,序列对齐问题等。接下来要介绍的是背包问题。

连续背包问题

假设一个小偷,他只有一个8磅的背包,有三种货物,4磅的金砂,3磅的银砂和10磅的葡萄干。如果这个小偷想拿到最有价值的分配,他该怎么做?

显而易见的做法是:

先装4磅金砂,再装3磅银砂,最后装1磅的葡萄干。

为什么说这个问题是连续背包呢?因为金砂还有银砂可以看做是无限小的。

这个问题的数学描述是

function=cg×pg+cs×ps+cr×pr

constraints=pg+ps+pr≤8

其中cg,cs,cr分别代表金砂的价值,银砂的价值和葡萄干的价值;

pg,ps,pr分别代表金砂的重量,银砂的重量和葡萄干的重量。

这个问题使用贪婪算法就可以解决。

但是,

局部最优并不能总是保证全局最优。

不连续背包问题

0/1 Knapsack Problem

不连续背包问题中物品的重量是不能再细分的。

同样是一个小偷,他有8磅容量的背包,房间内物品有

表,重0.5磅,价值A位,两块

收音机,重2磅,价值D位,两块

Tiffany花瓶,重5磅,价值B位,两个

Velvet Evis,重6磅,价值C位,两块

现在有三个小偷

一个是贪婪的贼,使用贪婪算法(greedy algorithm),他的选择是先拿最贵的,也就是表∗2+花瓶∗1

一个精心思考的慢性子的贼,他会使用蛮力法(Bruce algorithm),他的选择时把每种选择都试一遍,他最后还没试出最优解就被抓到警察局了。

还有最后一种使我们这样聪明的贼。

现在用数学公式描述一下上述问题

function=∑i=1nPiXi

constraint=∑i=1nwiXi

其中,n是物品的总数,wi是第i个物品的重量,Pi是第i个物品的价值,X是一个向量,例如(0,0,0,0,0,0,0,0)代表8个物品中没有取任意一个。

动态编程

Dynamin Programming, John说这个名字没有任何意义。

它的重点在于

overlapping subproblems(重叠子问题)

optimal substructrure(优化子结构)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: