2.4 进制转换
2.4 进制转换
日常生活中我们计数的方式有很多,如一年有12个月,则是十二进制,一周有七天,则是七进制,等等。平常我们用的最多的最习惯的十进制,是古人留下来的财富。需要强调的是任何一个值都可以用任何一种进制描述,但它的值是不变的,正如我们今天在一周中可以描述为星期几,在一个月中描述为多少号一样。
使用R进制计数的规则:
只使用R个基数:0,1,2,···,R-1;
逢R进一,退一当R进行数的运算。
2.4.1 R进制数转化为十进制数
这个转化问题较简单,根据上面讲的R进制的计数规则进行展开就得到相应的十进制数的表示方法。
(anan-1···a1a0.a-1a-2···a-m)R
=an*Rn+an-1*Rn-1+···+a1*R1+a0*R0+a-2*R-2+···+a-m*R-m
= ai*Ri。
2.4.2 十进制数转化为R进制数
由于十进制数的整数与小数转化为R进制的方法不同,所以必须分开讨论。先看十进制整数的转化,再讨论十进制小数的转化,最后讨论R进制的计数及转化问题。
1. 进制整数的转化
通过具体实例进行分析,如对十进制数325转化,根据原理可
按下式这样假设:
(325)10=3*102+2*101+5*100
=(anan-1…a1a0)R
=an*Rn+an-1*Rn-1+…+a1*R1+a0*R0
=(an*Rn-1+an-1*Rn-2+…+a1)*R+a0
两边同时除以R,得到整数部分和整数部分相等,余数和余数相等,显然右边的余数就是a0,再进行同样的处理就得到a1,一直这样进行下去,直到左边的数为0时为止,由于先求出的是R进制的最低位,再按求解过程倒过来写出就得到相应的R进制数。
以R=6为例,(325)10=(1301)6,转化过程详见图2-1所示。
十进制数转化为R进制数,得到整数部分的转换规律就是“除R取余,反序输出”。
2. 十进制小数的转化
通过具体实例进行分析,如对十进制数0.325转化,根据原理
以这样假设:
(0.375)10=3*10-1+7*10-2+5*10-3
=(0.a-1a-2…a-m)R
=a-1*R-1+a-2*R-2+…+a-m*R-m
=(a-1+a-2*R-1+…+a-m*R-m+1)*R-1
两边同时乘以R,等式两边的整数部分和小数部分分别相等,显然右边的整数部分就是a-1,再去掉等式的整数部分,然后进行相同的处理,就求得了a-2,一直进行下去,直到左边的值为0时或到要求的精度为止,这样就将十进制小数转化为相应的R进制数了。
以R=2为例,(0.375)10=(0.011)2,求解过程详见图2-2所示,这样就将十进制小数转化为相应的R进制数,得到小数部分的转换规律就是“乘R取整,顺序输出”。
3.进制数转化-R进制数
大家对R进制数都已经很熟悉了,但是,还有一种-R进制数。任何一个整数n都可以表示成n=,其中∈〔0,R-1〕,是整数。并且>=0。
现在我们来讨论R进制数怎么转化为-R进制数。需不需要先将R进制数转化为十进制数,
再将相应的十进制数利用上面的转化归律化为-R进制数,当然这是一种方法,但我们完全可以不必这么做。不妨以一个具体实例来讨论转化规律,如:
=4*63+3*62+2*61+5*60。
将等式右边改写成一个-6进制数的形式:
4*(-6)3+3*(-6)2+2*(-6)1+5*(-6)0。
比较观察一下,发现偶次幂的项与6进制数的相等,差别出在奇次幂的项,怎样修改才使它满足-6进制数表示的形式呢?记住我们计算的原理:进制只是表示方式不同,值是不变的
那么对于上面我们倒数第二项:2*61=X*(-6)1,而基数X是一个0到5之间的数,显然是不能成立的,要相信,X只能等于-2,而-2不能作为-6进制数的基数,解决这个问题就向高位借一个1,这样X变为(6.-2),由于高位已经是相等的,所以高位的基数相应要加1。若高位基数加1后,值已超过5,则修改幂。这样就能确保值没有变。
2.4.3 应用举例
在信息学奥赛中,巧妙地使用某些数制的特点,可能使解题变得相当简单。请看下面几道例题。
例15 火车转轨问题。
图2-3中两条轨道连接到一个铁路转轨处,形成一个铁路转轨网络的栈。其中右边轨道为输入端。如果执行了Push,Push,Pop,Push,Push,Pop,Pop,Pop,就会将输入端的车皮编号顺序1,2,3,4变成2,4,3,1,请编程求左边车皮编号为1,2,3,4时,在右边轨道可能得到的所有车皮编号顺序。
【算法分析】
这个例子是典型的栈应用题。但如果单纯使用栈技术,则在不断的判定入栈出栈过程中,很容易迷失方向。现在让我们仔细分析,看看有没有其他方法可以解决此题:①四列车必须经过转轨才能到达轨道的另一端,且进栈出栈各一次;②如果以1代表进栈,0代表出栈,则火车转轨可以用一相二进制数来表示,如,10代表火车进栈后出栈,01代表火车出栈后进栈;③四列火车都到达转轨的另一侧,一共进出栈八次,恰好一个字节8个位;④由于必须先进栈才能出栈,故在二进制的每一位数前,1的个数总不少于0的个数,否则转轨内无车可出。这样就巧妙地利用数的进制将原问题转化为一个求0至255内的二进制数中符合相应条件的数的个数。
【参考程序】
CONST n=4; {火车轨程序};
VAR
a,b,c:array[1..n] of integer;
top,I:integer;
FUNCTION judge(m:integer):Boolean; {判断二进制数m是否满足条件④};
VAR
S0,s1:integer; {s0为0的个数,s1为1的个数}
I:integer;
BEGIN
Judge:=true;
S0:=0;
S1:=0;
FOR I:=1 to 2*n do
BEGIN {从后往前计算0和1的个数}
IF m mod 2 =0 THEN s0:=s0+1 ELSE s1:=s1+1;
M:=m div 2;
If s0<s1 THEN BEGIN {如果0的个数比1的个数少,则不满足条件}
Judge:=false;
Exit;
End;{THEN};
END;{FOR};
IF s0<>s1 THEN BEGIN {如果整个数的0与1的个数不相等,也不满足条件};
Judge:=false;
Exit;
END;{THEN}
END;{judge}
PROCEDURE push; {进栈}
VAR I:integer;
BEGIN
B[top]:=a[1];
Top:=top+1;
FOR I:=1 to n-1 DO a[I]:=a[I+1];
A[n]:=0;
Write(‘push’);
END;{push};
PROCEDURE pop; {出栈}
VAR I:integer;
BEGIN
Top:=top-1;
FOR I:=1 to n-1 DO c[I]:=c[I+1];
C[n]:=b[top];
Write(’POP’);
END;
PROCEDURE print(m:integer); {输出满足条件的情况}
VAR
T:array[1..2*n] of bollean;
I:integer;
BEGIN
Write(m,‘ ‘);
FOR I:=1 TO N DO A[I]:=I;
FOR I:=1 to 2*n DO
BEGIN
T[I]:=(m mod 2=0);
M:=m div 2;
END;{FOR};
FOR I:=2*n DOWNTO 1DO
IF t[I] THEN pop ELSE push;
FOR I:=I to DO write(c[I]);
Writeln;
END;
BEGIN {主程序}
Top:=1;
FOR I:=1 to 255 DO {8个位,最多为255}
IF judge(i) THEN print(i);
Writeln;
END.