Programming Pearls笔记之二
Programming Pearls笔记之二
这里是编程珠玑(Programming Pearls)第二部分(中间五个专栏)的笔记.
1 效率和正确性
- 问题
有句话说"效率是第二位的,结果是第一位的--如果结果都错了,再快又有何用".这种观点正确吗?
- 解答
当错误不是太严重以至于影响结果的时候时间更重要.比如对于一些大型系统,与其修改10个bug,人们往往更愿意将速度提高10倍.
2 最大子序列和
- 问题
这是一道OJ题目,没想到在这里见到了它.或许这就是这个题目的出处?
对于一组整数,求连续最大子序列和.
- O(n^2)解法
这种时间复杂的算法很好设计.这里给出一个比较有启发性的.
cumarr[-1]=0 for i = [0, n) cumarr[i] = cumarr[i-1] + x[i] maxsofar = 0 for i = [0, n) for j = [i, n) sum = cumarr[j] - cumarr[i-1] /* sum is sum of x[i..j] */ maxsofar = max(maxsofar, sum)
这里用到了累加求和,很有用处(比如下面的问题 累加数组 就能用到).但这里的求和很盲目,对所有的都求,因此并没有能降低时间复杂度.我们知道如果x[i..j]是最大子序列和,这就说明对于任意的k(i≤k≤j),sum[i..k]≥0.否则sum[k+1..j]将是最大的子序列.因此我们在求和的时候可以每遇到cumarr[i-1]为负时,就不再让cumarr[i] = cumarr[i-1] + x[i],而让cumarr[i] = 0.这时时间复杂度就可以降至O(n).
- O(n)解法
根据上面的思路,解法如下.
cumarr[-1] = 0 maxsofa = 0 for i = [0, n] cumarr[i] = max(cumarr[i-1], 0) maxsofar = max(maxsofar, cumarr[0])
下面是书上给出的一个利用动态归化来推导这个算法的过程.
图一 示意图
maxendinghere是以当前下标为最后一个元素的最大子序列和(只有当包含当前元素值为0时可以是空序列),maxsofar是从数组开始到当前下标为止的最大子序列和.假设对于当前下标i-1成立.下面将这个状态推广到i.对于i,这时最大子序列和只有两种可能.要么还是maxsofar,要么是maxendinghere+x[i]. maxendinghere+[i]若小于0,则从0开始,于是代码如下.
maxsofar = 0 maxendinghere = 0 for i = [0, n] /* invariant: maxendinghere and maxsofar are accurate for x[0..i-1] */ maxendinghere = max(maxendinghere + x[i], 0) maxsofar = max(max, maxendinghere)
其实这和上面的伪代码基本是一样的.
- O(n log n)解法
这是一个分治算法,虽然不是最优解,但可以锻炼下思维.直接给出算法.
float maxsum3(l, u) if (l > u) /* zero elements */ return 0 if (l == u) /* one elements */ return max(0, x[l]) m = (l + u) / 2 /* find max crossing to left */ lmax = sum = 0 for (i = m; i >= l; i--) sum += x[i] lmax - max(lmax, sum) /* find max crossing to right */ rmax = sum = 0 for i = (m, u] sum += x[i] rmax = max(rmax, sum) return max(lmax+rmax, maxsum3(l, m), maxsum3(m+1, u))
3 累加数组
- 问题
数组x[0..n-1]中的元素初始化为0,经过n步下面的操作,给出最终的数组元素.其中l,u和v是每个操作的参数(0≤l≤u<n,是整数;v是实数). for i = [l, u] ① x[i] += v ②
- 解答
这个问题用累加数组来解决.操作①、②对应于cum[l]+=v;cum[u+1]-=v.意思是让x[l..n-1]+=v,然后再让x[u+1..n-1]-=v.最终计算x的方式是:
for (i = 0; i < n; i++) x[i] = x[i-1] + cum[i];
书中给出的答案是让操作①、②对应于cum[u]+=v;cum[l-1]-=v.最后计算x时从后往前累加计算.
4 哨兵元素
- 问题
利用哨兵元素求数组最大值.
- 解答
没什么知识可以学习,就觉得R.G.Dromey这个解答有点儿耐人寻味.
i = 0 while i < n max = x[i] x[n] = max i++ while x[i] < max i++
5 多项式计算
- 问题
对于下面的多项式,优化下面的算法. y = a[0] xi = 1 for i = [1, n] xi = x * xi y = y + a[i]*xi
- 解答
下面Horner的方法几乎可以使效率提高一倍.
y = a[n] for (i = n-1; i >= 0; i--) y = x*y + a[i]