您的位置:首页 > 编程语言

编程珠玑第8章总结

2016-07-09 18:18 399 查看

第八章主要讲了几个基本算法设计技术,引入了一个累加数组的思路,这个思路很有意思。

习题4 这个题目从数学上我是不会做的

E(max)=limN→∞∫N0p(max)∗max∗dmaxN.....(1)

P(max≤x)=∫x0p(max)∗dmax=?......(2)

中间这个问号就是关键,应该可以从这里导出关于最大值的分布函数,但是思考了半天不得其解。经过程序模拟:

def max_subvec(array):
max_here = 0
max_sum = 0
for it in array:
max_here = max(0,max_here+it)
max_sum = max(max_here,max_sum)
return max_sum
def simulate():
import random
sum = 0
for i in range(5000):
case_vec = [random.uniform(-1,1) for i in range(1000)]
sum += max_subvec(case_vec)
return sum/5000
print(simulate())


最大值大约是22.3左右….这个数字很奇怪,不知道怎么来的。

第六题:

我认为通过反证法就可以证明,假设有一个数远远大于其它所正数输入的和, 或者是一个很小的负数,那么如果不检测这个输入就没有办法确定是否要把这个输入考虑进来,那么就没有办法得到正确得答案。

习题10

首先如果是查找接近0的子向量,那么存在O(NlogN)的算法。解决方法很巧妙,它利用这样一个事实:寻找一段和接近0的子向量,就是在寻找两个非常接近的累加数组的元素!这样就把问题成功转换了。这样问题就回到寻找相似元素的思路上。假如是寻找完全相同的元素那么利用hash散列可以以O(N)的时间界求解,如果是寻找最接近,那么就可以采取排序的办法来解决,对一个有序的序列进行遍历,观测两两相邻的元素那个最接近即可。代码如下:

def zero_subvec(array):
#defensive coding,test the input
n = len(array)
if n <2:return array[0]
#compute accumulate array
cumarr = [(0,-1)]
last = 0
for i,each in enumerate(array):
cumarr.append((last+each,i))
last +=each
cumarr.sort(key = lambda t:t[0])
print(cumarr)
#test the near pair
min_sub = (cumarr[1][1],cumarr[0][1],cumarr[1][0]-cumarr[0][0])
for i in range(0,len(cumarr)-1):
if cumarr[i+1][0]-cumarr[i][0] < min_sub[2]:
min_sub = (cumarr[i+1][1],cumarr[i][1],cumarr[i+1][0]-cumarr[i][0])
if min_sub[0]<min_sub[1]:
print('[',min_sub[0]+1,',',min_sub[1],']','-----',-min_sub[2])
else:print('[',min_sub[1]+1,',',min_sub[0],']',min_sub[2])


这里有一个细节需要注意:我们在采用累加数组计算的时候,一定要添加一个假想的累加元素,即0之前的元素和,为什么?因为这样才能用累加数组完整的表示∑jk=ia[k].....(3),否则i=0的情况没法表示。当然我们也可以在程序里特殊处理,计算第一个元素的绝对值作为初始值,然后再两两作差比较。

如果要找到一个最接近t的元素怎么办,这个问题看似和原来的一样,实际上是没办法用O(NlogN)的算法来处理的,最坏的情况应该是N^2.假设子向量是x,注意到:利用累加数组之间的差值表示实际上得到的是d=|x|,我们所求的是使得|x−t|最小,而实际比较中是得到|d−1|,这样一来就没有办法利用排序进行优化了,当然t=0时由于两者没有区别,所以可以不去考虑正负。

最后,如果所有输入都是正数的情况下,求最接近实数t的子向量,这是可以利用累加数组进行优化的,因为

x=|x|,所以针对cumarr[i]....cumarr[n]之间表示的x,|x−t|是一个有规律的序列,...,因此可以利用这个条件排除掉一些不必要的搜索,比如当|x−t|首次大于0时只需要将它和最后一次小于0的值比较即可,后面的值必然是递增的,这里还可以利用二分查找获得首次大于0的值,如果不存在则最后一个¥|x−t|就是最小值,这样每一次搜查时logN,一共进行N次二分查找再加上排序的消耗,最后也是NlogN的复杂度。

习题12

这个是可以将时间从O(N*N)推向O(N)的,思路还是需要引入一个记录数组来解决。关键是考虑这样一个事实,一次输入的参数l,u,v,x[l]….x[u]都是加上同一个参数,可以把它们当作一个整体对待。伪代码如下:

for each case with l,u,v:
cum[u]+=v
cum[l-1]-=v
x
= cum

for i from n-1 to 0:
x[i] = cum[i]+x[i+1]


可以很容易证明其正确性,首先初始情况x
的值显然是正确,其次对于任意一个i,x[i]=∑ik=ncum[k]......(4),这很容易理解:前面的区间如果将i包含(l<=i<=u),那么在这个区间里面的v就应该加上,而不应该加上v的元素位置必然在l的后面,另外如果不包含i,那么u,v都大于i,两者相加会相互抵消,不会影响x[i].至于u小于x[i]就更加显然了。

最后实现的时候注意一下边界条件。

练习13

这个题最简单的办法就是现在一个纬度上面计算出所有的子向量,然后将这些子向量作为基本元素在另一个纬度上面相加,最后就可以得到O(n^3)的算法。伪代码如下:

for each i from:
sum = [0.....0]#initialize all sum as zero
for each j from i to n:
#compute the sum[i,j] in the column k
for each k:
sum[k] += array[j][k]
#have computed all sum[k]
#use sum as element to compute max
max = max(max_sub(sum),max)


实现如下:

def max_rec(array):
n = len(array)
max_sum = 0
for i in range(n):
sum = [0]*n#initialize all sum as zero
for j in range(i,n):
#compute the sum[i,j] in the column k
for k in range(n):
sum[k] += array[j][k]
#have computed all sum[k]
#use sum as element to compute max
max_sum = max(max_subvec(sum),max_sum)
return max_sum


练习14,太简单了,O(N)算法可以搞定,依次扫描即可。

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