轉:從零開始學演算法:高精度計算

來源:互聯網
上載者:User

標籤:double   函數   []   ror   ret   調用   stream   圖片   cin   

前言:由於電腦運算是有模運算,資料範圍的表示有一定限制,如整型int(C++中int 與long相同)表達範圍是(-2^31~2^31-1),unsigned long(不帶正負號的整數)是(0~2^32-1),都約為幾十億.如果採用實數型,則能儲存最大的double只能提供15~16位的有效數字,即只能精確表達數百萬億的數.因此,在計算位元超過十幾位的數時,不能採用現有類型,只能自己編程計算.
高精度計算通用方法:高精度計算時一般用一個數組來儲存一個數,數組的一個元素對應於數的一位(當然,在以後的學習中為了加快計算速度,也可用數組的一個元素表示數的多位元字,暫時不講),表示時,由於數計算時可能要進位,因此為了方便,將數由低位到高位依次存在數組下標對應由低到高位置上,另外,我們申請數組大小時,一般考慮了最大的情況,在很多情況下,表示有富餘,即高位有很多0,可能造成無效的運算和判斷,因此,我們一般將數組的第0個下標對應位置來儲存該數的位元.如數:3485(三千四百八十五),表達在數組a[10]上情況是:
下標  0    1    2    3     4    5    6    7    8    9  
內容  4    5    8    4     3    0    0    0    0    0
說明:位元   個位  十位  百位 千位
具體在計算加減乘除時方法就是小學時採用的列豎式方法.
註:高精度計算時一般用正數,對於負數,通過處理符號位的修正.
一.高精度數的儲存
1.如對數採用的字串輸入

 1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 const int N=100;//最多100位 5 int main() 6 { 7 int a[N+1],i; 8 string s1; 9 cin>>s1;//數s110 memset(a,0,sizeof(a)); //數組清011 a[0]=s1.length(); //位元12 for(i=1;i<=a[0];i++) a[i]=s1[a[0]-i]-‘0‘;//將字元轉為數字並倒序儲存.13 return 0;14 }

2.直接讀入

 1 #include <iostream> 2 using namespace std; 3 const int N=100;//最多100位 4 int main() 5 { 6 int a[N+1],i,s,key; 7 cin>>key;//數key 8 memset(a,0,sizeof(a)); //數組清0 9 i=0;//第0位10 while(key)  //當key大於011 {12   a[++i]=key%10;//取第i位的數13   key=key/10;14 }15 a[0]=i; //共i位元16 return 0;17 }

3.直接初始化(用a[]儲存)
初始化為0: memset(a,0,sizeof(a));
初始化為1: memset(a,0,sizeof(a));a[0]=1;a[1]=1;

以下程式都唯寫函數,不寫完整程式,所有高精度數儲存都滿足上述約定。
二.高精度數比較

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

三、高精度加法

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

 

四、高精度減法

 

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

五、高精度乘法1(高精度乘單精確度數,單精確度數是指通常的整型數)

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

六、 高精度除以低精度; 
演算法:按照從高位到低位的順序,逐位相除。在除到第j位時,該位在接受了來自第j+1位的餘數後與除數相除,如果最高位為零,則商的長度減一。來源程式如下:

 1 #include  <stdio.h> 2 #define   N  500 3 main() 4 { 5   int  a[N] = {0}, c[N] = {0}; 6   int  i, k, d, b; 7   char  a1[N];   8   printf("Input 除數:"); 9   scanf("%d", &b);10   printf("Input 被除數:");11   scanf("%s", a1);12   k = strlen(a1);13   for(i = 0; i < k; i++)  a[i] = a1[k - i - 1] - ‘0‘;14   d = 0;15   for(i = k - 1; i >= 0 ; i--)16   {17      d = d * 10 + a[i];18      c[i] = d / b;19      d = d % b;      20   }   21   while(c[k - 1] == 0 && k > 1)  k--;  22   printf("商=");23   for(i = k - 1; i >= 0; i--)  printf("%d", c[i]);24   printf("\n餘數=%d", d);   25 }     

七、高精度乘以高精度(要求用儘可能少的儲存單元); 
演算法:用數組儲存兩個高精度數,然後逐位相乘,注意考慮進位和總位元。來源程式如下:

 1 #include  <stdio.h> 2 main() 3 { 4   int  a[240] = {0}, b[240] = {0}, c[480] = {0}; 5   int  i, j, ka, kb, k; 6   char  a1[240], b1[240]; 7   gets(a1);    8   ka = strlen(a1); 9   gets(b1);   10   kb = strlen(b1);11   k = ka + kb;12   for(i = 0; i < ka; i++)  a[i] = a1[ka-i-1] - ‘0‘;13   for(i = 0; i < kb; i++)  b[i] = b1[kb-i-1] - ‘0‘;14   for(i = 0; i < ka; i++)15     for(j = 0; j < kb; j++)16     {17       c[i + j] = c[i + j] + a[i] * b[j];18       c[i + j +1] = c[i + j +1] + c[i + j]/10;19       c[i + j] = c[i + j] % 10;20     }21   if(!c[k])  k--;22   for(i = k-1; i >= 0; i--)  printf("%d", c[i]);        23 }     

八、高精度除以高精度(要求用儘可能少的儲存單元); 
演算法:用電腦類比手算除法,把除法試商轉化為連減。

  1 #include  <stdio.h>  2 #define   N  500  3 int  bj(int a[], int b[], int k1, int k2)   /*比較大小函數*/  4 {  5    int i, t, flag;       /*flag作標誌位*/  6    if(k1 < k2)    7      flag = 0;           /*被除數小於除數返回0*/  8    else if(k1 > k2)    9           flag = 1;      /*被除數大於除數返回1*/ 10         else 11           {              /*被除數和除數位元相等則逐位進行比較*/ 12             i = k1; 13             t = 0; 14             while(t == 0 && i > 0) 15             { 16               if(a[i] > b[i]) {t = 1; flag = 1;} 17               else if(a[i] == b[i])  i--; 18               else  {t = 1; flag = 0;}         19             } 20             if(i == 0 && t == 0)  flag = 2;     /*被除數等於除數返回2*/ 21           } 22   return flag;            23 } 24 int  jf(int a[], int b[], int k1, int k2)       /*減法運算*/ 25 { 26   int  i, k, d[N]; 27   for(i = 0; i < k2; i++)  d[i] = b[i];        /*把除數賦給數組d*/ 28   for(i = k2; i < N; i++)  d[i] = 0;          /*d數組無資料的高位置0*/ 29   k = k1 - k2 - 1;                            /*計算減法起始位置*/ 30   if(k < 0)  k = 0; 31   if(k > 0) 32   { 33     for(i = k2 - 1; i >= 0; i--)  d[i + k] = d[i];  /*移動減數位元與被減數對齊*/ 34     for(i = 0; i < k; i++)  d[i] = 0;            /*移動後的其餘位置0*/ 35   }   36   for(i = 0; i < k1; i++) 37   { 38     if(a[i] >= d[i])  a[i] -= d[i]; 39     else 40     { 41       a[i + 1] = a[i + 1] - 1; 42       a[i] = 10 + a[i] - d[i];   43     }      44   }    45   return k; 46 } 47 main() 48 { 49   int  a[N] = {0}, b[N] = {0}, c[N] = {0}, d[N] = {0}; 50   int  i, ka, kb, m, t, t1, t2, k, x, kd, kk; 51   char  a1[N], b1[N];   52   printf("Input 被除數:"); 53   scanf("%s", a1); 54   ka = strlen(a1); 55   for(i = 0; i < ka; i++)  a[i] = a1[ka - i -1] - ‘0‘; 56   printf("Input 除數:"); 57   scanf("%s", b1); 58   kb = strlen(b1); 59   for(i = 0; i < kb; i++)  b[i] = b1[kb - i -1] - ‘0‘; 60   kd = ka;    /*儲存被除數位元  */ 61   t2 = bj(a, b, ka, kb); 62   m = 0; 63   do 64   { 65     while(a[ka - 1] == 0)  ka--; 66     t = bj(a, b, ka, kb);    67     if(t >= 1) 68     { 69       k = jf(a, b, ka, kb); 70       c[k]++;       71       if(k > m)  m = k; 72       t1 = 0; 73       for(i = k; i <= m; i++) 74       { 75         x = c[i] + t1; 76         c[i] = x % 10; 77         t1 = x / 10;      78       } 79       if(t1 > 0)  {m++; c[m] = t1;  }      80     }    81   }while(t == 1); 82   if(t2 == 0)   83   { 84     printf("商=0");   85     printf("\n餘數="); 86     for(i = kd - 1; i >= 0; i--)  printf("%d", a[i]); 87     exit(1);   88   } 89   if(t2 == 2) 90   { 91     printf("商 = 1");   92     printf("\n餘數 = 0"); 93     exit(1);   94   } 95   kk = kd; 96   while(!c[kd - 1])  kd--; 97   printf("商 = "); 98   for(i = kd - 1; i >= 0; i--)  printf("%d", c[i]); 99   while(!a[kk])  kk--;100   printf("\n餘數 = ");101   if(kk < 0)  102   {103     printf("0");  104     exit(1);105   }106   for(i = kk; i >= 0; i--)  printf("%d", a[i]);107 } 

下面給出一些案例:

問題1. N!,要求精確到P位(0〈P〈1000〉。 
演算法:結果用數組a儲存,開始時a[0]=1,依次乘以數組中各位,注意進位和數組長度的變化。來源程式如下:

 1 #include   <stdio.h> 2 #define    M   1000 3 main() 4 { 5   int a[M], i, n, j, flag = 1; 6   printf("n="); 7   scanf("%d",&n); 8   printf("n!="); 9   a[0] = 1;10   for(i = 1; i < M; i++) a[i] = 0;11    for(j = 2; j <= n; j++)12    {13      for(i = 0; i < flag; i++) a[i] *= j;14      for(i = 0; i < flag; i++)15        if(a[i] >= 10)16        {17          a[i+1] += a[i]/10;18          a[i] = a[i] % 10;19          if(i == flag-1)  flag++;20        }21     }22   for(j = flag - 1; j >= 0; j--)23     printf("%d", a[j]);24 } 

問題2. 麥森數 
【問題描述】形如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。對於個位單獨考慮,每次左移一位。來源程式如下:

 1 #include <stdio.h> 2 #include <math.h> 3 #define  MAX  100000 4 main() 5 { 6    int p; 7    int i, j; 8    scanf("%d", &p); 9    printf("%d\n", (int)(p * log10(2.0)) + 1);10    long  store[110] = {0};11    store[0] = 1;12    int left = p % 10;13    p /= 10;14     for(i = 1; i <= p; i++)15     {16       for(j = 0; j <= 100; j++)17         store[j] <<= 10;18       for(j = 0; j <= 100; j++)19       {20         if(store[j] >= MAX)21         {22           store[j + 1] += store[j] / MAX;23           store[j] %= MAX;24         }25       }26     }27     for(i = 1; i <= left; i++)28     {29       for(j = 0; j <= 100; j++)30         store[j] <<= 1;31       for(j = 0; j <= 100; j++)32       {33         if(store[j] >= MAX)34         {35           store[j + 1] += store[j] / MAX;36           store[j] %= MAX;37         }38       }39     }40     store[0] -= 1;41     for(i = 1; i < 100; i++)42     {43       if(store[i - 1] < 0)44       {45          store[i] -= 1;46          store[i - 1] += MAX;47       }48       else49         break;50     }51     for(i = 99; i >= 0; i--)52     {53       printf("%05d", store[i]);54       if((100 - i) % 10 == 0)55           printf("\n");56     }57 } 

問題3. 有一個正整數N(N可能達到120位),它是由若干個不大於65535的正整數相乘而得到的。請把這個數分解成素數因子(質因子)的乘積。 
輸入:輸入檔案只有一行為N的值。 
輸出:(1)素數因子由小到大分行輸出; 
(2)每一行輸出一個素數因子和該素數因子的個數,用一個空格分開; 
(3)如果正整數N的分解中有一個以上的大於65535的素數,請按照(1)、(2)的要求輸出分解中的小於65535的素數後,在下一行輸出
“DATA  ERROR!”。 
演算法:先將2到65535之間的所有素數儲存在數組中,用這個數去除數組中的每一個數,得到一個質因數就列印出來。來源程式如下:

 1 #include  <stdio.h> 2 #include  <math.h> 3 int length, temp[120]; 4 int sushu(int a[]) 5 { 6   int i, j, k = 0, m; 7   for(i = 2; i <= 65537; i++) 8   { 9     m = sqrt(i);10     for(j = 2; j <= m; j++)11       if(i % j == 0)  break;12     if(j > m)13     {14       a[k] = i;15       k++;   16     }         17   }18  return k;     19 }20 int divide(int a[], int k)21 {22   int i, d = 0;23   for(i = length - 1; i >= 0; i--)24   {25      d = d * 10 + a[i];26      temp[i] = d / k;27      d = d % k;      28   }   29   if(!d)30     {31       while(temp[length - 1] == 0 && length > 1)  length--;32       for(i = 0; i < length; i++)33       {34         a[i] = temp[i];35         temp[i] = 0;      36       }37       for(i = length; i < 120; i++) a[i] = 0;   38     }39     else  40       for(i = 0; i < length; i++)  temp[i] = 0;41   return d;     42 }43 main()44 {45   int i, k, s, d;       /*s計數器; d餘數*/46   int a[6600], b[120] = {0}, c[120] = {0};47   char b1[120];48   gets(b1);49  length = strlen(b1);50   for(i = 0; i < length; i++)  b[i] = b1[length - i - 1] - ‘0‘;51   k = sushu(a);52   for(i = 0; i < k; i++)53   {54     s = 0;55     d = divide(b, a[i]);56     while(!d)57     {58       s++;59       d = divide(b, a[i]);         60     }61     if(i == k - 1)  62  63     {  64       printf("Data Error!");65       break;66     } 

 

轉:從零開始學演算法:高精度計算

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.