三、进制转换
1、题目和要求
时间限制:1s,内存限制:32MB,特殊判题:否
2、总结
存储数据选择的方式不对,做了很长时间,到最后也没有求解出来。
例程用到了重载运算符,简化计算。
//用一个普通整数初始化高精度整数
void set(int x){
do{
digit[size++] = x%10000;
x /= 10000;
}while(x!=0)
}
1)字符串与数字连接,使用了<sstream>
头文件,具体使用方法参见:c++ 拼接字符串和数字。这里为什么不使用字符串直接相加(str_shang += tmp_c/N + '0'
)的方式?因为,求出来的商,可能是两位以上的数字。
2)这里有一个非常重要的地方:ss << str_shang <<setfill(‘0’)<<setw(4)<< tmp_c/N;
比如,十进制数1,1112,0011转换为八进制数647 707 213(为了方便观察,逗号的标记和常用方式不一样),求八进制中的3
时,运算过程是这样的:
- 第一组:1除以8商为0,余数为1。由于判断条件
if(tmp_c/N != 0)
,所以str_shang
的值没有改变,为空; - 第二组:由于
tmp_c = numM.digit[i] + tmp_y*10000;
所以现在的被除数是1,1112。1,1112除以8商为1389,余数为0。此时str_shang
为1389; - 第三组:0011除以8商为1,余数为3。如果不加
ss << str_shang <<setfill('0')<<setw(4)<< tmp_c/N;
这句话,那么str_shang
为1,但实际因为前面几位除以8都为0,且0不做开头,所以商的真实值里,不应该缺0。
或许可以把第73行的if(tmp_c/N != 0)
注释掉,但是,在不改动其它部分的情况下,不可以!去掉的话,如果tmp_c/N
为0时,str_shang
中的值会是四个0,而循环的判断条件while(str_shang != "")
就永远无法成立,陷入死循环。
当然,对程序其他部分做做改动,去掉这个也是可以的。
这个好像跟上面没有关系,既然写上了,舍不得删了……
3)这道题采用第一种代码中将字符串从后往前,转换为四位一组的方式有很大弊端。对于其他的进制转换,这种方式求解不成立! 详解见下方:
16进制数(如:11,ABCD)需要存储时
- 首先需要计算出后四位的结果:13×1+12×16+11×16×16+10×16×16×16=43,981
- 由于进制转换时都是将其他进制转换为10进制,然后再将10进制转换为其他进制。所以,四位十进制数,最大是9999。43,981对10000取余,就是需要保存的值(3,981),43,981除以10000,就是需要进位的值(4)。
- 接下来,先按照十进制数存储时的方法进行存储,之后解释一下发生了什么错误。
- 计算接下来两位的十进制结果:1×1+1×16
+4
=21(这里的4是上一步的进位)。所以,下一个digit
中的值应为21。
但是,1×1+1×16+4
这样的算法真的对吗?数学中,这两位的计算结果应该是1×16^4 + 1×16^5 = 1,114,112
,和4
相加的应该是111
而不是1×1+1×16=17
,并且!!!1111
后面还有数字,应该和刚才求得的43,981
中的3,981
进一步求和。如此进行下去,如果还有16^10、16^20、16^100
,很有可能会超过数据类型所能计算的范围,因此这种做法在除了十进制以外的其他进制中,不能适用。
这样的做法在十进制中是成立的,是因为10的次方的后几位永远是零,相加对原来的数据没有影响。
3、思路
1)由于输入输出时可能有大写字母出现,所以采用字符串方式存储。
2)可能有大数据,所以用自定义BigInteger
数据类型存储输入字符串转换后的数据。
3)下面的算法实现了十进制转其他进制,主要思路是:
- 使用
string
类型num
存储输入的数据 - 使用
void str2BigInteger(string str, int weight, BigInteger &b)
函数将string
类型数据从后往前,每四位转换为BigInteger.digit[]
中的一项。(函数的主要工作流程是:取str[i]
,将字符类型变为整型;乘以该位的权重,与当前已有的数据相加;更新权重。当已经凑够四位数字或已经到达最开始的1、2、3位数字后,需要把相加的结果存起来:注意向前进位)。 - 重头戏,进制转换!!!进行这里之前需要先回想一下数学中是如何进行进制转换的:所有位的数据均参与除法运算,得到的余数是转换后的结果,注意求解顺序和最后结果顺序是相反的。
- 明确如何计算以后,参照
do-while
循环里面的注释,就不难理解了。
4、代码
自己写的拙劣的代码,只能完成一部分功能。
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
using namespace std;
struct BigInteger{
int size;
int digit[1000];
BigInteger(){
size = 0;
}
}numM;
void str2BigInteger(string str, int weight, BigInteger &b){
int c = 1,num = 0,i,carry = 0;
int tmp;
//从最后一位开始,从后往前存在digit[]的从前往后大小内
for(i = str.size()-1; i>=0; i--){
//由于str中可能出现字母,所以需要换成相应的十进制数
tmp = (str[i] >= 'A') ? (str[i] - 'A' + 10) : (str[i] - '0');
num += tmp * c;
c *= weight;
if(c > weight*weight*weight || i == 0){
if(numM.size != 0){
num = (num*weight*weight*weight*weight + carry * 10000)/10000;
}
carry = num/10000;
numM.digit[numM.size++] = num%10000;
c = 1;
num = 0;
}
}
}
void printBigInteger(BigInteger b){
for(int i = b.size-1; i>=0; i--){
if(i == b.size-1){
cout<<b.digit[i];
}else{
cout<<setfill('0')<<setw(4)<<b.digit[i];
}
}
cout<<endl;
}
//M,N,num 代表题中输入的数据
//tmp_y,tmp_c 代表每一个digit[i]求得的余数和商
//output,str_shang 代表存到进制转换后的字符串、整个数的商
int main(){
int M=10,N=8,tmp_y=0,tmp_c,tmp;
string num="11112001111",output = "",str_shang="";
//cin>>M>>N>>num;
//cin>>num;
str2BigInteger(num, M, numM);
printBigInteger(numM);
do{
str_shang = "";
//要想求得整个数的商,需要把整个数组遍历一遍
//实际数据:ABCDEFG;digit[]:DEFG ABC ,因此从后往前遍历digit[]以求得商和余数。
//商需要按照 str2BigInteger()方法再存入digit[]
//余数作为最后结果,需要按F的值进行转换(F>10时,11->A),存入output中
// 余数的求解顺序,先求的最后一位,从后往前逐个求解,保存在output中,所以输出需要倒序输出output中内容
for(int i = numM.size-1; i>=0; i--){
//求商时:
//numM.digit[i]需要加上 numM.digit[i-1]的余数,再继续进行运算
tmp_c = numM.digit[i] + tmp_y*10000;
tmp_y = tmp_c % N;
//最小是五位数,五位数除以两位数,肯定不为0
if(tmp_c/N != 0){
stringstream ss;
ss << str_shang <<setfill('0')<<setw(4)<< tmp_c/N;
str_shang = ss.str();
}
}
//求余数时:
//需要按F的值进行转换
output += (tmp_y >= 10) ? (tmp_y - 10 + 'A') : (tmp_y + '0');
if(str_shang != ""){
numM.size = 0;
str2BigInteger(str_shang, M, numM);
tmp_y=0;
}
}while(str_shang != "");
for(int i = output.size()-1; i>=0; i--){
cout<<output[i];
}
cout<<endl;
}