高精度POJ1001

今天看到這道題了 poj1001 題目地址是http://bailian.openjudge.cn/practice/1001/html

英文看得懂,但是算法不明白,因此轉別人的文章,留着給學生看看:喬高建(高精度java

 

  • 解題思路

    這道題屬於高精度乘法運算,要求輸入一個實數R一個指數N,求實數R的N次方,因爲R有5個數位,而N又特別大,所以用C++自帶的數據類型放不下.
    解題思路是經過數組儲存每次乘積結果和底數的每一位數,按照乘法上下算式的方法,計算底數乘數數組每一位與臨時結果數組的每一位的乘積,(由於算術運算中是從數的後面往前算的,這裏存儲數時要先倒序,輸出時再顛倒過來,)而後偏移相加,判斷得出的臨時結果數組的每一位是否大於9,經過除法和取模實現進位和取餘.至此得出一個有不少無效數位的結果數組(不少無效的0).
    最後判斷結果數組的每一位是否爲0,先輸出小數點前面的數,後輸出小數點後面的數,最終得出乘法結果.
    這個題目實際上考的是高精度乘法,高精度的其餘運算和這個差很少,原理都是同樣的.
    解題思路ios

  • Description

    Problems involving the computation of exact values of very large magnitude and precision are common. For example, the computation of the national debt is a taxing experience for many computer systems.
    This problem requires that you write a program to compute the exact value of Rn where R is a real number ( 0.0 < R < 99.999 ) and n is an integer such that 0 < n <= 25.算法

  • Input

    The input will consist of a set of pairs of values for R and n. The R value will occupy columns 1 through 6, and the n value will be in columns 8 and 9.數組

  • Output

    The output will consist of one line for each line of input giving the exact value of R^n. Leading zeros should be suppressed in the output. Insignificant trailing zeros must not be printed. Don't print the decimal point if the result is an integer.markdown

  • Sample Input

    95.123 12
    0.4321 20
    5.1234 15
    6.7592 9
    98.999 10
    1.0100 12函數

  • Sample Output

    548815620517731830194541.899025343415715973535967221869852721
    .00000005148554641076956121994511276767154838481760200726351203835429763013462401
    43992025569.928573701266488041146654993318703707511666295476720493953024
    29448126.764121021618164430206909037173276672
    90429072743629540498.107596019456651774561044010001
    1.126825030131969720661201
  • Hint

    If you don't know how to determine wheather encounted the end of input:
    s is a string and n is an integerpost

  • SourceCode

#include<iostream> #include<cstring> using namespace std; int main() { string r; //底數 int n,dianwei; //指數,小數點位置 const int len=200; //數位長度 short result[len],jieguo[len],chengshu[6]; //最終結果,臨時結果,底數乘數 while(cin>>r>>n) { //初始化 for(int i=0;i<len;++i) jieguo[i]=result[i]=0; for(int i=0;i<6;++i) chengshu[i]=0; dianwei=0; //獲得底數小數點位置 size_t pos = r.find('.'); //若是底數中有小數點 獲取小數點後面有多少位數 if(pos!=string::npos) dianwei=(5-pos)*n; //把底數中全部不是小數點的數字挑出來轉換爲int並賦給最終結果,臨時結果,底數乘數 獲得的是3個5位先後顛倒的數組 之因此顛卻是由於乘法是從後往前乘的 for(int i=5,j=0;i>=0;--i) { if(r[i]!='.') { jieguo[j]=result[j]=chengshu[j]=r[i]-'0'; ++j; } } //當指數大於1時 進行如下運算 等於1時跳過這段程序直接輸出 while(n>=2) { --n; //初始化最終結果數組 for(int i=0;i<len;++i) result[i]=0; for(int i=0;i<5;++i) //從底數乘數的每一位 { //底數乘數每位數和臨時結果每位數相乘的臨時變量 int temp; for(int j=0;j<len;++j) //乘以臨時結果的每一位 { //若是底數乘數某一位是0 不必乘下去了 跳出當前循環 if(chengshu[i]==0) break; temp=chengshu[i]*jieguo[j]; //i+j實現乘法相加時的移位 result[i+j]+=temp; //++t遍歷全部結果數組中大於9的數 用除法和取模實現進位和餘數 for(int t=i+j;result[t]>9;++t) { result[t+1]+=result[t]/10; result[t]=result[t]%10; } } } //把一次乘法後的結果賦給臨時結果來進行下次乘方 for(int i=0;i<len;++i) jieguo[i]=result[i]; } //獲取最終結果從後數第一個不爲0的數做爲第一個數的標誌位 之因此從後數 是由於以前顛倒的數要顛倒回來了 int firstindex=-1; for(int i=len;i>=dianwei;--i) { if(result[i]>0) { firstindex=i; break; } } //獲取 最終結果從前數第一個不爲0的數做爲最後一個數的標誌位 int lastindex=-1; for(int i=0;i<dianwei;++i) { if(result[i]>0) { lastindex=i; break; } } //若是最終結果數組中不全是0 倒序輸出結果數組中小數點前面的數 if(firstindex!=-1) { while(firstindex>=dianwei) { cout<<result[firstindex]; --firstindex; } } //若是最終結果數組中不全是0 倒序輸出結果數組中小數點後面的數 if(lastindex!=-1) { cout<<'.'; --dianwei; while(dianwei>=lastindex) { cout<<result[dianwei]; --dianwei; } } cout<<endl; } } 

#附錄:ui

一.高精度數的存儲

1.字符串輸入

#include <iostream> #include <cstring> using namespace std; const int N=100;//最多100位 int main() { int a[N+1],i; string s1; cin>>s1;//數s1 memset(a,0,sizeof(a)); //數組清0 a[0]=s1.length(); //位數 for(i=1;i<=a[0];i++) { a[i]=s1[a[0]-i]-'0';//將字符轉爲數字並倒序存儲. } return 0; }

2.直接讀入

#include <iostream> using namespace std; const int N=100;//最多100位 int main() { int a[N+1],i,s,key; cin>>key;//數key memset(a,0,sizeof(a)); //數組清0 i=0;//第0位 while(key) //當key大於0 { a[++i]=key%10;//取第i位的數 key=key/10; } a[0]=i; //共i位數 return 0; }

3.直接初始化(用a[]存儲)

  • 初始化爲0: memset(a,0,sizeof(a));
  • 初始化爲1: memset(a,0,sizeof(a));a[0]=1;a[1]=1;

如下程序都只寫函數,不寫完整程序,全部高精度數存儲都知足上述約定。spa

二.高精度數比較

int compare(int a[],int b[]) //比較a和b的大小關係,若a>b則爲1,a<b則爲-1,a=b則爲0 { int i; if (a[0]>b[0]) return 1;//a的位數大於b則a比b大 if (a[0]<b[0]) return -1;//a的位數小於b則a比b小 for(i=a[0];i>0;i--) //從高位到低位比較 { if (a[i]>b[i]) return 1; if (a[i]<b[i]) return -1; } return 0;//各位都相等則兩數相等。 }

3、高精度加法

int plus(int a[],int b[]) //計算a=a+b {int i,k; k=a[0]>b[0]?a[0]:b[0]; //k是a和b中位數最大的一個的位數 for(i=1;i<=k;i++) { a[i+1]+=(a[i]+b[i])/10; //如有進位,則先進位 a[i]=(a[i]+b[i])%10; //計算當前位數字,注意:這條語句與上一條不能交換。 } if(a[k+1]>0) { a[0]=k+1; //修正新的a的位數(a+b最多隻能的一個進位) } else { a[0]=k; } return 0; }

4、高精度減法

int gminus(int a[],int b[]);//計算a=a-b,返加符號位0:正數 1:負數 { int flag,i flag=compare(a,b); //調用比較函數判斷大小 if (falg==0)//相等 { memset(a,0,sizeof(a));return 0; //若a=b,則a=0,也可在return前加一句a[0]=1,表示是 1位數0 } if(flag==1) //大於 { for(i=1;i<=a[0];i++) { if(a[i]<b[i]){ a[i+1]--;a[i]+=10;} //若不夠減則向上借一位 a[i]=a[i]-b[i]; } while(a[a[0]]==0) a[0]--; //修正a的位數 return 0; } if (flag==-1)//小於 則用a=b-a,返回-1 { for(i=1;i<=b[0];i++) { if(b[i]<a[i]){ b[i+1]--;b[i]+=10; //若不夠減則向上借一位 } a[i]=b[i]-a[i];} a[0]=b[0]; while(a[a[0]]==0) a[0]--; //修正a的位數 return -1; } }

5、高精度乘法(高精度乘單精度數,單精度數是指一般的整型數)

int multi1(int a[],long key) //a=a*key,key是單精度數 { int i,k; if (key==0){memset(a,0,sizeof(a));a[0]=1;return 0;} //單獨處理key=0 for(i=1;i<=a[0];i++) { a[i]=a[i]*key;//先每位乘起來 } for(i=1;i<=a[0];i++) { a[i+1]+=a[i]/10;a[i]%=10; //進位 } //注意上一語句退出時i=a[0]+1 while(a[i]>0) { a[i+1]=a[i]/10;a[i]=a[i]%10;i++;a[0]++]; //繼續處理超過原a[0]位數的進位,修正a的位數 } return 0; }
 
分類:  ACM