E N D
第4章 计算学科中的核心概念 认知学科终究是通过概念来实现的,掌握和应用学科中的核心概念是成熟的计算机科学家和工程师的标志之一。本章首先介绍计算学科中一个最具方法论性质的核心概念——算法,包括算法的历史、定义、表示方法,以及算法的分析等内容。然后,介绍数据结构、程序、软件、硬件,以及计算机中的数据(含进位制数及其相互转换,原码、反码和补码及其转换,字符、字符串和汉字,图像数据的表示,声音数据的表示)等内容。最后,给出CC1991提取的12个核心概念,即绑定、大问题的复杂性、概念模型和形式模型、一致性和完备性、效率、演化、抽象层次、按空间排序、按时间排序、重用、安全性、折衷与结论。
4.1 引 言 学科中的核心概念是学科中最关键、最重要的概念,它涉及学科研究的内涵、对象、本质、核心要素等内容,其基本特征有以下4点: (1)在学科中多处出现; (2)在各分支领域及抽象、理论和设计的各个层面上都有很多示例; (3)在技术上有高度的独立性; (4)一般都在数学、科学和工程中出现。 在计算学科的一般文献中,学科中的核心概念指的是CC1991报告提取的学科中反复出现的12个核心概念。为便于教学,本书将学科中最具方法论性质的概念——算法,以及数据结构、程序、软件、硬件、计算机中的数据等与CC1991报告提取的12个核心概念一起统称为学科中的核心概念。
4.2 算 法 算法是计算学科中最具方法论性质的核心概念,也被誉为计算学科的灵魂。算法设计的优劣决定着软件系统的性能,对算法进行研究能使我们深刻地理解问题的本质以及可能的求解技术
4.2.1 算法的历史简介 公元825年,阿拉伯数学家阿科瓦里茨米 (AlKhowarizmi)撰写了著名的《波斯教科书》(Persian Textbook),书中概括了进行四则算术运算的法则。“算法(Algorithm)”一词就来源于这位数学家的名字。后来,《韦氏新世界词典》(Websters New World Dictionary)将其定义为“解某种问题的任何专门的方法”。而据考古学家发现,古巴比伦人在求解代数方程时,就已经采用了“算法”的思想。 在算法的研究中,人们不可避免的要提到丢番图方程,希尔伯特著名的23个数学问题中的第十个问题就是关于“丢番图方程的可解性问题”。 古希腊数学家丢番图(Diophantus)对代数学的发展有极其重要的贡献,并被后人称之为“代数学之父”。
他在《算术》(Arithmetica)一书中提出了有关两个或多个变量整数系数方程的有理数解问题。对于具有整数系数的不定方程,若只考虑其整数解,这类方程就叫丢番图方程。“丢番图方程可解性问题”的实质为:能否写出一个可以判定任意丢番图方程是否可解的算法?本书仅讨论线性丢番图方程,至于非线性丢番图方程,本书不再讨论。对于只有一个未知数的线性丢番图方程而言,求解很简单,如ax=b,只要a能整除b,就可判定其有整数解,该整数解即b/a。 他在《算术》(Arithmetica)一书中提出了有关两个或多个变量整数系数方程的有理数解问题。对于具有整数系数的不定方程,若只考虑其整数解,这类方程就叫丢番图方程。“丢番图方程可解性问题”的实质为:能否写出一个可以判定任意丢番图方程是否可解的算法?本书仅讨论线性丢番图方程,至于非线性丢番图方程,本书不再讨论。对于只有一个未知数的线性丢番图方程而言,求解很简单,如ax=b,只要a能整除b,就可判定其有整数解,该整数解即b/a。 对于有两个未知数的线性丢番图方程判定其是否有解的方法也很简单,如ax+by=c,先求出a和b的最大公因子d,若d能整除c,则该方程有(整数解)。
例4.1 问:方程13x+26y=52有无整数解? 答:13和26的最大公因子是13,13又可整除52,故该方程有整数解(如x=2,y=1即方程的解)。 例4.2 问:方程2x+4y=15有无整数解? 答:2和4的最大公因子是2,2不能整除15,故该方程无整数解。 因此可以看出,对于两个未知数的线性丢番图方程来说,求解的关键就是求最大公因子。公元前300年左右,欧几里德在其著作《几何原本》(Elements)第七卷中阐述了关于求解两个数最大公因子的过程,这就是著名的欧几里德算法:给定两个正整数m和n,求它们的最大公因子,即能同时整除m和n的最大正整数。
算法如下: (1)以n除m,并令所得余数为r(r必小于n); (2)若r=0,算法结束,输出结果n;否则,继续步骤(3); (3)将n置换为m,r置换为n,并返回步骤(1)继续进行。 例4.3 设m=56,n=32,求m、n的最大公因子。 算法的执行过程如下: (1)32除56余数为24; (2)24除32余数为8; (3)8除24余数为0,算法结束,输出结果8。 答:m、n的最大公因子为8。 欧几里德算法既表述了一个数的求解过程,同时,它又表述了一个判定过程,该过程可以判定“m和n是互质的”(即除1以外,m和n没有公因子)这个命题的真假。
4.2.2 算法的定义和特征 有关算法的定义不少,其内涵基本上是一致的,其中最为著名的是计算机科学家克努特在其经典巨著—《计算机程序设计的艺术》(The Art of Computer Programming)第一卷中对算法的定义和特性所作的有关描述。 1.算法的非形式化定义 一个算法,就是一个有穷规则的集合,其中之规则规定了一个解决某一特定类型问题的运算序列。 2.算法的重要特性 (1)有穷性:一个算法在执行有穷步之后必须结束。也就是说,一个算法,它所包含的计算步骤是有限的。如在欧几里德算法中,由于m和n均为正整数,在步骤(1)之后,r必小于n,若r≠0,下一次进行步骤(1)时,n的值已经减小,而正整数的递降序列最后必然要终止。因此,无论给定m和n的原始值有多大,步骤(1)的执行都是有穷次。
(2)确定性:算法的每一个步骤必须要确切地定义。即算法中所有有待执行的动作必须严格而不含混地进行规定,不能有歧义性。如欧几里德算法中,步骤(1)中明确规定“以n除m”,而不能有类似“以n除m或以m除n”这类有两种可能做法的规定。 (2)确定性:算法的每一个步骤必须要确切地定义。即算法中所有有待执行的动作必须严格而不含混地进行规定,不能有歧义性。如欧几里德算法中,步骤(1)中明确规定“以n除m”,而不能有类似“以n除m或以m除n”这类有两种可能做法的规定。 (3)输入:算法有零个或多个的输入,即在算法开始之前,对算法最初给出的量。如欧几里德算法中,有两个输入,即m和n。 (4)输出:算法有一个或多个的输出,即与输入有某个特定关系的量,简单地说就是算法的最终结果。如在欧几里德算法中只有一个输出,即步骤(2)中的n。 (5)能行性:算法中有待执行的运算和操作必须是相当基本的,换言之,它们都是能够精确地进行的,算法执行者甚至不需要掌握算法的含义即可根据该算法的每一步骤要求进行操作,并最终得出正确的结果。
3.算法的形式化定义 算法的形式化定义:算法是一个四元组,即(Q,I,Ω,F)。 其中: (1)Q是一个包含子集I和Ω的集合,它表示计算的状态; (2)I表示计算的输入集合; (3)Ω表示计算的输出集合; (4)F表示计算的规则,它是一个由Q到它自身的函数,且具有自反性,即对于任何一个元素q∈Q,有F(q)=q。 一个算法是对于所有的输入元素x,都在有穷步骤内终止的一个计算方法。在算法的形式化定义中,对于任何一个元素x∈I,x均满足以下性质:x0=x,xk+1=F(xk),(k≥0) 该性质表示任何一个输入元素x均为一个计算序列,即x0,x1,x2,…,xk。对任何输入元素x,该序列表示算法在第k步结束。
4.2.3 算法实例 下面,再介绍几个简单的算法实例,以加深读者对算法思想的理解。 例4.4 求1+2+3+…+100 设变量X表示加数,Y表示被加数,用自然语言将算法描述如下: (1)将1赋值给X; (2)将2赋值给Y; (3)将X与Y相加,结果存放在X中; (4)将Y加1,结果存放在Y中; (5)若Y小于或等于100,转到步骤(3)继续执行;否则,算法结束,结果为X。 例4.5 求解调和级数Hn。 Hn=1/1+1/2+1/3+…+1/n
…调和级数在算法分析中有重要作用。直觉上,当n很大时,Hn也未必会得到很大的值。其实不然,只要n充分大,则Hn就能得到我们所需要的不论多大的数。这个例子与梵天塔问题一样清晰地表明:在算法的研究中,不能依靠人的直觉,而只能依靠严密的数学方法。 …调和级数在算法分析中有重要作用。直觉上,当n很大时,Hn也未必会得到很大的值。其实不然,只要n充分大,则Hn就能得到我们所需要的不论多大的数。这个例子与梵天塔问题一样清晰地表明:在算法的研究中,不能依靠人的直觉,而只能依靠严密的数学方法。 下面,给出求解调和级数的算法。 设变量X表示累加和,变量I表示循环的次数,自然语言描述算法如下: (1)将0赋值给X; (2)将1赋值给I; (3)将X与1/I相加,然后把结果存入X; (4)将I加1; (5)若I大于等于N,算法结束,结果为X;否则转到步骤 (3)继续执行。
例4.6 求解斐波那契数。 0,1,1,2,3,5,8,13,21,34,… (1) 数列(1)即著名的斐波那契数列,它来源于1202年意大利数学家斐波那契(L.P.Fibonacci)在其《珠算之书》(Liber Abaci)中提出的一个“兔子问题”: 假设一对刚出生的兔子一个月后就能长大,再过一个月就能生下一对兔子,并且此后每个月都能生一对兔子,且新生的兔子在第二个月后也是每个月生一对兔子。问:一对兔子一年内可繁殖成多少对兔子? 在序列(1)中,每个数都是它的前两个数之和,Fn表示这个序列的第个数,该序列可以形式化的定义为: F0=0,F1=1,Fn+2=Fn+1+Fn,n≥0
斐波那契数列不仅包含着一个有趣的“兔子问题”,而且还是一个关于加法算法的典型实例。下面,给出求解前个斐波那契数的算法。 斐波那契数列不仅包含着一个有趣的“兔子问题”,而且还是一个关于加法算法的典型实例。下面,给出求解前个斐波那契数的算法。 设变量X表示前一个数的值,即定义中的Fn,变量Y表示当前数的值,即定义中的Fn+1,变量Z表示后一个数的值,即定义中的Fn+2。那么求解问题的自然语言描述如下: (1)如果n=0,那么将0赋值给Y,并输出Y,转步骤(11)继续执行; (2)将0赋给X,将1赋值给Y; (3)输出X、Y; (4)将1赋值给I; (5)如果I大于-1,则转到步骤(11),否则继续执行;
(6)将X和Y的和赋值给Z; (7)将Y赋值给X; (8)将Z赋值给Y; (9)将Y输出; (10)将I加1,转步骤(5)继续执行; (11)算法结束。
4.2.4 算法的表示方法 算法是对解题过程的精确描述,这种描述是建立在语言基础之上的,表示算法的语言主要有自然语言、流程图、伪代码、计算机程序设计语言等。 1.自然语言 前面关于欧几里德算法以及算法实例的描述使用的都是自然语言,自然语言是人们日常所用的语言,如汉语、英语、德语等。使用这些语言不用专门训练,所描述的算法也通俗易懂。然而,其缺点也是明显的: (1)由于自然语言的歧义性,容易导致算法执行的不确定性; (2)自然语言的语句一般太长,从而导致了用自然语言描述的算法太长; (3)由于自然语言表示的串行性,因此,当一个算法中循环和分支较多时就很难 清晰地表示出来; (4)自然语言表示的算法不便翻译成计算机程序设计语言理解的语言。
2.流程图 流程图是描述算法的常用工具,它采用美国国家标准化协会ANSI(American National Standard Institute)规定的一组图形符号来表示算法。流程图可以很方便地表示顺序、选择和循环结构,而任何程序的逻辑结构都可以用顺序、选择和循环结构来表示,因此,流程图可以表示任何程序的逻辑结构。另外,用流程图表示的算法不依赖于任何具体的计算机和计算机程序设计语言,从而有利于不同环境的程序设计。就算法的描述而言,流程图优于其他描述算法的语言。下面,分别给出求解例4.4、例4.5和例4.6的流程图算法描述。
(2)求解例4.4的算法流程图,如图4.1所示。 图4.1 例4.4算法流程图
(2)求解例4.5的算法流程图,如图4.2所示。 图4.2 例4.5算法流程图
(3)求解例4.6的算法流程图,如图4.3所示。 图4.3 例4.6算法流程图
3.伪代码 伪代码是用介于自然语言和计算机语言之间的文字和符号来描述算法的工具。它不用图形符号,因此,写方便,格式紧凑,易于理解,便于向计算机程序设计语言算法(程序)过渡。下面,分别给出求解例4.4、例4.5和例4.6的伪代码算法描述。 (1)求解例4.4的伪代码算法描述: BEGIN(算法开始) 1=>X 2=>Y while(Y<=100) { X+Y=>X Y+1=>Y } { X+1/I=> X I+1=>I }while(I>=n) END(算法结束)
(3)例4.6的伪代码算法描述: BEGIN(算法开始) if n = =0 { 0=>Y Print Y } else { 0=>X 1=>Y Print X,Y for(i=1;i<=n-1;i++) { X+Y=>Z Y=>X Z=>Y Print Y } } END(算法结束)
4.计算机程序设计语言 计算机不能识别自然语言、流程图和伪代码等算法描述的语言。因此,用自然语言、流程图和伪代码等语言描述的算法最终还必须转换为具体的计算机程序设计语言描述的算法,即转换为具体的程序。 一般而言,计算机程序设计语言描述的算法(程序)是清晰的、简明的,最终也能由计算机处理的。然而,就使用计算机程序设计语言描述算法而言,它还存在以下几个缺点: (1)算法的基本逻辑流程难于遵循。与自然语言一样,程序设计语言也是基于串行的,当算法的逻辑流程较为复杂时,这个问题就变得更加严重; (2)用特定程序设计语言编写的算法限制了与他人的交流,不利于问题的解决;
(3)要花费大量的时间去熟悉和掌握某种特定的程序设计语言;(3)要花费大量的时间去熟悉和掌握某种特定的程序设计语言; (4)要求描述计算步骤的细节,而忽视算法的本质。 下面,分别给出求解例4.4、例4.5和例4.6的计算机程序设计语言(C语言)的算法描述。 (1)求解例4.4的计算机程序设计语言(C语言)的算法描述: main() {int X,Y; X=1; Y=2; while<=100) { X=X+Y; Y=Y+1; }; printf("%d",X); }
(2)求解例4.5的计算机程序设计语言(C语言)的算法描述:(2)求解例4.5的计算机程序设计语言(C语言)的算法描述: main() { int n; float X,I; printf("Please input n:"); scanf("%d",&n); X=0; I=1; do { X=X+1/I; I=I+1; }while(I<=n); printf("\n%f",X); }
(3)求解例4.6的计算机程序设计语言(C语言)的算法描述:(3)求解例4.6的计算机程序设计语言(C语言)的算法描述: main() { int X,Y,Z,I,j,n; printf("please input n:"); scanf("%d",&n); printf("\n"); if (n==0) { Y=0; printf("%d " , Y); }
else { X=0; Y=1; printf("%d %d ", X ,Y); for(I=1;I<=n-1;I++) { Z=X+Y; X=Y; Y=Z; printf("%d ", Y); } } }
4.2.5 算法分析 解一个问题,往往有若干不同的算法,这些算法决定着根据该算法编写的程序性能的好坏。那么,在保证算法正确性的前提下,如何确定算法的优劣就是一个值得研究的课题。 在算法的分析中,一般应考虑以下3个问题: (1)算法的时间复杂度; (2)算法的空间复杂度; (3)算法是否便于阅读、修改和测试。 算法时间复杂度是指算法中有关操作次数的多少,它用T(n)表示,T为英文单词Time的第一个字母,T(n)中的n表示问题规模的大小。如在累加求和中,n表示待加数的个数;在矩阵相加问题中,n表示矩阵的阶数;在图中,n表示顶点数等。
在算法的复杂度分析中,经常使用一个记号(读作“大”),该记号是保罗·巴克曼(P.Bachmann)于1892年在《解析数论》(Analytische Zahlentheorie)一书引进的,它为Order(数量级)的第一个字母,它允许使用“=”代替“≈”。如n2+n+1=(n2),该表达式表示,当n足够大时表达式左边约等于n2。 设f(n)是一个关于正整数n的函数,若存在一个正整数n0和一个常数C,当n≥n0时,∣T(n)∣≤∣C f(n)∣均成立,则称f(n)为T(n)的同数量级的函数。于是,算法时间复杂度T(n)可表示为: T(n)=(f(n))
常见的大表示形式有: • (1)称为常数级; • (logn)称为对数级; • (n)称为线性级; • (nc)称为多项式级; • (cn)称为指数级; • (n!)称为阶乘级。 用以上表示方法,在第2章的梵天塔问题中,需要移动的盘子次数为h(n)=2n-1,则该问题的算法时间复杂度表示为(2n);例4.4的算法时间复杂度表示为(1);例4.5的算法时间复杂度表示为(n);例4.6的算法时间复杂度表示为(n)等等。 一般而言,对于较复杂的算法,应将它分成容易估算的几个部分,然后用的求解原则计算整个算法的时间复杂度,最好不要采用指数级和阶乘级的算法,而应尽可能选用多项式级或线性级等时间复杂度较小的算法。另外,还要在算法最好、平均和最坏的情况下区别执行效率的不同。
在阶乘级的算法中,如果问题规模n为10,则算法时间复杂度为10!(3 628 800)。若要检验10!种情况,设每种情况需要1毫秒的计算时间,则整个计算将需1小时左右。一般来说,如果选用了阶乘级的算法,则当问题规模等于或者大于10时,就要认真考虑算法的适用性问题。 算法的空间复杂度是指算法在执行过程中所占存储空间的大小,它用S(n)表示,S为英文单词Space的第一个字母。与算法的时间复杂度相同,算法的空间复杂度S(n)也可表示为:S(n)=(g(n))。
4.3 数据结构 数学模型有定量模型和定性模型两类之分。定量模型指的是可以用数值方程表示的一类模型,而定性模型则是指非数值性的数据结构(如表、树和图等)及其运算。 在计算领域中,数据结构(Data Structure)指的是一类定性数学模型,它是计算机算法设计的基础,它在计算科学中占有十分重要的地位。本节将介绍数据结构的基本概念和常用的几种数据结构,如线性表、数组、树和二叉树以及图等。
4.3.1 数据结构的基本概念 1.数据结构的组成 数据结构是一类定性的数学模型,它由数据的逻辑结构、数据的存储结构(或称物理结构)及其运算3部分组成。 2.数据逻辑结构的形式化定义 DS=<D,R> 其中: D表示数据的集合; R表示数据D上关系的集合。
3.数据的存储结构 数据的存储结构是指在反映数据逻辑关系的原则下,数据在存储器中的存储方式。数据存储结构的基本组织方式有顺序存储结构和链式存储结构。 (1)顺序存储结构:借助元素在存储器中的相对位置来表示数据元素的逻辑关系。 (2)链式存储结构:借助指针来表示数据元素之间的逻辑关系,通常在数据元素上增加一个或多个指针类型的属性来实现这种表示方式。
4.数据结构的基本运算内容 (1)建立数据结构; (2)清除数据结构; (3)插入数据元素; (4)删除数据元素; (5)更新数据元素; (6)查找数据元素; (7)按序重新排列; (8)判定某个数据结构是否为空,或是否已达到最大允许的容量; (9)统计数据元素的个数。
4.3.2 常用的几种数据结构 1.线性表 线性表是n个数据元素的有限序列,即(X[1],X[2],X[3],…,X[i],…,X[n])。插入、删除和存取数据元素是所有数据结构的基本操作,若对线性表的这些基本操作加一定的限制,则形成下面几种特殊的线性表: (1)后进先出(Last In First Out,简称LIFO)的线性表。它的所有插入、删除和存取都是在线性表的表尾进行的; (2)先进先出(First In First Out,简称FIFO)的线性表。它的所有插入都是在线性表的一端进行的,而所有的删除和存取又都在线性表的另一端进行; (3)限定所有插入、删除和存取都在表的两端进行的线性表。 2.数组 数组是线性表的推广形式之一。如在一个mn的二维数组中,每个元素A[i,j]都分别属于两个线性表,即(A[i,1],A[i,2],…,A[i,n])和(A[1,j],A[2,j],…,A[m,j])。
3.树和二叉树 树和二叉树是一种具有层次关系的非线性结构,在计算机领域中有广泛的应用,尤其以二叉树最为常用。 (1)树 树(Tree)是由n(n≥0)个结点组成的有限集合。若n=0,则称为空树,任何一个非空树均满足以下两个条件: ① 仅有一个称为根的结点; ② 当n>0时,其余结点可分为m(m≥0)个互不相交的有限集合,其中每个集合又是一棵树,并称为根的子树。 例如,图4.4所示的树中有12个结点,A是根结点,该树又可再分为若干不相交的子树,如T1={B,E,F,K},T2={C,G},T3={D,H,I,J,L}等。
图4.4 树 (2)二叉树 二叉树是n(n≥0)个结点组成的有限集合,它或者是空集(n0),或者由一个结点及两棵互不相交的子树组成,且这两个子树有左、右之分,其次序不能任意颠倒。
4.图 图是由结点和连接这些结点的边所组成的集合。在图形结构中,结点之间的关系可以是任意的,图中任意两个数据元素之间都可能相关。 图的形式化定义为:G=<V,E> 其中: V是一个非空结点的集合; E是连结结点的边的集合。 例 G=<V,E> 其中V={A,B,C,D}, E={(A,B),(A,C),(B,D),(B,C),(D,C),(A,D)}。 该图也可以用图4.5来表示。 图4.5 图的表示
4.4 程 序 “程序”一词,从广义上讲可以认为是一种行动方案或工作步骤。在日常生活中,我们经常会碰到这样的程序:某个会议的日程安排、一条旅游路线的设计、手工小制作的说明书等,这些程序表示的都是我们在做一件事务时按时间的顺序应先做什么后做什么。 本书所指的程序特指计算机的程序,也是一种处理事务的时间顺序和处理步骤。由于组成计算机程序的基本单位是指令,因此,计算机程序就是按照工作步骤事先编排好的、具有特殊功能的指令序列。 一个程序具有一个单一的、不可分的结构,它规定了某个数据结构上的一个算法。瑞士著名计算机科学家尼可莱·沃思(Nikiklaus Wirth)在1976年曾提出这样一个公式: 算法+数据结构=程序
由此看来,我们前面提到的算法和数据结构是计算机程序的两个最基本的概念。算法是程序的核心,它在程序编制、软件开发,乃至在整个计算机科学中都占据重要地位。数据结构是加工的对象,一个程序要进行计算或处理总是以某些数据为对象的,而要设计一个好的程序就需将这些松散的数据按某种要求组成一种数据结构。然而,随着计算机科学的发展,人们现在已经意识到程序除了以上两个主要要素外,还应包括程序的设计方法以及相应的语言工具和计算环境等内容。
4.5 软 件 软件是与程序密切相关的一个概念,在计算机发展的初期,硬件设计和生产是主要问题,那时的软件就是程序。后来,随着计算技术的发展,传统软件的生产方式已不适应发展的需要,于是人们将工程学的基本原理和方法引入软件设计和生产中。现在计算机软件一般指计算机系统中的程序及其文档,也可以指在研究、开发、维护,以及使用上述含义下的软件所涉及的理论、方法、技术所构成的分支学科。软件一般分为系统软件、支撑软件、应用软件3类。 (1)系统软件是计算机系统中最靠近硬件层次的软件。如操作系统、编译程序等。 (2)支撑软件是支撑其他软件的开发与维护的软件。如数据库管理系统、网络软件、各种接口软件和开发工具等。 (3)应用软件是特定应用领域的专用软件。如商业会计软件、教学软件等。
4.6 硬 件 计算机硬件是构成计算机系统的所有物理器件、部件、设备,以及相应的工作原理与设计、制造、检测等技术的总称。广义的硬件包含硬件本身及其工程技术两部分。其中: 计算机系统的物理元器件包括:集成电路、印制电路板,以及其他磁性元件、电子元件等。 计算机系统的部件和设备包括:控制器、运算器、存储器、输入输出设备、电源等。 硬件工程技术包括:印制电路板制造、高密度组装、抗环境干扰、抗恶劣环境破坏等技术,以及在设计和制造过程中为提高计算机性能所采取的措施等。 计算机就是由计算机硬件和计算机软件组成的。硬件是计算机的“躯体”,软件是计算机的“灵魂”。
4.7 数据存储和表示 计算机用位的形式来表示数据。位(binary digit,二进制数字,缩写为bit)是存储在计算机中的最小数据单位,位表示二进制数字的0或1,8位表示1个字节(byte)。存储一个位需要用一个有两种状态的设备。例如,电子开关就能表示并存储位,通常用“开”(合上)状态表示“1”,用“关”(断开)表示“0”。现代计算机使用各种各祥的两态设备来存储数据。 在计算机中,数据和指令都是用二进制代码来表示的。二进制数的每一位只能是数字0或1,它只有形式的意义,对于不同的应用,可以附给它不同的含义。因此,可以用它来表示数值、字符、图像甚至声音,对这些数据,需要进行相应的编码。在需要呈现给用户时,再对它们进行解码。 下面分别介绍进位制数及其相互转换,原码、反码和补码及其转换,以及字符、字符串和汉字,图像和声音等数据的表示。
4.7.1 进位制数及其相互转换 在计算机中,只能存二进制数,因此,要保存一个十进制(Decimal)的数,也就是要保存一个对应的二进制数。由于计算机很难确定在内存中一个值的结束位置和另一个值的起始位置,因此,大多数计算机和程序都采用固定的宽度来避免这个问题,即每一个数都用相同的位数来表示。常用的有16位、32位和64位。 在一个16位的计算机中,若一个十进制数转换为二进制数后,不够16位,则在这个二进制数前加0,直到满16位为止。比如,十进制数2,其二进制数为10,在10前加14个0,即为:0000000000000010。 在实际的程序中,由于二进制数不直观。因此,在程序的输入和输出中,一般仍采用十进制数,而在分析计算机内部工作时,常用十六进制(Hexadecimal)数,这样,就要进行相应的转换。下面,分别介绍十进制与二进制,以及二进制与八进制(Octal),二进制与十六进制等几种常用的进位制数的表示及其相互转换。
1.十进制数与二进制数之间的转换 我们知道在表示十进制数的时候,从右边起的第1个数字是1(100 = 1),第2个数字是10(101 = 10),第3个数字是100(102 = 100),第4个数字是1000(103 = 1000),以此类推。如果我们要把十进制整数3254展开,我们很容易知道存在如下的关系,3254=3*103 +2*102 +5*101 +4*100。 这样的关系同样存在二进制数中,只是这里的基数不是10,而变成了2。这时,从右边起的第1个数字就是1(20 = 1),第2个数字就是2(21 = 2),第3个数字是4(22 = 4),第4个数字就是8(23 = 8)了。从这一点出发,我们很容易找到十进制和二进制转换的方法。 (1)十进制数转换为二进制数
若R表示十进制整数,Kn-1,Kn-2,Kn-3 … K1,K0表示二进制数的各位数,最低(右)端一位为K0, 最高(左)端一位为Kn-1。为了区分十进制数和二进制数,我们分别给十进制数和二进制数加上下标10和2,即有如下形式 (R)10 = (Kn-1Kn-2Kn-3…K1K0)2 ① 与上文提到的十进制的展开类似,我们可以将上式写为 (Kn-1Kn-2Kn-3…K1K0)2= Kn-1*2n-1 +Kn-2*2n-2+Kn-3*2n-3+…+K1*21+K0*20 ② 显然,从等式右边看,除最后一项K0以外,其余每项都包含有2的因子,它们都能2除尽。故R除以2,它们的余数即为K0,商变为Kn-1*2n-2+Kn-2*2n-3+Kn-3*2n-4+K1*20,将得到的商再除以2,余数即为K1,商变为Kn-1*2n-3 +Kn-2*2n-4+Kn-3*2n-5+…+K2*20,这样依次下去,分别得到K2,K3,K4 … Kn ,最终得到二进制数的各位数。十进制数126的转换如下图所示。
根据上图,十进制数126可以用二进制数1111110表示(这里只讨论数之间的转换,没有涉及标准格式的存储,为了方便,如无特殊说明,不用0去补足计算机的实际位数)。 (126)10 = (1111110)2 (2)二进制数转换为十进制数 二进制数转换为十进制数比较简单,由①式和②式我们很容易得到
(R)10 = Kn-1*2n-1 +Kn-2*2n-2+Kn-3*2n-3+…+K1*21+K0*20 我们只要简单地将每一位与其对应为的2的幂次方相乘,然后求和。比如 (1111110)2 = 1*26+1*25+1*24+1*23+1*22+1*21+0*20 = 64+32+16+8+4+2+0 =(126)10。 这样,就完成了从二进制数向十进制数的转换。以下是几个计算机中常用的二进制数和十进制数转换的例子。
2.八进制数与二进制数之间的转换 因为23 = 8 , 所以1位八进制数相当于3位二进制数。利用这一点,我们可以将每位八进制数用3位对应的二进制数来表示,完成八进制数向二进制数的转换;将二进制数每3位表示成1位八进制数,完成二进制数向八进制数的转换。下面,通过两个例子说明八进制和二进制之间的转换。 (1)八进制数转换为二进制数