黄宇《算法设计与分析》课后习题解析系列(一)

第1章:抽象的算法设计与分析

    • 1.1: (3个数排序)
    • 1.2: (3个数的中位数)
    • 1.3: (集合覆盖问题)
    • 1.4: (换硬币问题)
    • 1.5: (多项式计算)
    • 1.6:(整数相乘)
    • 1.7: (平均复杂度计算)

1.1: (3个数排序)

输入3个各不相同的整数

1)题目:请设计一个算法将输入的3个整数排序。

  • 解析:任意选择一种简单的基于比较的排序算法即可;
  • 算法伪码如下:(采用冒泡排序思想,按升序排序)
Algorithm: BUBBLE(A[1,2,3])
flag := false
for i:= 1 to 2 do
	if A[i] > A[i+1] then
		SWAP(A[i],A[i+1]);
		flag := true;
if flag = true then
     if A[1]>A[2] then
        SWAP(A[1],A[2]); 

2)题目:在最坏情况下、平均情况下你的算法分别需要进行多少次的比较?(假设所有可能的输入等概率出现)

  • 解析:最坏情况即是比较次数最多的全逆序,平均情况可以直接枚举所有情况再求期望(注:不同算法答案可能不同);

  • 解答如下:(以三个整数∈集合{1,2,3}为例)

    最坏情况下:A[1,2,3] = {3,2,1}, 需要3次比较;

    平均情况下:共6种情况,每种情况等概率1/6,笔者的算法只有{1,2,3}一种情况需要2次比较,其余均需要3次比较,故平均需要: 1 6 ∗ 2 + 5 6 ∗ 3 = 17 6 \frac{1}{6}*2+\frac{5}{6}*3= \frac{17}{6} 612+653=617次比较;


3)题目:在最坏情况下将3个不同整数排序至少需要多少次比较?请证明你的结论。

  • 解析:容易得出至少需要3次比较,而证明可以利用反证法,证明不可能存在最坏情况仅需2次比较的算法;同时举出一个最坏情况只需要3次比较的算法(比如第一问你自己的算法,若满足的话),即可证明;(注:举例是必需的,否则只能证明答案不是2,不能证明答案就是3;而反证法的过程可以采用对手论证的格式)
  • 解答:至少需要3次比较,证明如下:
    设3个数为a,b,c,假设存在只需要2次比较的算法,则对手策略设计如下:
    情况 对手
    第1次比较a和b a< b
    第2次若比较a和c a < c, 此时无法确定b和c的大小关系
    第3次若比较b和c b > c, 此时无法确定a和c的大小关系
    综上:不存在只需要2次比较的算法,而第一问设计算法最坏情况只需要3次比较,因此至少需要3次比较,得证;


1.2: (3个数的中位数)

输入3个各不相同的整数

1)题目:请设计一个寻找3个数的中位数的算法。

  • 解析:即找到第二大的数,①可以先找最大数,再在剩下的两数中找较大数;②也可以先找最小数,再在剩下的两数中找较小数;③也可以找到最大数和最小数,那么剩下那个数就是中位数;④或者直接排序,取第二大的数;
  • 算法伪码如下:(采用先找到最大数和最小数的算法)
Algorithm: MIDDLE(A[1,2,3])
max := MAX(A[1],A[2]);      /*需且仅需1次比较*/
min := MIN(A[1],A[2]);    /*需且仅需1次比较*/
if A[3] > max then
    return max;
else 
   if A[3] > min then
          return A[3];
   else
	     return min;

2)题目:在最坏情况下、平均情况下你的算法分别需要进行多少次的比较?(假设所有可能的输入等概率出现)

  • 解析:最坏情况即是比较次数最多的全逆序,平均情况可以直接枚举所有情况再求期望(注:不同算法答案可能不同);

  • 解答如下:(以三个整数∈集合{1,2,3}为例)

    最坏情况下:A[1,2,3] = {1,3,2}或{3,1,2}或{2,3,1}或{3,2,1},需要3次比较;

    平均情况下:共6种情况,每种情况等概率1/6,只有{1,2,3},{2,1,3}种情况只需要2次比较,其余均需要3次比较,故平均需要: 2 6 ∗ 2 + 4 6 ∗ 3 = 8 3 \frac{2}{6}*2+\frac{4}{6}*3 = \frac{8}{3} 622+643=38次比较;


3)题目:在最坏情况下找出3个不同整数的中位数至少需要多少次比较?请证明你的结论。

  • 解析:解析同理于1.1 第3小问;
  • 解答:解答同理于1.1 第3小问;


1.3: (集合覆盖问题)

定义集合的最小覆盖如下:已知全集U={1,…,n}。给定U的子集组成的集合族S = {S1,…,Sm},找到S的最小子集T,满足 ⋃ S i ∈ T S i = U \bigcup_{S_i \in T} S_i = U SiTSi=U.

1)题目:找出下面算法失败的例子:首先选择S中最大的集合Si,并从全集中将Si中的所有元素删除;然后从S中剩余的集合中挑选最大的并从全集中删除对应元素;重复上述过程直到全集中的所有元素都被覆盖。

  • 解析:该算法能找到覆盖但不一定最小,因为前几大的子集可能彼此重叠很多元素;
  • 失败例子如下:
    输入:U = {1,2,3,4,5},S1 = {1,2,3,4},S2 = {1,2,3},S3 = {5} => 输出:{S1,S2,S3}, 但正确答案为:{S1,S3}

2)题目:请设计一个算法计算输入全集的一个集合覆盖,并证明你所设计算法的正确性。

  • 解析:算法只需要对于每个未覆盖元素xi,去遍历子集族,找到一个子集包含该元素,则将该子集加入覆盖,并将其中包括xi的所有元素标记为已覆盖;重复上述过程,直到所有元素全部覆盖或找不到任何一个子集能覆盖某个元素xj(此时不存在集合覆盖);算法正确性证明只需抓住元素个数有限,而每次只要找到一个包含未覆盖元素xi的子集S即能减少至少1个未覆盖元素即可;

  • 算法伪码如下: 设(全集U={x1,x2,xn}, 子集为S1,S2,.,Sk)

Algorithm: MINCOVER(S1,S2,..,SK)
mincover := {
     };
for each xi in U:   /*初始化所有元素为未标记*/
	xi is unvisited;
for each unvisited xi in U:
	if found Sj has xi:  /*这里需要在未加入覆盖中的子集中寻找*/
		make each xk in Sj visited;
		join Sj into mincover; 
	else:
		return NULL;  /*此时不存在集合覆盖*/
return mincover;	

  • 算法正确性证明:易知算法每次循环要么找不到覆盖xi的子集Sj而直接返回(此时正确判定为不存在集合覆盖);要么找到这样的子集Sj,而从未覆盖的元素中标记至少1个元素为已覆盖,而未覆盖的元素个数有限,则算法必然能中止;且根据覆盖定义,最后正常返回的子集族一定是一个集合覆盖,故算法正确,得证;

3)题目:你所设计的算法能否保证总是得出最小覆盖?如果不能,请针对你的算法设计一个反例。

  • 解析:易知上述算法只是针对寻找覆盖,并没有进行优化;而需要优化的话,可采用贪心算法的思想修改原算法,即每次循环都找包含xi且包含最多未覆盖元素的子集Sj;
  • 解答:不能,反例如下:
    U = {1,2,3},S1 = {1},S2 = {2},S3 = {3},S4 = {2, 3} => 输出:{S1,S2,S3}, 但正确答案为:{S1,S4}


1.4: (换硬币问题)

题目:…请将上述3种方案分别写成算法,并通过举反例的方式证明这3个算法的“不正确性”。

  • 解析:三种算法之所以不正确是因为它们都按照某种既定的面值顺序进行累加判断,这样就漏掉了许多其他面值组合的情况;而这道题一般的思路是采用动态规划的思想,进行递归搜查;
  • 解答:算法易写,略;反例易举,略;


1.5: (多项式计算)

题目:HORNER算法是用来计算多项式 P ( x ) = a n x n + a n − 1 x n − 1 + . . + a 1 x + a 0 P(x) = a_nx^n + a_{n-1}x^{n-1}+..+a_1x+a_0 P(x)=anxn+an1xn1+..+a1x+a0的,请证明其正确性。

  • 解析:HORNER算法本质即为秦九劭算法, 采用了迭代的思想,故正确性证明可采用同样使用了迭代思想的数学归纳法

  • 证明:
    i) 当n = 1 时,p = a0 = P(x), 易知正确;

    ii) 假设当n = k时算法正确,即 p = P ( x ) = ∑ a i x i ( i : 0 → k ) p = P(x) = ∑a_ix^i (i : 0 \rightarrow k) p=P(x)=aixi(i:0k);则当n = k+1时,由假设知,循环的倒数第二次: p = ∑ a i x i − 1 ( i : 1 → k + 1 ) p=∑a_ix^i-1 (i :1 \rightarrow k+1) p=aixi1(i:1k+1),故最后一遍循环: p = p ∗ x + A [ 0 ] = ∑ a i x i + a 0 ( i : 1 → k + 1 ) = ∑ a i x i ( i : 0 → k + 1 ) p = p*x+A[0] = ∑aix^i+a_0 (i :1 \rightarrow k+1) = ∑a_ix^i (i : 0 \rightarrow k+1) p=px+A[0]=aixi+a0(i:1k+1)=aixi(i:0k+1)
    综上:归纳可得算法对一切n成立;



1.6:(整数相乘)

INT-MULT算法用来计算两个非负整数y、z的乘积
1)题目:令c = 2, 请证明算法的正确性。

  • 解析:由算法易得算法的递推公式:
    F ( y , z ) = F ( c y , ⌊ z c ⌋ ) + y ∗ ( z m o d    c ) F(y,z) = F(cy,\lfloor \frac{z}{c} \rfloor) + y*(z \mod c) F(y,z)=F(cy,cz)+y(zmodc)
    z = z n , z i = c ∗ z i − 1 + r n z = z_n, z_i = c*z_{i-1} + r_n z=zn,zi=czi1+rn ,即 z i c = z i − 1 , z i m o d    c = r i , 特 殊 地 : z 0 = 0 \frac{zi}{c} = z_{i-1}, z_i \mod c = r_i, 特殊地:z_0 = 0 czi=zi1,zimodc=ri,z0=0
    故递推公式可变形为: F ( y , z n ) = y ∗ Σ c i ∗ r n − i ( i : 0 → n − 1 ) F(y,z_n) = y*Σc^i*r_{n-i} (i : 0 \rightarrow n-1) F(y,zn)=yΣcirni(i:0n1),
    通过变形其实可以清楚认识到算法的本质其实就是得到z的c进制形式,然后逐位与y相乘,故算法的进行(递归的层数)与y无关(y仅作为一个系数参数)因此任意y, 当算法对所有的z∈N成立时,即可得算法的正确性;

  • 算法正确性证明如下:(采用数学归纳法)
    i) 当 z = 1 z=1 z=1时, F ( y , 1 ) = y ∗ 2 0 = y ∗ 1 F(y,1) = y*2^0 = y*1 F(y,1)=y20=y1, 易知成立;

    ii) 假设当 z ≤ k ( k > 1 ) z\leq k(k>1) zk(k>1)时算法成立,即 y ∗ z = F ( y , z ) y*z = F(y,z) yz=F(y,z), 则当z = k+1时, F ( y , k + 1 ) = F ( 2 y , ⌊ k + 1 2 ⌋ ) + y ∗ ( ( k + 1 ) m o d    2 ) F(y,k+1) = F(2y,\lfloor \frac{k+1}{2}\rfloor)+y*((k+1) \mod 2) F(y,k+1)=F(2y,2k+1)+y((k+1)mod2),对k奇偶分类讨论如下:

    ① 当 k = 2 m k = 2m k=2m时, F ( y , 2 m + 1 ) = F ( 2 y , ⌊ 2 m + 1 2 ⌋ ) + y = F ( 2 y , m ) + y = 2 y ∗ m + y = y ∗ k + y = y ∗ ( k + 1 ) F(y,2m+1) = F(2y,\lfloor \frac{2m+1}{2}\rfloor) + y = F(2y,m)+y = 2y*m+y = y*k+y = y*(k+1) F(y,2m+1)=F(2y,22m+1)+y=F(2y,m)+y=2ym+y=yk+y=y(k+1), 成立;

    ② 当 k = 2 m + 1 k = 2m+1 k=2m+1时, F ( y , 2 m + 2 ) = F ( 2 y , m + 1 ) + 0 = 2 y ∗ ( m + 1 ) = y ∗ ( k + 1 ) F(y,2m+2) = F(2y,m+1)+0 = 2y*(m+1) = y*(k+1) F(y,2m+2)=F(2y,m+1)+0=2y(m+1)=y(k+1), 成立;

    综上:归纳可得算法对所有的z∈N成立,故算法正确,得证;


2)题目:令c为任意一个不小于2的常数,请证明算法的正确性。

  • 解析:同 1) 解析;

  • 算法正确性证明如下:(同理与 1)证明)
    i) 当 z = 1 z=1 z=1时, F ( y , 1 ) = y ∗ c 0 = y ∗ 1 F(y,1) = y*c^0 = y*1 F(y,1)=yc0=y1, 易知正确;

    ii) 假设当 z ≤ k ( k > 1 ) z\leq k(k>1) zk(k>1)时算法成立,即 y ∗ z = F ( y , z ) , ( z ≤ k ) y*z = F(y,z) ,(z\leq k) yz=F(y,z),zk, 则当 z = k + 1 z = k+1 z=k+1时, F ( y , k + 1 ) = F ( c y , ⌊ k + 1 c ⌋ ) + y ∗ ( ( k + 1 ) m o d    c ) F(y,k+1) = F(cy,\lfloor \frac{k+1}{c}\rfloor)+y*((k+1) \mod c) F(y,k+1)=F(cy,ck+1)+y((k+1)modc),

    ⌊ k + 1 c ⌋ = q , ( k + 1 ) m o d    c = r \lfloor\frac{k+1}{c}\rfloor = q,(k+1) \mod c = r ck+1=q,(k+1)modc=r, 由带余除法知:
    k + 1 = c q + r k+1=cq+r k+1=cq+r, 则 F ( y , k + 1 ) = F ( c y , q ) + y ∗ r = y ∗ c q + y ∗ r = y ∗ ( c q + r ) = y ∗ ( k + 1 ) F(y,k+1) = F(cy,q)+y*r = y*cq+y*r = y*(cq+r) = y*(k+1) F(y,k+1)=F(cy,q)+yr=ycq+yr=y(cq+r)=y(k+1), 成立;

    综上:归纳可得算法对所有的z∈N成立,故算法正确,得证;



1.7: (平均复杂度计算)

题目:···请针对输入的取值情况,分析下面算法的平均复情况时间复杂度(一个operation的代价记为1)

  • 解析:直接根据题中的概率算出代价的期望即可;
  • 解答:设平均情况时间复杂度为A(n),则:
    A ( n ) = ∑ P r ( i ) f ( i ) = 1 n ∗ ( 10 ∗ n 4 ) + 2 n ∗ ( 20 ∗ n 4 ) + 1 2 n ∗ ( 30 ∗ n 4 ) + 1 2 n ∗ ( n ∗ n 4 ) = n + 130 8 A(n) = ∑Pr(i)f(i) = \frac{1}{n}*(10*\frac{n}{4}) + \frac{2}{n}*(20*\frac{n}{4})+\frac{1}{2n}*(30*\frac{n}{4})+ \frac{1}{2n}*(n*\frac{n}{4}) = \frac{n+130}{8} A(n)=Pr(i)f(i)=n1(104n)+n2(204n)+2n1(304n)+2n1(n4n)=8n+130



你可能感兴趣的:(算法)