原文1链接:https://blog.csdn.net/lisp1995/article/category/6384570
原文2链接:https://www.cnblogs.com/wuqianling/p/5387099.html
原文3链接:https://www.nowcoder.com/questionTerminal/0337e32b1e5543a19fa380e36d9343d7?f=discussion
一.大数加法
两个大数我们可以用数组来保存,在数组中逐位进行相加,再判断该位相加后是否需要进位。
为了方便计算,我们将数字的低位放在数组的前面,高位放在后面。
/*
大数加法
参数:
num1为第一个大数,用字符数组保存
num2为第二个大数
sum数组保存相加的结果 即:num1+num2=sum
返回值:返回数组sum的有效长度,即计算结果的位数
*/
int Addition(char num1[], char num2[], int sum[])
{
int i, j, len;
int n2[MAX] = {0};
int len1 = strlen (num1); // 计算数组num1的长度,即大数的位数
int len2 = strlen (num2); // 计算数组num2的长度,即大数的位数
len = len1>len2 ? len1 : len2; // 获取较大的位数
//将num1字符数组的数字字符转换为整型数字,且逆向保存在整型数组sum中,即低位在前,高位在后
for (i = len1-1, j = 0; i >= 0; i--, j++)
sum[j] = num1[i] - '0';
// 转换第二个数
for (i = len2-1, j = 0; i >= 0; i--, j++)
n2[j] = num2[i] - '0';
// 将两个大数相加
for (i = 0; i <= len; i++)
{
sum[i] += n2[i]; // 两个数从低位开始相加
if (sum[i] > 9) // 判断是否有进位
{ // 进位
sum[i] -= 10;
sum[i+1]++;
}
}
if(sum[len] != 0) // 判断最高位是否有进位
len++;
return len; // 返回和的位数
}
二.大数减法
相减算法也是从低位开始减的。先要判断被减数和减数哪一个位数长,若被减数位数长是正常的减法;若减数位数长,则用被减数减去减数,最后还要加上负号;当两数位数长度相等时,最好比较哪一个数字大,否则负号处理会很繁琐;处理每一项时要,如果前一位相减有借位,就先减去上一位的借位,无则不减,再去判断是否能够减开被减数,如果减不开,就要借位后再去减,同时置借位为1,否则置借位为0。
#include<stdio.h>
#include<string.h>
#define MAX 1000 // 大数的最大位数
/*
大数减法
参数:
num1为被减数,用字符数组保存
num2为减数
sum数组保存相减的结果 即:num1-num2=sum
返回值:返回数组sum的有效长度,即计算结果的位数
*/
int Subtraction(char num1[], char num2[], int sum[]) {
int i, j, len, blag;
char *temp;
int n2[MAX] = {0};
int len1 = strlen(num1); // 计算数组num1的长度,即大数的位数
int len2 = strlen(num2); // 计算数组num2的长度,即大数的位数
// 在进行减法之前要进行一些预处理
blag = 0; // 为0表示结果是正整数,为1表示结果是负整数
if(len1 < len2) { // 如果被减数位数小于减数
blag = 1; // 标记结果为负数
// 交换两个数,便于计算
temp = num1;
num1 = num2;
num2 = temp;
len = len1;
len1 = len2;
len2 = len;
} else if(len1 ==len2) { // 如果被减数的位数等于减数的位数
// 判断哪个数大
for(i = 0; i < len1; i++) {
if(num1[i] == num2[i])
continue;
if(num1[i] > num2[i]) {
blag = 0; // 标记结果为正数
break;
} else {
blag = 1; // 标记结果为负数
// 交换两个数,便于计算
temp = num1;
num1 = num2;
num2 = temp;
break;
}
}
}
len = len1>len2 ? len1 : len2; // 获取较大的位数
//将num1字符数组的数字转换为整型数且逆向保存在整型数组sum中,即低位在前,高位在后
for (i = len1-1, j = 0; i >= 0; i--, j++)
sum[j] = num1[i] - '0';
// 转换第二个数
for (i = len2-1, j = 0; i >= 0; i--, j++)
n2[j] = num2[i] - '0';
// 将两个大数相减
for (i = 0; i <= len; i++) {
sum[i] = sum[i] - n2[i]; // 两个数从低位开始相减
if (sum[i] < 0) { // 判断是否有借位
// 借位
sum[i] += 10;
sum[i+1]--;
}
}
// 计算结果长度
for (i = len1-1; i>=0 && sum[i] == 0; i--)
;
len = i+1;
if(blag==1) {
sum[len] = -1; // 在高位添加一个-1表示负数
len++;
}
return len; // 返回结果的位数
}
int main() {
int i, len;
int sum[MAX] = {0}; // 存放计算的结果,低位在前,高位在后,即sum[0]是低位
char num1[] = "987654321987654321"; // 第一个大数
char num2[] = "123456789123456789"; // 第二个大数
len = Subtraction(num1, num2, sum); // 两数相减
// 输出结果
printf("%s\n -\n%s\n =\n", num1, num2);
if(sum[i=len-1] < 0) { // 根据高位是否是-1判断是否是负数
printf("-"); // 输出负号
i--;
}
for (; i >= 0; i--)
printf("%d", sum[i]);
printf("\n");
return 0;
}
三.大数乘法
首先说一下乘法计算的算法,从低位向高位乘,在竖式计算中,我们是将乘数第一位与被乘数的每一位相乘,记录结果,之后,用第二位相乘,记录结果并且左移一位,以此类推,直到计算完最后一位,再将各项结果相加,得出最后结果。
计算的过程基本上和小学生列竖式做乘法相同。为了编程方便,并不急于处理进位,而是将进位问题留待最后统一处理。
总结一个规律: 即一个数的第i 位和另一个数的第j 位相乘所得的数,一定是要累加到结果的第i+j 位上。这里i, j 都是从右往左,从0 开始数。
ans[i+j] = a[i]*b[j];
另外注意进位时要处理,当前的值加上进位的值再看本位数字是否又有进位;前导清零。
#include<stdio.h>
#include<string.h>
#define MAX 1000 // 大数的最大位数
// 注:
// 本代码在以下博客代码中进行修改:
// http://www.cnblogs.com/javawebsoa/archive/2013/08/01/3231078.html
//
/*
函数SubStract功能:
用长度为len1的大整数p1减去长度为len2的大整数p2
结果存在p1中,返回值代表结果的长度
不够减:返回-1 , 正好够:返回0
*/
int SubStract(int *p1, int len1, int *p2, int len2) {
int i;
if(len1 < len2)
return -1;
if(len1 == len2 ) {
// 判断p1 > p2
for(i = len1-1; i >= 0; i--) {
if(p1[i] > p2[i]) // 若大,则满足条件,可做减法
break;
else if(p1[i] < p2[i]) // 否则返回-1
return -1;
}
}
for(i = 0; i <= len1-1; i++) { // 从低位开始做减法
p1[i] -= p2[i]; // 相减
if(p1[i] < 0) { // 若是否需要借位
// 借位
p1[i] += 10;
p1[i+1]--;
}
}
for(i = len1-1; i >= 0; i--) { // 查找结果的最高位
if( p1[i] ) //最高位第一个不为0
return (i+1); //得到位数并返回
}
return 0; //两数相等的时候返回0
}
/*
大数除法---结果不包括小数点
num1 被除数
num2 除数
sum 商,存放计算的结果,即:num1/num2=sum
返回数组sum的有效长度,即商的位数
*/
int Division(char num1[], char num2[], char sum[]) {
int k, i, j;
int len1, len2, len=0; //大数位数
int dValue; //两大数相差位数
int nTemp; //Subtract函数返回值
int num_a[MAX] = {0}; //被除数
int num_b[MAX] = {0}; //除数
int num_c[MAX] = {0}; //商
len1 = strlen(num1); //获得大数的位数
len2 = strlen(num2);
//将数字字符转换成整型数,且翻转保存在整型数组中
for( j = 0, i = len1-1; i >= 0; j++, i-- )
num_a[j] = num1[i] - '0';
for( j = 0, i = len2-1; i >= 0; j++, i-- )
num_b[j] = num2[i] - '0';
if( len1 < len2 ) { //如果被除数小于除数,直接返回-1,表示结果为0
return -1;
}
dValue = len1 - len2; //相差位数
for (i = len1-1; i >= 0; i--) { //将除数扩大,使得除数和被除数位数相等
if (i >= dValue)
num_b[i] = num_b[i-dValue];
else //低位置0
num_b[i] = 0;
}
len2 = len1;
for(j = 0; j <= dValue; j++ ) { //重复调用,同时记录减成功的次数,即为商
while((nTemp = SubStract(num_a, len1, num_b+j, len2-j)) >= 0) {
len1 = nTemp; //结果长度
num_c[dValue-j]++; //每成功减一次,将商的相应位加1
}
}
// 计算商的位数,并将商放在sum字符数组中
for(i = MAX-1; num_c[i] == 0 && i >= 0; i-- ); //跳过高位0,获取商的位数
if(i >= 0)
len = i + 1; // 保存位数
for(j = 0; i >= 0; i--, j++) // 将结果复制到sum数组中
sum[j] = num_c[i] + '0';
sum[j] = '\0'; // sum字符数组结尾置0
return len; // 返回商的位数
}
int main() {
int i;
int len; // 商的位数
char num1[MAX] = "1234567899876543210"; // 第一个大数
char num2[MAX] = "20160415123025"; // 第二个大数
char sum[MAX] = {0}; // 计算结果
//scanf("%s", num1); //以字符串形式读入大数
//scanf("%s", num2);
len = Division(num1, num2, sum);
//输出结果
printf("%s\n ÷\n%s\n =\n", num1, num2);
if( len>=0 ) {
for(i = 0; i < len; i++ )
printf("%c", sum[i]);
} else {
printf("0");
}
printf("\n");
return 0;
}
四.大数除法
#include<stdio.h>
#include<string.h>
char a[100],b[100];//用两个字符串用来输入两个大数
int x[100],y[100],z[100],m[100];//被除数 除数 商 余数
int digit;//大数的位数
void sub(int x[],int y[],int len1,int len2) { //大数减法
int i;
for(i=0; i<len1; i++) {
if(x[i]<y[i]) {
x[i]=x[i]+10-y[i];
x[i+1]--;
} else
x[i]=x[i]-y[i];
}
for(i=len1-1; i>=0; i--) { //判断减法结束之后,被除数的位数
if(x[i]) {
digit=i+1;
break;
}
}
}
int judge(int x[],int y[],int len1,int len2) {
int i;
if(len1<len2)
return -1;
if(len1==len2) { //若两个数位数相等
for(i=len1-1; i>=0; i--) {
if(x[i]==y[i])//对应位的数相等
continue;
if(x[i]>y[i])//被除数 大于 除数,返回值为1
return 1;
if(x[i]<y[i])//被除数 小于 除数,返回值为-1
return -1;
}
return 0;//被除数 等于 除数,返回值为0
}
}
int main() {
int i,j=0,k=0,temp;
int len1,len2,len;//len两个大数位数的差值
while(~scanf("%s %s",a,b)) {
len1=strlen(a);//被除数位数
len2=strlen(b);//除数位数
for(i=len1-1,j=0; i>=0; i--) //将字符串中各个元素倒序储存在数组中
x[j++]=a[i]-'0';
for(i=len2-1,k=0; i>=0; i--)
y[k++]=b[i]-'0';
if(len1<len2) { //当被除数位数 小于 除数位数时
printf("商是:0\n");
printf("余数是:");
puts(a);
} else { //当被除数位数 大于或者 除数位数时
len=len1-len2;//两个大数位数的差值
for(i=len1-1; i>=0; i--) { //将除数后补零,使得两个大数位数相同。被除数:4541543329 除数:98745,加零后:9874500000
if(i>=len)
y[i]=y[i-len];
else
y[i]=0;
}
len2=len1;//将两个大数数位相同
digit=len1; //将原被除数位数赋值给digit
for(j=0; j<=len; j++) {
z[len-j]=0;
while(((temp=judge(x,y,len1,len2))>=0)&&digit>=k) { //判断两个数之间的关系以及位数与除数原位数的关系
sub(x,y,len1,len2); //大数减法函数
z[len-j]++;//储存商的每一位
len1=digit;//重新修改被除数的长度
if(len1<len2&&y[len2-1]==0)
len2=len1;//将len1长度赋给len2;
}
if(temp<0) { //若被除数 小于 除数,除数减小一位。例如:被除数:4541543329 除数:(原)98745,(加零后)9874500000,后退一位后:0987450000
for(i=1; i<len2; i++)
y[i-1]=y[i];
y[i-1]=0;
if(len1<len2)
len2--;
}
}
printf("商是:");
for(i=len; i>0; i--) { //去掉前缀0
if(z[i])
break;
}
for(; i>=0; i--)
printf("%d",z[i]);
printf("\n");
printf("余数是:");
for(i=len1; i>0; i--) {
if(x[i])
break;
}
for(; i>=0; i--)
printf("%d",x[i]);
printf("\n");
}
}
return 0;
}
二进制转换
链接:https://www.nowcoder.com/questionTerminal/0337e32b1e5543a19fa380e36d9343d7?f=discussion
来源:牛客网
#include <stdio.h>
#include <string.h>
using namespace std;
int main(){
char str[31];
int num[31];
int res[2000];
while(scanf("%s",str)!=EOF){ //字符串转数字
for(int i=0;i<strlen(str);i++)
num[i]=str[i]-'0';
int size=strlen(str);
int index=0;//结果数组游标
for(int i=0;i<size;){ //控制被除数开始位置
int temp=0,remain=0;//余数
for(int j=i;j<size;j++){ //控制除法运算,竖式除法,从头往后每一位依次作除法
temp=(10*remain+num[j])%2;
num[j]=(10*remain+num[j])/2;
remain=temp;
}
res[index]=remain;
index++;
while(num[i]==0) //从第一个非0开始
i++;
}
for(int i=index-1;i>=0;i--) //逆序输出
{
printf("%d",res[i]);
}
printf("\n");
}
}
五.大数阶乘
#include <stdio.h>
int main() {
int a[20001];//储存每一位所得到的数
int temp,digit,n,i,j=0;//temp每次的得数 digit每次得数的位数
scanf("%d",&n);
a[0]=1;//从1开始乘
digit=1;//位数从第一位开始
for(i=2; i<=n; i++) {
int num=0;
for(j=0; j<digit; j++) {
temp=a[j]*i+num;//将一个数的每一位数都分别乘以i,
a[j]=temp%10;//将一个数的每一位数利用数组进行储存
num=temp/10;
}
while(num) { //判断退出循环后,num的值是否为0
a[digit]=num%10;//继续储存
num=num/10;
digit++;
}
}
for(i=digit-1; i>=0; i--) //倒序输出每一位
printf("%d",a[i]);
printf("\n");
return 0;
}