计算机程序设计艺术 卷1:基本算法 1.1习题

1.[10]正文中展示了可以利用替换记号,通过置t\leftarrowm,m\leftarrown,n\leftarrowt,交换变量m和n的值。请说明怎样通过一连串替换,把四个变量(a,b,c,d)重新排列成(b,c,d,a)。换句话说,a的新值是b的初始值,以此类推。试用最少的替换次数实现。

解:除了a,b,c,d 4个变量外,新增一个变量t,通过这5个变量进行连续替换,过程如下:

t\leftarrowa,a\leftarrowb,b\leftarrowc,c\leftarrowd,d\leftarrowt。
 


public class ExchangeVariables {

    public static void main(String[] args){
        int[] b = new int[]{1,2,3,4};
        int t = b[0];
        for (int i = 0; i < b.length; i++) {
            if((i+1)<b.length){
                b[i] = b[i+1];
            }
        }
        b[b.length-1]=t;

        System.out.println(Arrays.toString(b));
    }
}

最少4次替换实现。

2.[15]证明从步骤E1第二次执行起,每次该步开始时,m必然大于n(第一次执行时可能不满足)。

解:在第一次执行步骤E1后,变量m和n的值分别是n和r原来的值,且n>r。

public class GreatestCommonFactor {
    public static void main(String[] args){
        int m = 119;
        int n = 544;
        int t = 0;
        if(m<n){
            t=m;
            m=n;
            n=t;
        }
        algorithmE(m,n);
    }

    static int algorithmE(int m,int n){
        if(m%n==0){
            System.out.println("最大公约数是:"+n);
            return n;
        }else{
            if(m%n > 0){
                int r=(m%n);
                m=n;
                n=r;
                return algorithmE(m,n);
            }
        }
        return 0;
    }
}

3.[20](为了提高效率)修改算法E,使其避免出现m\leftarrown之类的平凡替换操作。按照算法E的风格写出这个新算法,将其称为算法F。

解:算法F(欧几里得算法)。给定两个正整数m和n,求它们的最大公因数。

F1.[求m/n的余数。]用n除m,令m为余数。

F2.[余数是否为0?]如果m=0,算法终止,n是答案。

F3.[求n/m的余数。]用m除n,令n为余数。

F4.[余数是否为0?]如果n=0,算法终止,m是答案;否则,返回步骤F1。 I

public class GreatestCommonFactor {
    public static void main(String[] args){
        int m = 119;
        int n = 544;
        int t = 0;
        if(m<n){
            t=m;
            m=n;
            n=t;
        }
        algorithmF(m,n);
    }
    static int algorithmF(int m,int n){
        if(m%n==0){
            System.out.println("最大公约数是:"+n);
            return n;
        }else{
            m=(m%n);

            if(m == 0){
                return n;
            }else{
                n = n%m;
                if(n == 0){
                    return m;
                }else{
                    algorithmF(m,n);
                }
            }
        }
        return 0;
    }
}

4.[16]2166与6099的最大公因数是多少?

解:有算法E,n=6099,2166,1767,399,171,57。答案是57。

public class GreatestCommonFactor {
    public static void main(String[] args){
        int m = 2166;
        int n = 6099;
        int t = 0;
        if(m<n){
            t=m;
            m=n;
            n=t;
        }
        algorithmE(m,n);
    }
    static int algorithmE(int m,int n){
        if(m%n==0){
            System.out.println("最大公约数是:"+n);
            return n;
        }else{
            if(m%n > 0){
                int r=(m%n);
                m=n;
                n=r;
                return algorithmE(m,n);
            }
        }
        return 0;
    }
}

5.[12]说明“阅读本套书的步骤”其实不是一个真正的算法,因为在算法的五个特征中,它至少缺少三个!另外请指出它与算法E在格式上的差异。

解:它不满足有限性、确定性和可行性,可能没有输出。就格式而言,在操作步号码之前没有字母,未出现概述性短语,而且没有“I”。

6.[20]当n=5时,执行算法E步骤E1的平均次数T_{5}是多少?

解:用n=5和m=1,2,3,4,5实验算法E,步骤E1执行的次数分别为2,3,4,3,1。所以步骤E1执行的平均次数是2.6=T_{5}

7.[M21] m已知,n在所有正整数范围内取值,令U_{m}为算法E中步骤E1执行的平均次数。说明U_{m}具有合理定义。U_{m}T_{m}有关系吗?

解:除了数目有限的特例以外,n>m总成立。当n>m时,算法E的第1次迭代仅仅交换这两个数,所以U_{m}=T_{m}+1。例如m=5,n=1,2,...,1,2,3,2,1,3,4,5,4,2,3,4,5,4,2,...的平均次数是3.6。

8.[M25]通过指定式(3)中的\theta _{j},\phi _{j},a_{j},b_{j},给出计算正整数m和n的最大公因数的“可行的”形式算法。令输入由字符串a^{m}b^{n}表示,也就是在m个a后连着n个b。你的解法应当力求尽可能简单。[提示:利用算法E,把步骤E1中的除法改为置r\leftarrow \left | m-n \right |,n\leftarrow min(m,n).]

解:令A={a,b,c},N=5,算法结束时得到字符串a^{gcd(m,n)}.

j        \theta _{j}        \phi _{j}        b_{j}        a_{j}

0       ab       (空)       1         2        消除一个a和一个b,或者转到2.

1       (空)      c          0         0        在最左端加c,转回0.

2        a         b          2         3        把所有a变成b.

3        c         a          3         4        把所有c变成a.

4        b         b          0         5        如果还有b,重复.

每次迭代要么减少m,要么保持m不变并减少n。

9.[M30]假定C_{1}=(Q_{1},I_{1},\Omega _{1},f_{1})C_{2}=(Q_{2},I_{2},\Omega _{2},f_{2})是两个计算方法。例如C_{1}可以代表式(2)中的算法E,不过要限制m和n的大小;而C_{2}可以代表算法E的一个计算机程序实现。(因此Q_{2}可以是计算机所以状态的集合,也就是计算机内存和寄存器所以可能得配置;f_{2}可以是计算机单步操作的定义;I_{2}可以是初始状态的集合,包括确定最大公因数的相应程序以及m和n的值。)

试就“C_{2}C_{1}的一个表示”或者“C_{2}模拟C_{1}”的概念,给出集合论定义。直观上,这就意味着C_{1}的任何计算序列都由C_{2}模拟实现,不过C_{2}可能要用更多的计算步骤,保留更多的状态信息。(因此,我们得以严格解释“程序X是算法Y的一个实现”这一陈述。)

解:例如,我们说C_{2}表示C_{1},如果存在一个从I_{1}I_{2}的函数g,一个从Q_{2}Q_{1}的函数h,以及一个从Q_{2}到正整数集的函数j,满足下列条件:

(a)如果x在I_{1}中,那么h(g(x))=x.

  (b)如果q在Q_{2}中,那么f_{1}(h(q))=h(f_{2}^{[j(q)]}(q)),其中f_{2}^{[j(q)]}是指函数f_{2}迭代j(q)次。

  (c)如果q在Q_{2}中,那么h(q)\Omega _{1}中的充分必要条件为q在\Omega _{2}中。

  例如,令C_{1}按式(2)定义,令C_{2}I_{2}=\{(m,n)\},\Omega _{2}=\{(m,n,d)\}Q_{2}=I_{2}\cup \Omega _{2}\cup \{(m,n,a,b,1)\}\cup \{(m,n,a,b,r,2)\}\cup \{(m,n,a,b,r,3)\}\cup \{(m,n,a,b,r,4)\}\cup \{(m,n,a,b,5)\}。令f_{2}((m,n))=(m,n,m,n,1)f_{2}((m,n,d))=(m,n,d);f_{2}((m,n,a,b,1))=(m,n,a,b,a\mod b,2)f_{2}((m,n,a,b,r,2))=(m,n,b),如果r=0,其他情形为(m,n,a,r,3)f_{2}((m,n,a,b,3))=(m,n,b,b,r,4)f_{2}((m,n,a,b,r,4))=(m,n,a,r,5)f_{2}((m,n,a,b,5))=f_{2}((m,n,a,b,1))

  现在令h((m,n))=g((m,n))=(m,n)h((m,n,d))=(d)h((m,n,a,b,1))=(a,b,0,1)h((m,n,a,b,r,2))=(a,b,r,2)h((m,n,a,b,r,3))=(a,b,r,3)h((m,n,a,b,r,4))=h(f_{2}((m,n,a,b,r,4)))h((m,n,a,b,5))=(a,b,b,1)j((m,n,a,b,r,3))=j((m,n,a,b,r,4))=2,其他情形为j(q)=1。于是C_{2}表示C_{1}

  注记:读者可能希望寻找更简单的定义方式,例如令g是从Q_{1}Q_{2}的函数,并且仅要求当x_{0},x_{1},...C_{1}中的计算序列时,g(x_{0}),g(x_{1}),...C_{2}中以g(x_{0})为首项的计算序列的子序列。然而这是不恰当的。在上面的例子中,C_{1}忽略m和n的原值,但是C_{2}未忽略。

  如果C_{2}通过函数g,h,j表示C_{1},而且C_{3}通过函数g^{'},h^{'},j^{'}表示C_{2},那么C_{3}通过函数g^{''},h^{''},j^{''}表示C_{1},其中

g^{''}(x)=g^{'}(g(x)),h^{''}(x)=h(h^{'}(x)),

并且如果q_{0}=qq_{k+1}=f_{3}^{[j^{'}(q_{k})]}(q_{k}),那么

j^{''}(q)=\sum_{0\leq k<j(h^{'}(q))}j^{'}(q_{k}).

因此上面定义的关系是传递的。如果函数j是有界的,我们可以说C_{2}直接表示C_{1},这个关系也是传递的。关系“C_{2}表示C_{1}”产生一个等价关系,两个计算方法等价显示当且仅当它们计算的函数是同构的;关系“C_{2}直接表示C_{1}”产生一个更有趣的等价关系,它大概相当于二者“本质上是同一个算法”的直观概念。

关于模拟的另一种定义方法,参阅罗伯特\cdot弗洛伊德和理查德\cdot贝尔格的《机器的语言》的3.3节[The Language of Machines(Computer Science Press,1994)]。

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jinyongbomatt2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值