北大ACM 1001题:求高精度幂-------http://blog.sina.com.cn/s/blog_498c588d0100vwjg.html

 
 

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 R n  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.

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 integer 
    
    
C++

while(cin>>s>>n)

{

...

}

c

while(scanf("%s%d",s,&n)==2) //to  see if the scanf read in as many items as you want

/*while(scanf(%s%d",s,&n)!=EOF) //this also work    */

{

...

}

Source




求高精度幂

Description

对数值很大、精度很高的数进行高精度计算是一类十分常见的问题。比如,对国债进行计算就是属于这类问题。  现在要你解决的问题是:对一个实数R( 0.0 < R < 99.999 ),要求写程序精确计算 R 的 n 次方(R n),其中n 是整数并且 0 < n <= 25。

Input

T输入包括多组 R 和 n。 R 的值占第 1 到第 6 列,n 的值占第 8 和第 9 列。

Output

对于每组输入,要求输出一行,该行包含精确的 R 的 n 次方。输出需要去掉前导的 0 后不要的 0 。如果输出是整数,不要输出小数点。

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 
 
分析:
在计算机上进行高精度计算,首先要处理好以下几个基本问题:
 1、数据的接收与存储; 
 2、计算结果位数的确定; 
 3、进位处理和借位处理; 
 4、商和余数的求法;  

 
输入和存储 
运算因子超出了整型、实型能表示的范围,肯定不能直接用一个数的形式来表示。能表示多个数的数据类型有两种:数组和字符串。 
(1)数组:每个数组元素存储1位(在优化时,这里是一个重点!),有多少位就需要多少个数组元素;优点:每一位都是数的形式,可以直接加减;运算时非常方便缺点:数组不能直接输入;输入时每两位数之间必须有分隔符,不符合数值的输入习惯; 
(2)字符串:字符串的最大长度是255,可以表示255位。优点:能直接输入输出,输入时,每两位数之间不必分隔符,符合数值的输入习惯;缺点:字符串中的每一位是一个字符,不能直接进行运算,必须先将它转化为数值再进行运算;运算时非常不方便(注意‘0’的问题)。 

 

 
优化: 
一个数组元素存放四位数 
     注意:是加减法时可以用interger,但是当是乘法的时候,就要用int64,否则会出现越界的情况。 
     还有就是:输出时对非最高位的补零处理。 

 
另一个问题: 
存储顺序 
  正序?? 
  逆序?? 

 
还有就是一定不要忘了初始化!! 
计算结果位数的确定 
两数之和的位数最大为较大的数的位数加1。 
乘积的位数最大为两个因子的位数之和。 
阶乘:lgn!=lgn+lg(n-1)+lg(n-2)...................+lg3+lg2+lg1 
=lnn/ln10+ln(n-1)/ln10+ln(n-2)/ln10+................+ln3/ln10+ ln2/ln10+ln1/ln10 
=trunc(1/ln10*(lnn+ln(n-1)+ln(n-2)+...........+ln3+ln2+ln1) 
乘方:lg(a?^b)=trunc(lg(a^b))+1 
             =trunc(b*lga)+1 
             =trunc(b*lna/ln10)+1 

 
高精度的加法 
ncarry=0; 
      for (i=0;i<=len;i++) 
      { 
            k=a[i]+b[i]+ncarry; 
            a[i+1]+=k/N; 
            ncarry=k%N; 
     } 
     当最后ncarry>0时,len会变化!! 
高精度的减法 

 
  先比较大小,大的为a,用一个变量记录符号。 
  ncarry=0; 
  for (i=0;i<=len;i++) 
  { 
       if (a[i]-b[i]-ncarry>=0) 
             a[i]=a[i]-b[i]-ncarry,ncarry=0; 
      else 
            a[i]=a[i]+N-b[i]-ncarry,ncarry=1; 
  } 
高精度的乘法 
For (i=0;i<=lena;i++) 
    for (j=0;j<=lenb;j++) 
        c[i+j]+=a[i]*b[j]; 
For (i=0;i<=lena+lenb;i++) 
     { 
                              
          c[i+j]=c[i+j]/N; 
     } 
高精度的除法 
A÷B精确值有两种情况:①、A能被B整除,没有余数。②、A不能被B整除,对余数进行处理。首先,我们知道,在做除法运算时,有一个不变的量和三个变化的量,不变的量是除数,三个变化的量分别是:被除数、商和余数。 
可以用减法代替除法运算:不断比较A[1..n]与B[1..n]的大小,如果A[1..n]>=B[1..n]则商C[1..n]+1→C[1..n],然后就是一个减法过程:A[1..n]-B[1..n]→A[1..n]。 
由于简单的减法速度太慢,故必须进行优化。 
       设置一个位置值J,当A[1..n]>B[1..n]时,B[1..n]左移→B[0..n],j:=j+1,即令B[1..n]增大10倍。这样就减少了减法的次数。当j>0且A[1..n]<B[1..n]时,B[0..n]右移 

 

 
求n! 

 

 

 
一个例子:计算5*6*7*8 
方法一:顺序连乘 
5*6=30,1*1=1次乘法 
30*7=210,2*1=2次乘法 
210*8=1680,3*1=3次乘法 
方法二:非顺序连乘 
5*6=30,1*1=1次乘法 
7*8=56,1*1= 1次乘法 
30*56=1680,2*2=4次乘法  

 
若“n位数*m位数=n+m位数”,则n个单精度数。 
无论以何种顺序相乘,乘法次数一定为n(n-1)/2次。(n为积的位数) 
证明: 
设F(n)表示乘法次数,则F(1)=0,满足题设 
设k<n时,F(k)=k(k-1)/2,现在计算F(n) 
设最后一次乘法计算为“k位数*(n-k)位数”,则 
F(n)=F(k)+F(n-k)+k (n-k)=n(n-1)/2(与k的选择无关) 

 
考虑k+t个单精度数相乘 
     a1*a2*…*ak *ak+1*…*ak+t 
设a1*a2*…*ak结果为m位高进制数(假设已经算出) 
ak+1*…*ak+t结果为1位高进制数 
若顺序相乘,需要t次“m位数*1位数” ,共mt次乘法 
可以先计算ak+1*…*ak+t,再一起乘,只需要m+t次乘法 
        在设置了缓存的前提下,计算m个单精度数的积,如果结果为n位数,则乘法次数约为n(n–1)/2次,与m关系不大 
设S=a1*a2*…*am,S是n位高进制数 
可以把乘法的过程近似看做,先将这m个数分为n组,每组的积仍然是一个单精度数,最后计算后面这n个数的积。时间主要集中在求最后n个数的积上,这时基本上满足“n位数*m位数=n+m位数”,故乘法次数可近似的看做n(n-1)/2次 
10!=28*34*52*7 
n!分解质因数的复杂度远小于nlogn,可以忽略不计 
与普通算法相比,分解质因数后,虽然因子个数m变多了,但结果的位数n没有变,只要使用了缓存,乘法次数还是约为n(n-1)/2次 
因此,分解质因数不会变慢(这也可以通过实践来说明) 
分解质因数之后,出现了大量求乘幂的运算,我们可以优化求乘幂的算法。这样,分解质因数的好处就体现出来 

 

 
二分法求乘幂 

 
a2n+1=a2n*a 
a2n=(an)2 
其中,a是单精度数 
二分法求乘幂之优化平方算法 
怎样优化 
(a+b)2=a2+2ab+b2 
例:123452=1232*10000+452+2*123*45*100 
把一个n位数分为一个t位数和一个n-t位数,再求平方 
怎样分 
设求n位数的平方需要F(n)次乘法 
F(n)=F(t)+F(n-t)+t(n-t),F(1)=1 
用数学归纳法,可证明F(n)恒等于n(n+1)/2  
所以,无论怎样分,效率都是一样 
将n位数分为一个1位数和n–1位数,这样处理比较方便 
优化: 

 
计算S=ax+kbx=(ab)xak 
当k<xlogab时,采用(ab)xak比较好,否则采用ax+kbx更快 
可以先计算两种算法的乘法次数,再解不等式,就可以得到结论 
也可以换一个角度来分析。其实,两种算法主要差别在最后一步求积上。由于两种方法,积的位数都是一样的,所以两个因数的差越大,乘法次数就越小 
∴当axbx–ak>ax+k–bx时,选用(ab)xak,反之,则采用ax+kbx。 
∴axbx–ak>ax+k–bx 
∴(bx–ak)(ax+1)>0 
∴bx>ak 
这时k<xlogab 

这道题: 
1.处理小数点问题,以及反序。 
2.进行n次乘法。 
3.进行输出,并加上小数点。 

 
代码: 
#include <stdio.h> 
#include <iostream>
#include <string>
 
using namespace std;
 
int main()
{
  string mlp;     //乘数
 
    int power;     //乘数的幂
      int r[151];    //保存结果
      int hdot;
 
 while(cin>>mlp>>power)
   {
            hdot=0;
              for(int t=0;t<150;t++)
                {
                    r[t]=-1;
             }
            if(mlp.find(".")!=string::npos)
        hdot=mlp.length()-mlp.find(".")-1;
         string::iterator itr=mlp.end()-1;
 
         while(hdot>0&&itr>=mlp.begin())
                {
                    if(*itr!='0')
                        {break;}
                     hdot--;
                      itr--;
               }
 
         int cn=0;
 
         while(itr>=mlp.begin())
               {
                    if(*itr!='.')
                        {
                            r[cn]=*itr-'0';
                              cn++;
                        }
                    itr--;
               }
 
         int k=cn-1;
          int m=0;     //保存临时数;
 
           while(k>-1)
           {
                    m=m*10+r[k];
                 k--;
         }
 
         for(int i=1;i<power;i++)
              {
                    int j=0;
                     while(r[j]>-1)
                        {
                            r[j]=r[j]*m;
                         j++;
                 }
                    j=0;
                 while(r[j]>-1)
                        {
                            if(r[j+1]==-1&&r[j]>=10)
                                      r[j+1]=r[j]/10;
                              else
                                 r[j+1]+=r[j]/10;
                             r[j]=r[j];
                           j++;
                 }
            }
 
         hdot=hdot*power;
             int cnt=0;
 
                while(r[cnt]>-1)
              {
                    cnt++;
               }
            if(hdot>=cnt)
         {
                    cout<<".";
                     while(hdot>cnt)
                       {
                            cout<<"0";
                             hdot--;
                      }
                    hdot=0;
              }
            for(k=cnt-1;k>=0;k--)
         {
                    if((k+1)==hdot&&hdot!=0)
                             cout<<".";
                     cout<<r[k];
            }
            cout<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值