轉載自:http://www.cppblog.com/kuangbin/archive/2011/08/25/154299.html
高精度的進制轉換
前面的內容來源於:http://www.cnblogs.com/phinecos/archive/2009/09/11/1564975.html
引用原文:
在數據結構課關於棧的這一章中,我們都學過用“模2取餘法”來將一個10進制數轉換爲一個二進制數,進而可以推廣到“模n取餘法”,經其轉換爲n進制(n任意指定)。
確實,這是一個很基礎的題目,可你是否想過如果這個10進制數是一個大數(其位數可能上千位,此時用一般數據類型肯定是會溢出的),那麼這個問題又如何來求解呢?
當然,也許你會說很簡單嘛,自己寫一個大數類(當然至少要寫一個大數除法才行),或者你用的是Java這種現代化語言,就更輕鬆了,直接用BigInteger這樣的大數類就可以來表示一個大數,進而用書上教的方法來實現。
但是,真的需要用到大數類嗎?事實上,“殺雞焉用牛刀“,我們在紙上模擬一番上述運算後就可以發現,只要做一些小小的改進,就可以在不使用大數的情況下,也可以通過“模n取餘”的原理來實現大數的進制轉換的。(當然,整體的思想仍然是“模n取餘”原理!!!)。
舉個簡單的例子,就比如說把10進制數12轉換爲2進制形式,書上的方法可以用下圖來表示
按照 “先餘爲低位,後餘爲高位“這條鐵律,其結果爲1100.
這是書上教我們的常規思路(可惜按這個的話,大數是沒法考慮的,因爲假如這裏不是12,而是一個1000位的大數,由於是是對大數的整體進行取餘運算,不使用大數類及其除法操作,又如何得以進行呢?),可我們的目的是不使用大數類,那麼現在我們就來換一個視角來看這個問題,12是一個十位數,十位上是1,個位上是2,按照我們正常的思維來看,這個計算應該是下面這樣的:
那麼我們發現在第一輪運算時,十位上的1作爲被除數,2作爲除數,得到的商是0,餘數是1(可以斷言只考慮當前這一個數位的計算,餘數或是0,或是1,若是1的話,則進入下一數位(這裏即對個位進行運算)時,要用1乘上進制(這裏是10)再加上下一個數位上的值(這裏是2)),即得到運算進入個位時被除數是12,除數是2,得到的商是6,餘數是0。第一輪運算的結果是商是06,餘數是0.
進入第二輪運算,則上一輪的商6(這裏首先要去掉前面多餘的0)變成本輪的被除數,如此下去,即可得到每輪的餘數。
推廣開來,如果被除數是一個1000位的大數,例如“12343435154324123……342314324343”
那麼我們照樣可以從第一個數位開始逐位考慮,比如第一位是1(作爲被除數),2是除數,得到的商是0,餘數是1,然後是第二個數位2,由於上一位留下了餘數1,則此時被除數應該是1*10+2 = 12,所以得到的商是6,餘數是0,即運算到此時的商是06,然後是第三個數位3,由於上一個數位留下的餘數是0,所以此時被除數就是3,。。。如此下去就完成第一輪的運算,
這一輪完畢後,需要把得到的商變成下一輪的被除數,繼續上述的運算,直到被除數爲0才停止。
下面給出了一個示例代碼,展示瞭如何將一個10進制的大數轉換爲其二進制形式,僅供參考:
#include <stdio.h> #include <string.h> char str[1000];//輸入字符串 int start[1000],ans[1000],res[1000]; //被除數,商,餘數 //轉換前後的進制 const int oldBase = 10; const int newBase = 2; void change() {//各個數位還原爲數字形式 int i,len = strlen(str); start[0] = len; for(i=1;i<= len;i++) { if(str[i-1] >= '0' && str[i-1] <= '9') { start[i] = str[i-1] - '0'; } } } void solve() { memset(res,0,sizeof(res));//餘數初始化爲空 int y,i,j; //模n取餘法,(總體規律是先餘爲低位,後餘爲高位) while(start[0] >= 1) {//只要被除數仍然大於等於1,那就繼續“模2取餘” y=0; i=1; ans[0]=start[0]; // while(i <= start[0]) { y = y * oldBase + start[i]; ans[i++] = y/newBase; y %= newBase; } res[++res[0]] = y;//這一輪運算得到的餘數 i = 1; //找到下一輪商的起始處 while((i<=ans[0]) && (ans[i]==0)) i++; //清除這一輪使用的被除數 memset(start,0,sizeof(start)); //本輪得到的商變爲下一輪的被除數 for(j = i;j <= ans[0];j++) start[++start[0]] = ans[j]; memset(ans,0,sizeof(ans)); //清除這一輪的商,爲下一輪運算做準備 } } void output() {//從高位到低位逆序輸出 int i; for(i = res[0];i >= 1;--i) { printf("%d",res[i]); } printf("\n"); } int main() { scanf("%s",str); change(); solve(); output(); return 0; }
個人根據POJ1220,總結高精度的N進制轉換模板如下:
/* 高精度進制轉換 把oldBase 進制的數轉化爲newBase 進制的數輸出。 調用方法,輸入str, oldBase newBase. change(); solve(); output(); 也可以修改output(),使符合要求,或者存入另外一個字符數組,備用 */ #include<stdio.h> #include<string.h> #defien MAXSIZE 1000 char str[MAXSIZE];//輸入字符串 int start[MAXSIZE],ans[MAXSIZE],res[MAXSIZE];//被除數,商,餘數 int oleBasw,newBase;//轉換前後的進制 //單個字符得到數字 int getNum(char c)//這裏進制字符是先數字,後大寫字母,後小寫字母的 { if(c>='0'&&c<='9') return c-'0';//數字 if(c>='A'&&c>='Z') return c-'A'+10;//大寫字母 return c-'a'+36;//小寫字母 } //數字得到字符 char getChar(int i) { if(i>=0&&i<=9)return i+'0'; if(i>=10&&i<=35)return i-'10'+'A'; return i-36+'a'; } void change()//把輸入的字符串的各個數位還原爲數字形式 { int i; start[0]=strlen(str);//數組的0位存的是數組長度 for(i=1;i<=start[0];i++) start[i]=getNum(str[i-1]); } void solve() { memset(res,0,sizeof(res));//餘數位初始化爲空 int y,i,j; while(start[0]>=1) { y=0;i=1; ans[0]=start[0]; while(i<=start[0]) { y=y*oldBase+start[i]; ans[i++]=y/newBase; y%=newBase; } res[++res[0]]=y;//這一輪得到的餘數 i=1;//找下一輪商的起始處,去掉前面的0 while(i<=ans[0]&&ans[i]==0) i++; memset(start,0,sizeof(start)); for(j=i;j<ans[0];j++) start[++start[0]]=ans[j]; memset(ans,0,sizeof(ans)); } } void output()//從高位到低位逆序輸出 { int i; for(i=res[0];i>=1;i--) printf("%d",getChar(res[i])); printf("\n"); }