在华为的机试的时候,碰到了大数的问题,所有的大数问题,无非就是相加相减相乘相除这四种,里面的区别就是大数的表示方式,一般我们采用字符数组的方式去保存大数,也有采用链表的。这两种方式中,字符数组的方式比较好理解一些,本文通过代码,来说明大数运算的集中情况,力争将这个问题讲清楚。
题目描述:输入两个不超过100位的大整数的乘积。
输入:1234567 123
输出:151851741
下面说明一下代码的思路,当两数相乘的时候,一般我们都是从被乘数的个位开始的,于是就有了以下代码,首先是按照顺序用被乘数的个位乘以乘数,这个过程中,遇到进位的时候需要保存下来,也就是carry;而得到的结果我们会存放在我们的tempRes中。
for(i = num1Len - 1; i >= 0;i--)
{
res = Int(num1[i])*Int(num2[j]) + carry;
tempRes[tempResLen--] = Char(res % 10);
carry = res / 10;
}
由于上面最后一次循环没有加上进位,所以在后面还是需要加上进位位的,并且需要将tempResLen这个值和carry初始化。
tempRes[tempResLen] = Char(carry);
tempResLen = num1Len;
carry = 0;
然后我们需要将最终结果保存在result中,并且由于每次被乘数没有移位,所以我们在保存结果的时候,每次循环需要移位,所以每次offset都会加上1。其实这个处理方式和上面的一样,只是这个是保存在最终结果中。
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);
carry = 0;
tempResLen = num1Len;
offset++;
完整代码如下。
#include
#include
#include
#define Int(X) (X - '0') //将字符转化为整型
#define Char(X) (X + '0') //将整型转化位字符
char *multiBigInteger(const char *,const char *);
int checkNum(const char *);
int main(void)
{
char num1[100] = {'\0'},num2[100] = {'\0'};
while(scanf("%s %s",num1,num2) != EOF)
{
char *result = "0";
if(strlen(num1) > 100 || strlen(num2) > 100)
{
printf("ERROR!\n");
return 1;
}
if(checkNum(num1) || checkNum(num2))
{
printf("ERROR:input must be an Integer!\n");
return 1;
}
printf("num1 = %s\nnum2 = %s\n",num1,num2);
result = multiBigInteger(num1,num2);
if(result[0] == '0')
{
int i;
printf("result:\t");
for(i = 1; (size_t)i < strlen(result); i++)
{
printf("%c",result[i]);
}
printf("\n");
}
else
{
printf("result:\t%s\n",result);
}
printf("\n");
}
return 0;
}
int checkNum(const char *num)
{
int i;
for(i = 0; (size_t)i < strlen(num); i++)
{
if(num[i] < '0' || num[i] > '9')
{
return 1;
}
}
return 0;
}
char *multiBigInteger(const char *num1,const char *num2)
{
char *tempRes = NULL; //用来保存每次相乘的结果
char *result = NULL; //用来保存最终结果
int tempResLen; //每次相乘结果的最大长度
int num1Len = strlen(num1);
int num2Len = strlen(num2);
int i,j,k;
int res; //每次一位相乘/相加的结果
int carry = 0; //进位
int offset = 0; //加法的偏移位
int resultLen = num1Len + num2Len - 1; //结果的最大长度
tempResLen = num1Len;
result = (char *)malloc((resultLen + 2)*sizeof(char)); //初始化result为0
memset(result,'0',(resultLen+1)*sizeof(char));
result[resultLen + 1] = 0;
tempRes = (char *)malloc((tempResLen+2)*sizeof(char));
for(j = num2Len - 1; j >= 0; j--)
{
//初始化tempRes每位为0
memset(tempRes, '0',(tempResLen + 1)*sizeof(char));
for(i = num1Len - 1; i >= 0;i--)
{
//计算num1与num2各位相乘的结果,结果保存到res
res = Int(num1[i])*Int(num2[j]) + carry;
//将res的个位保存到tempRes中
tempRes[tempResLen--] = Char(res % 10);
//将进位位保存在carry中
carry = res / 10;
}
//tempRes第一位为进位,最后将进位加上
tempRes[tempResLen] = Char(carry);
tempResLen = num1Len;
carry = 0;
//由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);
carry = 0;
tempResLen = num1Len;
offset++;
}
printf("num1len:%d\nnum2len:%d\n",num1Len,num2Len);
return result;
}
方法二
#include
#include
#include
#define Int(X) (X - '0')
int *multiBigInteger(const char *,const char *);
int checkNum(const char *);
char *checkZero(char *);
int main(void)
{
char num1[100] = {'\0'},num2[100] = {'\0'};
printf("please input two number(less than 100 digits):\n");
while(scanf("%s %s",num1,num2) != EOF)
{
int *result = NULL;
int i,change = 0;
if(strlen(num1) > 100 || strlen(num2) > 100)
{
printf("per number must less than 100 digits\n");
return 1;
}
if(checkNum(num1) || checkNum(num2))
{
printf("Error:input must be an Integer\n");
return 1;
}
printf("num1:\t%s\nnum2:\t%s\n",num1,num2);
checkZero(num1);
checkZero(num2);
result = multiBigInteger(num1,num2);
//输出结果result,result[0]保存着result的长度,所以从下标1开始
printf("result:\t");
for(i = 1; i <= result[0]; i++)
{
//用于去除前面的0,第一位如果是0,就跳过不输出
if(result[i] != 0)
change = 1;
if(!change)
{
if(i > 1) //用于判断结果是否为0
{
printf("0"); //如果第二位还是0,就判断为0
break;
}
continue;
}
printf("%d",result[i]);
}
printf("\n");
printf("\nplease input two number(less than 100 digits):\n");
}
return 0;
}
//用于去除输入数中前面的0
char *checkZero(char *a)
{
int i,j=0;
for(i = 0;a[i] != '\0';)
{
if(Int(a[i]) == 0)
{
i++;
continue;
}
else if(Int(a[i]) != 0)
break;
}
while(a[i] != '\0')
a[j++] = a[i++];
a[j] = '\0';
return a;
}
//用于检测输入的是否为数字,如果是就返回0,不是就返回1
int checkNum(const char *num)
{
int i;
for(i = 0; (size_t)i < strlen(num); i++)
{
if(num[i] < '0' || num[i] > '9')
return 1;
}
return 0;
}
int *multiBigInteger(const char *num1,const char *num2)
{
int *result = NULL;
int num1Len = strlen(num1);
int num2Len = strlen(num2);
int resultLen;
int i,j;
resultLen = num1Len + num2Len;
result = (int *)malloc((resultLen+1)*sizeof(int));
memset(result,0,(resultLen+1)*sizeof(int));
result[0] = resultLen;
//result的第一位用于保存result的长度
/*
*num1和num2相乘,这个算法不采用先进位的方式,所以算法
*是从左到右按顺序来乘,然后将结果保存在result的每一位
*中,循环一次result就从下一位开始求和。
*/
for(j = 0; j < num2Len; j++)
{
for(i = 0; i < num1Len; i++)
{
/*
*result第一位是用来保存result长度的,而第二位是保存
*结果最后的进位位的。如果没有进位,则为0,所以相乘之
*和是从第三位开始的。
*/
result[i+j+2] += Int(num1[i]) * Int(num2[j]);
}
}
/*
*这个循环用来处理进位的,所以要从result的最后一位一直处理到
*首位。要注意result的总长度是resultLen+1,有一位是保存result
*的长度,而C语言的下标是从0 开始的,所以result的最后一位下标
*是resultLen,而第一位是1
*/
for(i = resultLen; i > 1; i--)
{
result[i - 1] += result[i]/10;
result[i] = result[i] % 10;
}
printf("num1Len:%d\nnum2Len:%d\n",num1Len,num2Len);
return result;
}
题目描述:输入n个不超过100位的大整数的乘积。
输入:
3
12345
67890
456789
输出:537024
这个结果方式比较简单,按位相加,注意进位即可!
#include
#include
#define MAX 100
char *BigDataAdd(char *s1,char *s2)
{
if(s1 == NULL || s2 == NULL)
return NULL;
int len1 = strlen(s1);
int len2 = strlen(s2);
int Maxlen = (len1>len2)?len1:len2;
int i,k;
for(i = len1-1,k=Maxlen;i >= 0;i--,k--)
s1[k] = s1[i] - '0';
if(k >= 0)
memset(s1,0,(k+1)*sizeof(char));
for(i = len2-1,k=Maxlen; i >= 0; i--,k--)
s2[k] = s2[i] - '0';
if(k>=0)
memset(s2,0,(k+1)*sizeof(char));
for(i = Maxlen;i >= 1; i--)
{
s1[i] += s2[i];
if(s1[i] >= 10)
{
s1[i] -= 10;
s1[i-1] += 1;
}
}
if(s1[0] == 0)
{
for(i = 1;i <= Maxlen; i++)
s1[i-1] = s1[i] + '0';
s1[i-1] = '\0';
}
else
{
for(i=0; i<=Maxlen; i++)
s1[i] = s1[i] + '0';
s1[i] = '\0';
}
return s1;
}
char *input(int n,char *temp)
{
char s1[MAX];
if(n == 0)
{
printf("%s\n",temp);
return temp;
}
else
{
scanf("%s",s1);
BigDataAdd(temp,s1);
input(n-1,temp);
}
}
int main()
{
int n;
char temp[MAX + 5] = {'\0'};
scanf("%d",&n);
input(n,temp);
return 0;
}
方法二
将输入字符翻转相加,然后结果再次翻转,注意,该算法是利用Java实现的。
private static String add(String str1,String str2)
{
//任何一个字符串为空,都不需要继续运算了。
if(str1 == null || "".equals(str1))
return str2;
if(str2 == null || "".equals(str2))
return str1;
int maxlen = Math.max(str1.length(), str2.length());
//定义一个存储结果的字符串
StringBuffer result = new StringBuffer(maxlen + 1);
//对输入的两个字符串进行翻转
str1 = new StringBuffer(str1).reverse().toString();
str2 = new StringBuffer(str2).reverse().toString();
int minlen = Math.min(str1.length(), str2.length());
int carry = 0;
int currentNum = 0;
int i = 0;
for(;i < minlen; i++)
{
//分别获取两个字符对应的字面数值,然后相加,再加上进位
currentNum = str1.charAt(i) + str2.charAt(i) - 2*'0' + carry;
//获取进位值
carry = currentNum / 10;
//处理当前位的最终值
currentNum %= 10;
//保存当前位的值到最终的字符缓冲区中
result.append(String.valueOf(currentNum));
}
//找到长度大的那个数
if(str1.length() < str2.length())
str1 = str2;
//对长度大的数剩余的位进行处理,处理方式参考上面的过程
for(;i < str1.length(); i++)
{
currentNum = str1.charAt(i) - '0' + carry;
carry = currentNum / 10;
currentNum %= 10;
result.append(String.valueOf(currentNum));
}
//处理最后一个进位
if(carry > 0)
result.append(String.valueOf(carry));
//最后翻转恢复字符串,返回结果
return result.reverse().toString();
}
参考文章:
https://www.cnblogs.com/king-ding/p/bigIntegerMul.html