高精度算法总结及应用实例

来源:互联网 发布:国际 社会统计 数据库 编辑:程序博客网 时间:2024/05/21 10:36

 

 

#include <stdio.h>

#include <string.h>

#include <math.h>

#include <malloc.h>

int an,bn,fa=1,fb=1;         /* 把an,bn,k设为全局变量,an纪录第一个高精度数组的位数,bn纪录第二个高精度数组的位数,k纪录输出结果的位数*/

char b1[250], b2[250];           /*纪录需要计算的两个高精度数据  */

void input(int a1[],int a2[])    /*函数input为输入函数,用来纪录两个待计算的高精度数据,以数组首地址为参数.以实现返回两个高精度数据*/

{

    int i,ai=1,bi=1;

    scanf ( "%s%s", b1, b2 );                      /*输入两个高精度数据 */

    an = strlen( b1 );                             /*an纪录b1的位数 */

    bn = strlen( b2 );                            /*bn纪录b2的位数 */

    if(b1[0]==45) { an--; fa=-1;ai=0;}           /*判断数组的符号 */

    if(b2[0]==45) { bn--; fb=-1;bi=0;}

    for (i=0; i<an; i++,ai++) {a1[i]=b1[an-ai]-'0'; printf("%d",a1[i]);}     /*把字符形数据b1转为整数形数据,同样用数组纪录 */

    for (i=0; i<bn; i++,bi++) a2[i]=b2[bn-bi]-'0';    /* 同上  */

    return;

}

void addition(int a[],int b[],int q)              /*高精度加法运算*/

{

    int i,c[251]={0},k;

    if(fa*fb>0||q)

    {

        if(an>bn) k=an;

        else k=bn;                                            /*用k纪录结果的最小位数*/

        for(i=0;i<k;i++)

        {

            c[i]=a[i]+b[i]+c[i];

            c[i+1]=(int)c[i]/10;

            c[i]=(int)c[i]%10;

         }                                                 /*高精度加法运算过程*/

        if(c[k]) k++;                                     /*判断最后结果的位数*/

        if(fa<0&&q||fa<0) printf("-");

        for(i=k-1;i>=0;i--)  printf("%d",c[i]);         /*输出结果*/

        return;

    }

    else subtraction(a,b,1);

    return;

}

 

subtraction(int a[],int b[],int q)                      /*高精度减法运算*/

{

    int i,f=0,c[251]={0},k;

    if(fa*fb>0||q)

    {

        if(an>bn) k=an;

        else                                     /*用k纪录结果的最大位数*/

         { k=bn;

            for(i=k;a[i]<=b[i]&&i>=0;i--)

            if(a[i]<b[i]) f=1;                /*f纪录结果符号*/

           }

 

        if(!f)                                /*高精度减法运算过程*/

         for(i=0;i<k;i++)

         {

             if(a[i]<b[i])

             {     a[i+1]--;

                   a[i]+=10;

            }

            c[i]=a[i]-b[i];

          }

        else                                         /*当a<b时的处理*/

          for(i=0;i<k;i++)

            {

               if(b[i]<a[i])

               {   b[i+1]--;

                  b[i]+=10;

               }

               c[i]=b[i]-a[i];

             }

        while(!c[k-1]&&k>1) k--;                  /*判断最后结果的位数*/

        if(q&&(fa>0&&f||fa<0&&!f)||fa>0&&(fb>0&&!f||f&&!q)) printf("-");                      /*如果f为真是输出负号*/

        for(i=k-1;i>=0;i--) printf("%d",c[i]);

        return;

    }

    else addition(a,b,1);

}

 

 

 

 

 

 

 

void multiplication( int a[], int b[])                  /*高精度乘法运算*/

{

    int i, j, c[501] = {0},k;

    k = an + bn - 1;                          /*用k纪录结果的最大位数*/

    for(i = 0; i < an; i++)                  /*高精度乘法运算过程*/

     for(j = 0;j < bn; j++)

      {

        c[i+j] = a[i] * b[j] + c[i+j];

        c[i+j+1] = c[i+j] / 10 + c[i+j+1];

        c[i+j] = c[i+j] % 10;

      }

    while(!c[k]) k--;                          /*判断最后结果的位数*/

    if(fa*fb<0) printf("-");

    for(i = k; i >= 0; i--)  printf("%d",c[i]);     /*输出结果*/

}

main()

{

    int a[250]={0},b[250]={0};

    input(a,b);

    printf("/n%s+%s=",b1,b2);addition(a,b,0);

    printf("/n%s-%s=",b1,b2);subtraction(a,b,0);

    printf("/n%s*%s=",b1,b2);multiplication(a,b);

    getch();

}

 

1、 高精度除以低精度; 

算法:按照从高位到低位的顺序,逐位相除。在除到第j位时,该位在接受了来自第j+1位的余数后与除数相除,如果最高位为零,则商的长度减一。源程序如下: 

#include  <stdio.h> 

#define   N  500 

main() 

  int  a[N] = {0}, c[N] = {0}; 

  int  i, k, d, b; 

  char  a1[N];  

  printf("Input 除数:"); 

  scanf("%d", &b); 

  printf("Input 被除数:"); 

  scanf("%s", a1); 

  k = strlen(a1); 

  for(i = 0; i < k; i++)  a[i] = a1[k - i - 1] - '0'; 

  d = 0; 

  for(i = k - 1; i >= 0 ; i--) 

  { 

     d = d * 10 + a[i]; 

     c[i] = d / b; 

     d = d % b;      

  }   

  while(c[k - 1] == 0 && k > 1)  k--;  

  printf("商="); 

  for(i = k - 1; i >= 0; i--)  printf("%d", c[i]); 

  printf("/n余数=%d", d);    

}     

 

2、高精度乘以高精度(要求用尽可能少的存储单元); 

算法:用数组保存两个高精度数,然后逐位相乘,注意考虑进位和总位数。源程序如下: 

#include  <stdio.h> 

main() 

  int  a[240] = {0}, b[240] = {0}, c[480] = {0}; 

  int  i, j, ka, kb, k; 

  char  a1[240], b1[240]; 

  gets(a1);    

  ka = strlen(a1); 

  gets(b1);    

  kb = strlen(b1); 

  k = ka + kb; 

  for(i = 0; i < ka; i++)  a[i] = a1[ka-i-1] - '0'; 

  for(i = 0; i < kb; i++)  b[i] = b1[kb-i-1] - '0'; 

  for(i = 0; i < ka; i++) 

    for(j = 0; j < kb; j++) 

    { 

      c[i + j] = c[i + j] + a[i] * b[j]; 

      c[i + j +1] = c[i + j +1] + c[i + j]/10; 

      c[i + j] = c[i + j] % 10; 

    } 

  if(!c[k])  k--; 

  for(i = k-1; i >= 0; i--)  printf("%d", c[i]);        

}     

 

3、高精度除以高精度(要求用尽可能少的存储单元); 

算法:用计算机模拟手算除法,把除法试商转化为连减。 

#include  <stdio.h> 

#define   N  500 

int  bj(int a[], int b[], int k1, int k2)   /*比较大小函数*/ 

   int i, t, flag;       /*flag作标志位*/ 

   if(k1 < k2)  

     flag = 0;           /*被除数小于除数返回0*/ 

   else if(k1 > k2)  

          flag = 1;      /*被除数大于除数返回1*/ 

        else 

          {              /*被除数和除数位数相等则逐位进行比较*/ 

            i = k1; 

            t = 0; 

            while(t == 0 && i > 0) 

            { 

              if(a[i] > b[i]) {t = 1; flag = 1;} 

              else if(a[i] == b[i])  i--; 

              else  {t = 1; flag = 0;}        

            } 

            if(i == 0 && t == 0)  flag = 2;     /*被除数等于除数返回2*/ 

          } 

  return flag;           

int  jf(int a[], int b[], int k1, int k2)       /*减法运算*/ 

  int  i, k, d[N]; 

  for(i = 0; i < k2; i++)  d[i] = b[i];        /*把除数赋给数组d*/ 

  for(i = k2; i < N; i++)  d[i] = 0;          /*d数组无数据的高位置0*/ 

  k = k1 - k2 - 1;                            /*计算减法起始位置*/ 

  if(k < 0)  k = 0; 

  if(k > 0) 

  { 

    for(i = k2 - 1; i >= 0; i--)  d[i + k] = d[i];  /*移动减数位数与被减数对齐*/ 

    for(i = 0; i < k; i++)  d[i] = 0;            /*移动后的其余位置0*/ 

  }  

  for(i = 0; i < k1; i++) 

  { 

    if(a[i] >= d[i])  a[i] -= d[i]; 

    else 

    { 

      a[i + 1] = a[i + 1] - 1; 

      a[i] = 10 + a[i] - d[i];  

    }     

  }   

  return k; 

main() 

  int  a[N] = {0}, b[N] = {0}, c[N] = {0}, d[N] = {0}; 

  int  i, ka, kb, m, t, t1, t2, k, x, kd, kk; 

  char  a1[N], b1[N];  

  printf("Input 被除数:"); 

  scanf("%s", a1); 

  ka = strlen(a1); 

  for(i = 0; i < ka; i++)  a[i] = a1[ka - i -1] - '0'; 

  printf("Input 除数:"); 

  scanf("%s", b1); 

  kb = strlen(b1); 

  for(i = 0; i < kb; i++)  b[i] = b1[kb - i -1] - '0'; 

  kd = ka;    /*保存被除数位数  */ 

  t2 = bj(a, b, ka, kb); 

  m = 0; 

  do 

  { 

    while(a[ka - 1] == 0)  ka--; 

    t = bj(a, b, ka, kb);    

    if(t >= 1) 

    { 

      k = jf(a, b, ka, kb); 

      c[k]++;      

      if(k > m)  m = k; 

      t1 = 0; 

      for(i = k; i <= m; i++) 

      { 

        x = c[i] + t1; 

        c[i] = x % 10; 

        t1 = x / 10;     

      } 

      if(t1 > 0)  {m++; c[m] = t1;  }     

    }    

  }while(t == 1); 

  if(t2 == 0)  

  { 

    printf("商=0");  

    printf("/n余数="); 

    for(i = kd - 1; i >= 0; i--)  printf("%d", a[i]); 

    exit(1);  

  } 

  if(t2 == 2) 

  { 

    printf("商 = 1");  

    printf("/n余数 = 0"); 

    exit(1);  

  } 

  kk = kd; 

  while(!c[kd - 1])  kd--; 

  printf("商 = "); 

  for(i = kd - 1; i >= 0; i--)  printf("%d", c[i]); 

  while(!a[kk])  kk--; 

  printf("/n余数 = "); 

  if(kk < 0)  

  { 

    printf("0");  

    exit(1); 

  } 

  for(i = kk; i >= 0; i--)  printf("%d", a[i]); 

4、 N!,要求精确到P位(0〈P〈1000〉。 

算法:结果用数组a保存,开始时a[0]=1,依次乘以数组中各位,注意进位和数组长度的变化。源程序如下: 

#include   <stdio.h> 

#define    M   1000 

main() 

  int a[M], i, n, j, flag = 1; 

  printf("n="); 

  scanf("%d",&n); 

  printf("n!="); 

  a[0] = 1; 

  for(i = 1; i < M; i++) a[i] = 0; 

   for(j = 2; j <= n; j++) 

   { 

     for(i = 0; i < flag; i++) a[i] *= j; 

     for(i = 0; i < flag; i++) 

       if(a[i] >= 10) 

       { 

         a[i+1] += a[i]/10; 

         a[i] = a[i] % 10; 

         if(i == flag-1)  flag++; 

       } 

    } 

  for(j = flag - 1; j >= 0; j--) 

    printf("%d", a[j]); 

问题1. 麦森数 

【问题描述】形如2P-1的素数称为麦森数,这时P一定也是个素数。但反过来不一定,即如果P是个素数,2P-1不一定也是素数。到1998年底,人们已找到了37个麦森数。最大的一个是P=3021377,它有909526位。麦森数有许多重要应用,它与完全数密切相关。 

任务:从文件中输入P(1000<P<3100000),计算2P-1的位数和最后500位数字(用十进制高精度数表示) 

【输入格式】 

文件中只包含一个整数P(1000<P<3100000) 

【输出格式】 

第一行:十进制高精度数2P-1的位数。 

第2-11行:十进制高精度数2P-1的最后500位数字。(每行输出50位,共输出10行,不足500位时高位补0) 

不必验证2P-1与P是否为素数。 

【输入样例】 

1279 

【输出样例】 

386 

00000000000000000000000000000000000000000000000000 

00000000000000000000000000000000000000000000000000 

00000000000000104079321946643990819252403273640855 

38615262247266704805319112350403608059673360298012 

23944173232418484242161395428100779138356624832346 

49081399066056773207629241295093892203457731833496 

61583550472959420547689811211693677147548478866962 

50138443826029173234888531116082853841658502825560 

46662248318909188018470682222031405210266984354887 

32958028878050869736186900714720710555703168729087 

算法:2的幂可以转化成左移运算,为了提高运算速度,可每次左移10位,即每次乘210。对于个位单独考虑,每次左移一位。源程序如下: 

#include <stdio.h> 

#include <math.h> 

#define  MAX  100000 

main() 

   int p; 

   int i, j; 

   scanf("%d", &p); 

   printf("%d/n", (int)(p * log10(2.0)) + 1); 

   long  store[110] = {0}; 

   store[0] = 1; 

   int left = p % 10; 

   p /= 10; 

    for(i = 1; i <= p; i++) 

    { 

      for(j = 0; j <= 100; j++) 

        store[j] <<= 10; 

      for(j = 0; j <= 100; j++) 

      { 

        if(store[j] >= MAX) 

        { 

          store[j + 1] += store[j] / MAX; 

          store[j] %= MAX; 

        } 

      } 

    } 

    for(i = 1; i <= left; i++) 

    { 

      for(j = 0; j <= 100; j++) 

        store[j] <<= 1; 

      for(j = 0; j <= 100; j++) 

      { 

        if(store[j] >= MAX) 

        { 

          store[j + 1] += store[j] / MAX; 

          store[j] %= MAX; 

        } 

      } 

    } 

    store[0] -= 1; 

    for(i = 1; i < 100; i++) 

    { 

      if(store[i - 1] < 0) 

      { 

         store[i] -= 1; 

         store[i - 1] += MAX; 

      } 

      else 

        break; 

    } 

    for(i = 99; i >= 0; i--) 

    { 

      printf("%05d", store[i]); 

      if((100 - i) % 10 == 0) 

          printf("/n"); 

    } 

问题2. 有一个正整数N(N可能达到120位),它是由若干个不大于65535的正整数相乘而得到的。请把这个数分解成素数因子(质因子)的乘积。 

输入:输入文件只有一行为N的值。 

输出:(1)素数因子由小到大分行输出; 

(2)每一行输出一个素数因子和该素数因子的个数,用一个空格分开; 

(3)如果正整数N的分解中有一个以上的大于65535的素数,请按照(1)、(2)的要求输出分解中的小于65535的素数后,在下一行输出

“DATA  ERROR!”。 

算法:先将2到65535之间的所有素数保存在数组中,用这个数去除数组中的每一个数,得到一个质因数就打印出来。源程序如下: 

#include  <stdio.h> 

#include  <math.h> 

int length, temp[120]; 

int sushu(int a[]) 

  int i, j, k = 0, m; 

  for(i = 2; i <= 65537; i++) 

  { 

    m = sqrt(i); 

    for(j = 2; j <= m; j++) 

      if(i % j == 0)  break; 

    if(j > m) 

    { 

      a[k] = i; 

      k++;    

    }         

  } 

 return k;     

int divide(int a[], int k) 

  int i, d = 0; 

  for(i = length - 1; i >= 0; i--) 

  { 

     d = d * 10 + a[i]; 

     temp[i] = d / k; 

     d = d % k;      

  }   

  if(!d) 

    { 

      while(temp[length - 1] == 0 && length > 1)  length--; 

      for(i = 0; i < length; i++) 

      { 

        a[i] = temp[i]; 

        temp[i] = 0;      

      } 

      for(i = length; i < 120; i++) a[i] = 0;    

    } 

    else  

      for(i = 0; i < length; i++)  temp[i] = 0; 

  return d;     

main() 

  int i, k, s, d;       /*s计数器; d余数*/ 

  int a[6600], b[120] = {0}, c[120] = {0}; 

  char b1[120]; 

  gets(b1); 

 length = strlen(b1); 

  for(i = 0; i < length; i++)  b[i] = b1[length - i - 1] - '0'; 

  k = sushu(a); 

  for(i = 0; i < k; i++) 

  { 

    s = 0; 

    d = divide(b, a[i]); 

    while(!d) 

    { 

      s++; 

      d = divide(b, a[i]);         

    } 

    if(i == k - 1)  

 

    {  

      printf("Data Error!"); 

      break; 

    }