大數問題简谈

大數問題

一、基本觀念

在某些情況下,我們必須處理位數相當多的一個整數,例如 100 位數,系統內建的資料型態不管是 int、long int、long long int 等,位數顯然都不夠用。要解決這個問題,我們必須自己用程式來處理,最簡單的方法,就是模仿人工處理數字的方式,也就是把數字拆成一個位數一個位數,這個時候我們可以用一個陣列來完成這個動作。不過,這樣一來,所有對於這種超大整數的處理,包括輸入一個大數、兩個大數相加、印出一個大數等等,都需要我們自己寫一個函數來處理。至於這個陣列要用什麼樣的資料型態,因為兩個一位數相乘也不過才 81,所以我們可以用 char 的陣列來記錄一個超長整數,如:

char n[300];

就可以記錄 300 位數的資料。前面提到大數的輸入及輸出,都需要我們自己寫函數來處理,我們分別把它們定名為 Input() 及 Print() :

view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······15001.#include <stdio.h>  
02.#include <string.h>  
03.#define LEN 300  
04.int Input(char n[])  
05.{  
06.}  
07.void Print(char n[])  
08.{  
09.}  
10.void main()  
11.{  
12. char a[LEN];  
13. Input(a);  
14. Print(a);  
15.} 
#include <stdio.h>
#include <string.h>
#define LEN 300
int Input(char n[])
{
}
void Print(char n[])
{
}
void main()
{
 char a[LEN];
 Input(a);
 Print(a);
}

上面第三行 #define LEN 300 的意思,即是定義一個常數叫做 LEN ,而它的值是 300,下面用到 LEN 的地方,在執行的時候便會自動變成 300。這樣寫的好處是,如果我們有好個地方都用到 300 這個數字,但是後來發現不夠用了,要將它改成 1000,這個時候要一行一行改,還可能不小心漏掉,如果用 #define 定義一個常數,則只要改一個地方就行了。接下來我們看到 Input() 函數要做的步驟:

1.把該陣列的每一格數字歸零。
2.將使用者輸入的數字以字串方式存到另一個字串。
3.計算該字串的長度。
4.從該字串的尾端(即個位數)開始一個一個位數往左,將它轉換成數字後,存到陣列對應的格子中。
我們把上面提到的字串及超長整數的陣列做一個比較:

字串
 s[0]
 s[1]
 s[2]
 s[3]
 s[4]
 s[5]
 s[6]
 s[7]
 s[8]
 s[9]
 
'1'
 '2'
 '3'
 '4'
 '5'
 '6'
 0
 ×
 ×
 ×
 
 
陣列
 n[0]
 n[1]
 n[2]
 n[3]
 n[4]
 n[5]
 n[6]
 n[7]
 n[8]
 n[9]
 
6
 5
 4
 3
 2
 1
 0
 0
 0
 0
 
 

上面提到的 Input() 的程式碼可以寫成:

view plaincopy to clipboardprint?
01.int Input(char n[])  
02.{  
03. char s[LEN];  
04. int i, l;  
05. for(i=0; i<LEN; i++)  
06.   n[i]=0;  
07. if(scanf("%s", s)<1) return -1;  
08. l=strlen(s);  
09. for(i=0; i<l; i++)  
10.   n[i]=s[l-i-1]-'0';  
11. return 0;  
12.} 
int Input(char n[])
{
 char s[LEN];
 int i, l;
 for(i=0; i<LEN; i++)
   n[i]=0;
 if(scanf("%s", s)<1) return -1;
 l=strlen(s);
 for(i=0; i<l; i++)
   n[i]=s[l-i-1]-'0';
 return 0;
}

上面的 Input() 函數為了要得知是否有資料輸入,所以我們利用傳回值 0 代表輸入成功,傳回 -1 代表傳入失敗。至於 Print() 函數要做的步驟為:

1.從該陣列最後面往回找到第一個不是 0 的數字。
2.從該位數字往左邊依序把它們一個一個印出來。
3.印出換行符號。
它的程式碼可以寫成:

view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······15001.void Print(char n[])  
02.{  
03. int i;  
04. for(i=LEN-1; i>0; i--)  
05.   if(n[i]!=0) break;  
06. for(; i>=0; i--)  
07.   printf("%d", n[i]);  
08. printf("/n");  
09.} 
void Print(char n[])
{
 int i;
 for(i=LEN-1; i>0; i--)
   if(n[i]!=0) break;
 for(; i>=0; i--)
   printf("%d", n[i]);
 printf("/n");
}

寫成之後,我們可以先輸入一個約 60 位數的數字,看是不是可以正常印出結果。

二、大數相加

接下來我們來寫一個處理兩個大數相加的題目,接著上一段的程式,我們接下來要寫一個處理相加的函數 Add() ,它的做法就跟小學時的加法運算一樣,從個位數開始,一個位數一個位數相加,如果有超過 10 的部分,就把它往前進位。例如:

大數 A
 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
 
 
大數 B
 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
 
 
相加
 0
 2
 4
 6
 8
 10
 12
 14
 16
 18
 
 
進位
 0
 2
 4
 6
 9
 1
 3
 5
 7
 8
 
 

下面的程式碼可以參考看看:

view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······15001.void Add(char a[], char b[], char c[])  
02.{  
03. int i;  
04. for(i=0; i<LEN; i++)  
05.   c[i]=a[i]+b[i];  
06. for(i=0; i<LEN-1; i++) {  
07.   if(c[i]>=10) {  
08.     c[i+1]+=c[i]/10;  
09.     c[i]=c[i]%10;  
10.   }  
11. }  
12.} 
void Add(char a[], char b[], char c[])
{
 int i;
 for(i=0; i<LEN; i++)
   c[i]=a[i]+b[i];
 for(i=0; i<LEN-1; i++) {
   if(c[i]>=10) {
     c[i+1]+=c[i]/10;
     c[i]=c[i]%10;
   }
 }
}

接下來把主程式 main() 改寫成:

void main()

{

 char a[LEN], b[LEN], c[LEN];

 Input(a);

 Input(b);

 Add(a, b, c);

 Print(c);

}

三、#10106 ─ Product

題目:按這裡 。

說明:每組測試資料有 2 列,分別代表 2 個大數 X、Y ( 0 <= X、Y < 10250 ),輸出 X*Y 的結果。

這一題的大數位數有 250 位,我們原先宣告的 LEN 有 300 所以已經夠用了。因為乘出來的積的最大位數為原來的兩倍,所以那一行 C[LEN] 要改成 C[LEN*2] ,而 Print() 函數裡面的 i=LEN-1 也要改成 i=LEN*2-1 。接下來,我們要寫一個乘(Multiply)的函數 Mul() ,它的做法就跟小學的乘法原理是一樣的,下面我們來看到它的程式碼:

void Mul(char a[], char b[], char c[])

{

 int i, j;

 for(i=0; i<LEN*2; i++)

   c[i]=0;

 for(i=0; i<LEN; i++) {

   for(j=0; j<LEN; j++) {

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

     if(c[i+j]>=10) {

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

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

     }

   }

 }

}

再把主程式改成:

void main()

{

 char a[LEN], b[LEN], c[LEN*2 ];

 Input(a);

 Input(b);

 Mul (a, b, c);

 Print(c);

}

最後再改成連續輸入的寫法:

void main()

{

 char a[LEN], b[LEN], c[LEN*2];

 while(1) {

   if( Input(a)) return ;

   Input(b);

   Mul(a, b, c);

   Print(c);

 }

}

from oiant

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值