C语言无符号双字节乘法,华为OJ机试标题:两个大整数相乘(纯C语言实现两个大整数相乘,两种方法实现大数相乘)...
華為OJ機試題目:兩個大整數(shù)相乘(純C語言實現(xiàn)兩個大整數(shù)相乘,兩種方法實現(xiàn)大數(shù)相乘)
題目描述:
輸出兩個不超過100位的大整數(shù)的乘積。
輸入:
輸入兩個大整數(shù),如1234567 123
輸出:
輸出乘積,如:151851741
樣例輸入:
1234567 123
樣例輸出:
151851741
注意:在oj上不能直接套用我的代碼,需要將無關(guān)的輸出去除才行
方法一
思路:
解這道題目最簡單的方法就是模擬我們筆算乘法的過程,如:1234×123
只要把這個過程實現(xiàn),無論多大的數(shù)我們都能解決了,是不是很簡單。
程序?qū)崿F(xiàn):
首先,我們用兩個字符串來保存我們的大整數(shù),num1[100], num2[100]
scanf("%s%s", num1, num2);
然后,求num2的每一位與num1的乘積,保存到tempRes中。
過程為:res保存每位相乘的結(jié)果,carry用來保存進位,每位相乘之后還要加上進位才是真的結(jié)果。將res的個位保存到tempRes中,其他位則為下一位相乘的進位。
for(j = num2Len - 1; j >= 0; j--)
{/*計算num1與num2各位相乘的結(jié)果,保存到tempRes中
*每一位相乘后與之前的進位相加得出res,將res的個
*位(res%10)保存到tempRes里,然后將res的其余位數(shù)
*(res/10)則為進位carry*/
for(i = num1Len-1; i >= 0; i--)
{
res= Int(num1[i]) * Int(num2[j]) +carry;
tempRes[tempResLen--] = Char(res % 10);
carry= res / 10;
}//tempRes第一位為進位,剛剛的循環(huán)是沒有算的,最后把進位算上
tempRes[tempResLen] =Char(carry);
tempResLen=num1Len;
carry= 0;
}
再然后,將tempRes與result求和,每求一次,向左偏移一位。res為每位之和再加上進位,這里很重要,然后保存到result里只是res的個位,res為下一位計算的進位。
//由result的末尾開始計算和,算完一次,向左偏移一位
for(k = resultLen-offset; k > (resultLen-offset-num1Len); k--)
{
res= Int(result[k]) + Int(tempRes[tempResLen--]) +carry;
result[k]= Char(res%10);
carry= res/10;
}
result[k]+= Int(tempRes[tempResLen] +carry);
offset++;
以上兩步就是我程序最核心的部分。以下是程序的全部代碼。
1 #include
2 #include
3 #include
4
5 #define and &&
6 #define or ||
7 #define not !
8 #define Int(X) (X - '0')
9 #define Char(X) (X + '0')
10
11 char *multiBigInteger(const char *, const char *);12 int checkNum(const char *);13
14 int main(void)15 {16 char num1[100] = {'\0'}, num2[100] = {'\0'};17 while(scanf("%s%s", num1, num2) !=EOF)18 {19 char *result = "0";20 if(strlen(num1) > 100 or strlen(num2) > 100)21 {22 printf("ERROR\n");23 return 1;24 }25 if(checkNum(num1) or checkNum(num2))26 {27 printf("ERROR: input must be an Integer\n");28 return 1;29 }30 printf("num1:\t%s\nnum2:\t%s\n", num1, num2);31 result =multiBigInteger(num1, num2);32 if(result[0] == '0')33 {34 inti;35 printf("result:\t");36 for(i = 1; (size_t)i < strlen(result); i++)37 {38 printf("%c", result[i]);39 }40 printf("\n");41 }42 else
43 {44 printf("result:\t%s\n", result);45 }46 printf("\n");47 }48 return 0;49 }50
51 int checkNum(const char *num)52 {53 inti;54 for(i = 0; (size_t)i < strlen(num); i++)55 {56 if(num[i] < '0' or num[i] > '9')57 {58 return 1;59 }60 }61 return 0;62 }63
64 char *multiBigInteger(const char *num1, const char *num2)65 {66 char *tempRes = NULL; //用來保存每次相乘的結(jié)果
67 char *result = NULL; //用來保存最終結(jié)果
68 int tempResLen; //每次相乘結(jié)果的最大長度
69 int num1Len = strlen(num1); //num1的長度
70 int num2Len = strlen(num2); //num2的長度
71 int resultLen; //結(jié)果的最大長度
72 int i, j, k; //循環(huán)計數(shù)器
73 int res; //每次一位相乘/相加的結(jié)果
74 int carry = 0; //進位
75 int offset = 0; //加法的偏移位
76 resultLen = num1Len + num2Len - 1; //結(jié)果長度最大為num1長度和num2長度之和,由于下標(biāo)從0開始,所以要減一
77 tempResLen = num1Len; //每次num1乘以num2每一位的結(jié)果最大長度是num1Len+1,由于下標(biāo)從0開始,所以減一后約去1,只剩num1Len78 //初始化result為0
79 result = (char *)malloc((resultLen+2)*sizeof(char));80 memset(result, '0', (resultLen+1)*sizeof(char));81 result[resultLen+1] = 0;82
83 tempRes = (char *)malloc((tempResLen+2)*sizeof(char));84 for(j = num2Len - 1; j >= 0; j--)85 {86 //初始化tempRes每位為0
87 memset(tempRes, '0', (tempResLen+1)*sizeof(char));88 /*計算num1與num2各位相乘的結(jié)果,保存到tempRes中89 *每一位相乘后與之前的進位相加得出res,將res的個90 *位(res%10)保存到tempRes里,然后將res的其余位數(shù)91 *(res/10)則為進位carry*/
92 for(i = num1Len-1; i >= 0; i--)93 {94 res = Int(num1[i]) * Int(num2[j]) +carry;95 tempRes[tempResLen--] = Char(res % 10);96 carry = res / 10;97 }98 //tempRes第一位為進位,剛剛的循環(huán)是沒有算的,最后把進位算上
99 tempRes[tempResLen] =Char(carry);100 tempResLen =num1Len;101 carry = 0;102 //由result的末尾開始計算和,算完一次,向左偏移一位
103 for(k = resultLen-offset; k > (resultLen-offset-num1Len); k--)104 {105 res = Int(result[k]) + Int(tempRes[tempResLen--]) +carry;106 result[k] = Char(res%10);107 carry = res/10;108 }109 result[k] += Int(tempRes[tempResLen] +carry);110 carry = 0;111 tempResLen =num1Len;112 offset++;113
114 }115 printf("num1Len:%d\nnum2Len:%d\n", num1Len, num2Len);116 returnresult;117 }
大整數(shù)相乘完整代碼
以下是程序執(zhí)行的結(jié)果:
看了以上的代碼,感覺思路雖然很簡單,但是實現(xiàn)起來卻很麻煩,那么我們有沒有別的方法來實現(xiàn)這個程序呢?答案是有的,接下來我來介紹第二種方法。
方法二:
思路:
簡單來說,方法二就是先不算任何的進位,也就是說,將每一位相乘,相加的結(jié)果保存到同一個位置,到最后才計算進位。
例如:result[200]用來保存結(jié)果,計算98×21,步驟如下
由上面可以看出,result的數(shù)據(jù)為result[100] = {0, 18, 27, 9}
接下來就處理進位,注意看,巧妙的地方來了:
有result末尾到首位計算:
第一次:result[3] = 9; result[2] = 27;
A.先將result[3]除個位以外的數(shù)加給前一位,也就是result[2]:result[2] = result[2]+result[3]/10 = 27 + [9/10]=27; 注:數(shù)學(xué)里面的[]為取整符。如[0.9] = 0
B.然后把result[3]的個位保存到result[3]:
>> result[3] = result[3]%10 = 9;
第二次,向前一位,result[2] = 27, result[1] = 18;
重復(fù)第一次的A、B步驟,求得result[1] = result[1]+result[2] / 10=18+[27/10] = 20;
>> result[2] = result[2] % 10 = 7
第三次,再向前一位,result[1] = 20, result[0] = 0
重復(fù)之前的步驟,
>> result[0] = result[0]+result[1]/10=0+[20]/10=2
>> result[1] = result[1] % 10 = 0;
至此,已經(jīng)算到首位,result此時的結(jié)果為:result[100] = {2, 0, 7, 9}可以知道這就是結(jié)果:99×21=2079;
核心代碼:
先是不進位的各位之和:
for(j = 0; j < num2Len; j++)
{for(i = 0; i < num1Len; i++)
{/*result第一位是用來保存result長度的,而第二位是保存結(jié)果最后的進位的
* 沒有進位,則result[1]為0,所以每位相乘之和是從第三位(即result[2])
* 開始。這里是本程序的比較巧妙的地方,需要仔細(xì)想想。
**/result[i+j+2] += Int(num1[i]) *Int(num2[j]);
}
}
接下來是處理進位的代碼:
/*這個循環(huán)用來處理進位的,所以要從result的最后一位一直處理到首位。
* 要注意result的總長度是resultLen+1,有一位是保存result的長度,而
* C語言下標(biāo)是從0開始,所以result的最后一位的下標(biāo)就是resultLen,而
* 第一位就是1。*/
for(i = resultLen; i > 1; i--)
{
result[i-1] += result[i]/10;
result[i]= result[i]%10;
}
注意:這個方法有一個大坑,就是保存結(jié)果的result的數(shù)據(jù)類型必須至少是int,而不能是char,為什么呢?先想想再打開答案。
/*因為char類型的數(shù)據(jù)每個只有1個字節(jié)
* 也就是8位,所以保存的最大數(shù)值為256
* 而我們這個程序,每位最大為100個9×9=81
* 之和,也就是每個數(shù)據(jù)必須最大要能保存的數(shù)
* 值為8100, 所以char數(shù)據(jù)類型就不夠保存了。
* good luck :-)*/
答案
接下來程序的完整代碼:
1 #include
2 #include
3 #include
4
5 #define and && /**************/
6 #define or || /* python風(fēng)格 */
7 #define not ! /* */
8 #define Int(X) (X - '0') /**************/
9
10 int *multiBigInteger(const char *, const char *);11 int checkNum(const char *);12
13 int main(void)14 {15 char num1[100] = {'\0'}, num2[100] = {'\0'};16 printf("Please input two nunber(less than 100 digits):\n>");17 while(scanf("%s%s", num1, num2) !=EOF)18 {19 int *result =NULL;20 int i, change = 0;21 //對輸入的數(shù)據(jù)進行檢驗
22 if(strlen(num1) > 100 or strlen(num2) > 100)23 {24 printf("per number must less than 100 digits\n");25 return 1;26 }27
28 if(checkNum(num1) or checkNum(num2))29 {30 printf("ERROR: input must be an Integer\n");31 return 1;32 }33
34 printf("num1:\t%s\nnum2:\t%s\n", num1, num2);35
36 result =multiBigInteger(num1, num2);37
38 /*輸出結(jié)果result,result[0]保存著result的長度,39 * 所以下標(biāo)要從1開始*/
40 printf("result:\t");41 for(i = 1; i <= result[0]; i++)42 {43 if(result[i] != 0) //這一步用來去掉前導(dǎo)0,第一位為0跳過不輸出
44 change = 1;45 if(not change)46 {47 if(i > 1) //這一步用來判斷結(jié)果是否為0,
48 { //如果結(jié)果第二位還是0,就判斷為0
49 printf("0");50 break;51 }52 continue;53 }54 printf("%d", result[i]);55 }56 printf("\n");57 printf("\nPlease input two nunber(less than 100 digits):\n>");58 }59 return 0;60 }61
62 //用于檢測輸入的是否是數(shù)字,如果是就返回0,不是就返回1
63 int checkNum(const char *num)64 {65 inti;66 for(i = 0; (size_t)i < strlen(num); i++)67 {68 if(num[i] < '0' or num[i] > '9')69 {70 return 1;71 }72 }73 return 0;74 }75
76 //返回結(jié)果result,為一片內(nèi)存塊,類似數(shù)組
77 int *multiBigInteger(const char *num1, const char *num2)78 {79 int *result = NULL; //用來保存最終結(jié)果
80 int num1Len = strlen(num1); //num1的長度
81 int num2Len = strlen(num2); //num2的長度
82 int resultLen; //結(jié)果的最大長度
83 int i, j; //循環(huán)計數(shù)器
84 resultLen = num1Len + num2Len; //結(jié)果長度最大為num1長度和num2長度之和85 //初始化result為0
86 result = (int *)malloc((resultLen+1)*sizeof(int));87 memset(result, 0, (resultLen+1)*sizeof(int));88
89 result[0] = resultLen; //result的第一位是用來保存result的長度的。
90 /*num1乘以num2,由于這里采用先不進位的算法,所以算法是按從左到右91 * 按順序來乘,然后將每位的結(jié)果保存到result的每一位中,循環(huán)一次92 * reult就從下一位開始求和。如下:(左邊為正常算法,右邊為本程序算法)93 *94 * 54321 | 5432195 * × 123 | × 12396 * ------- | --------97 * 162963 | 5432198 * 108642 | 10864299 * 54321 | 162963100 * -------- | ---------101 * 6681483 | 6681483102 *103 **/
104 for(j = 0; j < num2Len; j++)105 {106 for(i = 0; i < num1Len; i++)107 {108 /*result第一位是用來保存result長度的,而第二位是保存結(jié)果最后的進位的109 * 沒有進位,則result[1]為0,所以每位相乘之和是從第三位(即result[2])110 * 開始。這里是本程序的比較巧妙的地方,需要仔細(xì)想想。111 **/
112 result[i+j+2] += Int(num1[i]) *Int(num2[j]);113 }114 }115
116 /*這個循環(huán)用來處理進位的,所以要從result的最后一位一直處理到首位。117 * 要注意result的總長度是resultLen+1,有一位是保存result的長度,而118 * C語言下標(biāo)是從0開始,所以result的最后一位的下標(biāo)就是resultLen,而119 * 第一位就是1。*/
120 for(i = resultLen; i > 1; i--)121 {122 result[i-1] += result[i]/10;123 result[i] = result[i]%10;124 }125 printf("num1Len:%d\nnum2Len:%d\n", num1Len, num2Len);126 returnresult;127 }
大整數(shù)相乘2完整代碼
程序運行結(jié)果:
總結(jié):
這是一道非常經(jīng)典而且必須要掌握的題目,所以看到這篇文章的你最好能認(rèn)真理解一下。
作者:陳棟權(quán)
時間:2016/09/17
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,
且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。
最后有興趣的同學(xué)可以關(guān)注我的微信公眾號,可以隨時及時方便看我的文章。*^_^*
掃碼關(guān)注或者搜索微信號:King_diary
總結(jié)
以上是生活随笔為你收集整理的C语言无符号双字节乘法,华为OJ机试标题:两个大整数相乘(纯C语言实现两个大整数相乘,两种方法实现大数相乘)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Edraw Max(亿图图示)案例:产品
- 下一篇: 常用的《短信中心号码》收集