大数加法
大数阶乘的基础是大数加法,那么我先从大数加法看起:
题目描述:
给定整数a,b,计算a+b的值,其中a,b的位数<=10000
这种题目的特点是,就算使用long long 型去存储a,b,也是存不下的。因此,我们使用char型数组来存储这两个超级大数。
但在此之前,我们需要先回忆一下我们是如何计算两个数的加法的。我们会把这两个数按位对齐排列,然后从低位往高位计算。这样做的好处是低位运算有时会向高位产生进位,如果从高位往低位运算在不明确进位的条件下会出错。因此,我们在进行大数运算时也会从低位开始计算起。
为了实现从低位向高位的运算,我们在数组中存储大数时采用从最低位(也就是个位)开始存储。如下图:
但我们使用scanf手动输入的又是正序的数,因此需要使用一个循环帮我们完成逆序操作:
//buffer数组用于暂存输入的数(正序)
char* buffer = (char*)malloc(MAXLEN * sizeof(char));
//以下两个加数存储的并不是0~9的ASCII码,而直接是0~9,方便之后的数据运算
char a[MAXLEN + 1] = { 0 };//加数1
char b[MAXLEN + 1] = { 0 };//加数2
scanf("%s", buffer);
//a,b均采用逆序存储
for (int tmp = 0, i = strlen(buffer) - 1; i >= 0; i--) {
a[tmp++] = buffer[i] - '0';
}
free(buffer);
buffer = (char*)malloc(MAXLEN * sizeof(char));
scanf("%s", buffer);
for (int tmp = 0, i = strlen(buffer) - 1; i >= 0; i--) {
b[tmp++] = buffer[i] - '0';
}
以上操作,我们就可以得到两个逆序的数了。接下来就是加法运算的过程。
我们从个位开始计算起,现在逆序存储的好处就显示出来了。它们是自动对齐的,也就是说他们的个位都对应数组中第0个值、十位对应数组中第1个值....
我们要额外定义一个进位up,用来记录每次从低位向高位产生的进位。
//计算加法结果,并把计算结果存储在a数组中,因为之前我们是逆序存储的,因此在计算时,相当于从个位开始相加
for (int up = 0, i = 0,tmp; i < MAXLEN; i++) {
tmp = a[i] + b[i] + up;
a[i] = tmp % 10;
up = tmp / 10;
}
我们现在已经得到结果了,只需要输出在控制台就可以了。但是别忘了,我们的数是逆序存储的,还要把顺序倒过来才行:
//计算过后的a数组实际上就是所得的结果,但仍然是逆序存储的,为此,在输出时我们可以将它翻转过来
int i=MAXLEN;
while (i >= 0) {
//首先要去除高位多余的0,这些0不需要输出
while (a[i] == 0)i--;
for (; i >= 0; i--) {
printf("%d", a[i]);
}
}
至此,我们完成了大数加法的计算,完整的代码如下:
#include<iostream>
#include<string>
#include<stdio.h>
#include<stdlib.h>
#define MAXLEN 10000
using namespace std;
int main() {
char* buffer = (char*)malloc(MAXLEN * sizeof(char));
//以下两个加数存储的并不是0~9的ASCII码,而直接是0~9,方便之后的数据运算
char a[MAXLEN + 1] = { 0 };//加数1
char b[MAXLEN + 1] = { 0 };//加数2
scanf("%s", buffer);
//a,b均采用逆序存储
for (int tmp = 0, i = strlen(buffer) - 1; i >= 0; i--) {
a[tmp++] = buffer[i] - '0';
}
free(buffer);
buffer = (char*)malloc(MAXLEN * sizeof(char));
scanf("%s", buffer);
for (int tmp = 0, i = strlen(buffer) - 1; i >= 0; i--) {
b[tmp++] = buffer[i] - '0';
}
//计算加法结果,并把计算结果存储在a数组中,因为之前我们是逆序存储的,因此在计算时,相当于从个位开始相加
for (int up = 0, i = 0,tmp; i < MAXLEN; i++) {
tmp = a[i] + b[i] + up;
a[i] = tmp % 10;
up = tmp / 10;
}
//计算过后的a数组实际上就是所得的结果,但仍然是逆序存储的,为此,在输出时我们可以将它翻转过来
int i=MAXLEN;
while (i >= 0) {
//首先要去除高位多余的0,这些0不需要输出
while (a[i] == 0)i--;
for (; i >= 0; i--) {
printf("%d", a[i]);
}
}
return 0;
}
大数阶乘
问题描述:
对于给定的整数n,计算n!,其中n<=1000
我们可以沿用刚刚加法的核心代码,也就是下面这一段:
//计算加法结果,并把计算结果存储在a数组中,因为之前我们是逆序存储的,因此在计算时,相当于从个位开始相加
for (int up = 0, i = 0,tmp; i < MAXLEN; i++) {
tmp = a[i] + b[i] + up;
a[i] = tmp % 10;
up = tmp / 10;
}
只需要做亿点点改动即可:
for (i = 2; i <= n; i++) {
for (up = 0, j = 0; j <= MAXLEN; j++) {
tmp = arr[j] * i + up;
arr[j] = tmp % 10;
up = tmp / 10;
}
}
整数n代表我们要进行的n!中的n。所以最外层的循环所代表的含义就是阶乘次数。里面的小循环则是每次阶乘中乘法的运算。tmp仍然用来存储中间结果。
完整代码如下:
#include<iostream>
#include<string>
#include<stdio.h>
#include<stdlib.h>
#define MAXLEN 10000
using namespace std;
int main() {
int n, i, j, up, tmp;
char arr[MAXLEN + 1] = { 1 };
scanf("%d", &n);
for (i = 2; i <= n; i++) {
for (up = 0, j = 0; j <= MAXLEN; j++) {
tmp = arr[j] * i + up;
arr[j] = tmp % 10;
up = tmp / 10;
}
}
for (i = MAXLEN; i >= 0; i--) {
if(arr[i]!=0)
for (; i >= 0; i--) {
printf("%d", arr[i]);
}
}
return 0;
}