算法导论(第三版)练习 2.2-1 ~ 2.2-4

2.2-1

theta n^3

 

2.2-2

SELECTION-SORT(A)
    for i = 1 to A.length-1
        for j = i+1 to A.length
            if A[i] > A[j]
                swap(A[i], A[j])

维持的循环不变式是 A[1..i] 按序排列(从小到大),

不严谨地尝试证明↓

初始化:对于单个元素 A[1],显然成立。

保持:我们假设在 i = n 时循环不变式成立,那么当 i = n + 1 时,A[i] 将会和 A[i+1] ~ A[A.length] 中的每个元素进行比较,最终 A[i] 将取得它们中的最小值,事实上它就是在整个序列中次小于 A[i-1] 的那个数(因为每次迭代总会拿到剩余队列的最小值,因此“下一次”只能拿到仅次于“上次”的最小值),故 A[1..i] 保持循环不变式。

终止:当 i = A.length 时迭代结束,根据上面的证明 A[1..A.length-1] 已确保有序, A[A.length] 是经过 A.length - 1 次比较后剩下的次次次..(省略 A.length - 4 个次).次最小数,也就是序列中的最大数,因此 A[1.. A.length] 按序排列,故算法正确。 

算法的运行时间 T(n) = c1*(n-1) + c2*(n-1+1)*n / 2 + c3*(n-1+1)*n / 2 + c4*(n-1+1)*n / 2

最佳状况是数组已经有序(c4 取 0),最坏情况是数组逆序(如上式),不过两者增长量级是相同的,都是 theta n^2 。

 

2.2-3

平均需要检查 n / 2 个元素,最坏情况需要检查 n 个。

平均情况和最坏情况的增长量级都是 theta n 

SEARCH(v, A)
    for i = 1 to A.length    c1 tn
        if A[i] == v        c2 tn
            return i        c3 1 or 0
    return NIL                c4 1 or 0  

T(n) = c1*tn + c2*tn + c3 + c4

代入 n (最坏)或者 n/2 (平均),忽略常数,结果都是 theta n

 

2.2-4

针对特殊情况进行测试,如果符合的话就直接给出预先计算好的答案。

就是增加常数时间能跑完的代码,用于覆盖一些特例,减少无所谓的循环。

下面是网上找的参考答案:

 

 

摘:

1
我们先来观察几个简单的四则运算:

3 + 1 = 2 

23 + 23 = 46

2 + 4 = 6

上面几个式子有什么相似之处呢?

很显然它们的统一特征就是“求和”。我们找一个符号来抽象表示“这件事情”,比如说用 sum(a, b) 表示“求和”,

这样的话我们可以推出 sum(3, 1) = 2

sum(23, 23) = 46,

“ + ”只不过是个符号而已!

再比如我们需要把一些数组从小到大到排序。

例如说  1 3 2 ,很容易得到  1 2 3 

再比如  1 4 3  ,很容易得到 1 3 4

我们同样可以用一个符号来抽象表示这件事情, sort(1,  3, 2) 。 (sort 是英语中的排序)

 那我现在问你,你到底是如何把 1 3 2 从小到大到排序的呢?

按照普通高中生的思维,一般都会说:这还用想?#@X 把 32 调换就可以了。。

好吧 那我让你将 1729 237 9123 263 12 123 2813 123 213 123 321 312 3... 从小到大到排序你会怎么做呢?

▉▊▋▌▍▎▉▊▋▌▍▎▏█▇ ▆ ▅ ▄ ▃ ▂ ▂ 

▏█▇   ▌▍▎▇ ▆ 

▍▎▏▉▊▍▎▏▉▊▋▌▍▎▏▌▍▎█▌▍▎▌▍

是不是发现有特别多的方法?例如说你可以把最小的挑出来,然后再去找次小的、又或者可以按照排队的方法,把大的往后换...

当然,在我们软件开发的过程中要做的事情通常不是排序那么简单。

例如说一个非常简单的例子。。我希望你让计算机识别某张图片中的数字。。同样的,我们可以像 sum() 、sort() 一样把它抽象为 f(a.jpg) , f 只是个记号!

好吧,对于这个问题,我想非专业人员并不是很快可以知道该怎么做的。。。

我们把类似于 sum() 、sort() 、f(a.jpg) 的玩意儿叫做函数 ,算法可以理解为函数的具体实现。

 

2
大多数情况都不需要我们从头到尾亲自去设计一个算法,也就是你不需要去亲自弄清楚怎么样去识别图片中的数字。

更多时候,我们是作为一个算法的使用者,“识别数字”往往也只是软件功能的一小部分,

我们做得更多的事情是“分析算法”,然后拿来用:

要分析一个算法,首先需要我们能够看懂算法(),其次是能够甄别算法的效率。

 

我们怎样判断一个算法的效率呢?严格来讲一个算法的快慢相关于其具体的运行环境。

如果是真实的场景的话,肯定是要基于具体条件分析的。

但是从应用的角度,我们一般假设算法都运行在相同的环境下(诸如硬件条件、内存之类的),在此基础上去衡量算法的运行时间,这个运行时间对于不同的输入显然也不是一成不变的,排序 1 3 2 显然比排序 1729 237 9123 263 12 123 2813 123 213 123 321 312 3...快得多。

倘若赤裸裸的分析一个算法的话,运行时间指的是算法运行的步数!!!一般呈现这样的关系:运行时间相关于输入规模和输入内容, 

(当然,存在对于特定输入却有不同运行时间的状况,暂且不提)

通常认为,一个算法的增长量级越低就越有效,例如说n^2 相对于 nlogn 显得很笨重!

具体怎么算参考算法导论。

 

总之,认识一个算法或者说对待算法的态度应该是:算法有什么用 > 算法效率怎么样 > 算法具体做了什么
View Code

 

posted @ 2017-10-13 20:37  xkfx  阅读(458)  评论(0编辑  收藏  举报