在C语言解决问题的过程中,我们常常会遇到一些问题需要进行大数处理。
这里定义的大数为位数达到几百位的整数。
通常做法是将大数转化为一维数组,再进行四则运算。
在四则运算中,除法的高效率实现最为困难。
本文即是针对这种高精度大数除法问题给出模板化的解答。
首先,我们的思路是将大数转化为一维数组存储起来。但是,直接读入数组的话会产生一个问题。比如我们定义一个数组arr[1000],直接读入的话,大数的最高位会储存在下标为零的数组空间内,也就是arr[0]。这不符合我们对数字的自然逻辑,也会对之后的处理带来困难。所以在以下所涉及到的数字中,包括被除数、除数、商、余数这四个数,我们都采用反向读入的方式,也就是将个位储存在arr[0]中,将十位储存在arr[1]中,依次类推。
先定义数组
char beiChuShuStr[1004] = { 0 };
char chuShuStr[1004] = { 0 };
int beiChuShu[1004] = { 0 };
int chuShu[1004] = { 0 };
int shang[1004] = { 0 };
int yuShu[1004] = { 0 };
这里为什么多定义了两个字符串数组呢?
因为我们的处理思路是先将大数以字符串的形式读入,再进行反向赋值给数字数组
具体实现如下:
scanf("%s", beiChuShuStr);
scanf("%s", chuShuStr);
int len1 = strlen(beiChuShuStr);
int len2 = strlen(chuShuStr);
for (int i = 0; i < len1; i++) {//将字符串数组输入整形数组,调整顺序使得下标为零的元素
beiChuShu[i] = beiChuShuStr[len1 - 1 - i] - '0';//对应个位,下标为一的元素对应十位
}
for (int i = 0; i < len2; i++) {
chuShu[i] = chuShuStr[len2 - 1 - i] - '0';
}
如此以来我们的大数就成功储存了
同学们可以学习一下字符转化为数字的方法,只要减去‘0’就可以了。
完成了数据的读入,现在来确定算法的具体实现方式。
一种很简单的想法是,除法本身是许多个减法的叠加,那你只要令被除数不断减去除数,直到不能减不就好了吗?然而,这种思路的效率很低,因为如果被除数的位数是几百位,除数的位数是几十位,相除之后的商大概率也是几百位。相当于电脑要进行10的几百次方的运算,效率是很低下的。
让我们回归淳朴,来看看最开始手算除法是用什么方式呢?
答案是竖式除法。
竖式除法很重要的一步是,判断当前被除数的前lb位能否与除数相减,其中lb是除数的长度。对吧,尤其是对于几百位的大数而言,这个判断要进行几百次,所以,我们封装一个函数用来执行这个判断。
bool ability_to_subtraction(int a[], int b[], int last_weishu, int lb) {
if (a[last_weishu + lb] != 0)return true;
for (int i = lb; i > 0; i--) {
if (a[last_weishu + i - 1] > b[i - 1])return true;
if (a[last_weishu + i - 1] < b[i - 1])return false;
}
return true;
}
博主英语不好,只能想到这么一个函数名。意思是减法的能力。
传入的last_weishu这样一个参数,大小为la-lb,用来描述被除数的前lb位中,最小一位的位置。
这个语句意思是,如果取到的被除数长度比除数大,那么一定可以相减;
if (a[last_weishu + lb] != 0)return true;
这个语句的意思是从最大位开始判断,遇到被除数的该位数字大于除数的情况就可以相减,被除数的该位数字小于除数的就不可相减;
for (int i = lb; i > 0; i--) {
if (a[last_weishu + i - 1] > b[i - 1])return true;
if (a[last_weishu + i - 1] < b[i - 1])return false;
}
最后一个语句return true意思是,如果被除数取出的每一位都和除数对应的那个位子上的数字相等,也可以相减,结果为零嘛。
for (int i = 0; i < la; i++) {//保护被除数不改变,通过对余数进行改变最后得出结果
yuShu[i] = beiChuShu[i];
}
for (int i = la - lb; i >= 0; i--) {
while (ability_to_subtraction(yuShu, chuShu, i, lb)) {
for (int j = 0; j < lb; j++) {
yuShu[i + j] -= chuShu[j];
if (yuShu[i + j] < 0) {
yuShu[i + j] += 10;
yuShu[i + j + 1] -= 1;
}
}
shang[i]++;
}
}
最后输出结果
for (lc = 1004; lc >= 0; lc--)
if (yuShu[lc - 1] != 0)break;
for (ld = 1004; ld >= 0; ld--)
if (shang[ld - 1] != 0)break;
for (int i = 0; i < lc; i++) {
printf("%d", yuShu[i]);
}
printf("\n");
for (int i = 0; i < ld; i++) {
printf("%d", shang[i]);
}
这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如,111111就可以被13整除。 现在,你的程序要读入一个整数x
,这个整数一定是奇数并且不以5结尾。然后,经过计算,输出两个数字:第一个数字s
,表示x
乘以s
是一个光棍,第二个数字n
是这个光棍的位数。这样的解当然不是唯一的,题目要求你输出最小的解。
提示:一个显然的办法是逐渐增加光棍的位数,直到可以整除x
为止。但难点在于,s
可能是个非常大的数 —— 比如,程序输入31,那么就输出3584229390681和15,因为31乘以3584229390681的结果是111111111111111,一共15个1。
输入在一行中给出一个不以5结尾的正奇数x
(<1000)。
在一行中输出相应的最小的s
和n
,其间以1个空格分隔。
31
3584229390681 15
这道题目因为明显涉及到大数,所以我们可以直接借鉴高精度大数除法。
同学们可以自己尝试一下,附有参考代码。
#include
#include
int ability_to_subtraction(int a[], int b[], int last_weishu, int lb) {
if (a[last_weishu + lb] != 0)return 1;
for (int i = lb; i > 0; i--) {
if (a[last_weishu + i - 1] > b[i - 1])return 1;
if (a[last_weishu + i - 1] < b[i - 1])return 0;
}
return 1;
}
int main() {
char N[4] = { 0 };
int n;
int flag = 0;
int yuShu[1002] = { 0 };
int chuShu[4] = { 0 };
int shang[1002] = { 0 };
scanf("%s", &N);
int lb = strlen(N);
int ld;
for (int i = 0; i < lb; i++) {
chuShu[i] = N[lb - 1 - i] - '0';
}
//for (lb = 3; lb >= 0; lb--)
// if (chuShu[lb-1] != 0)break;
for (n = 1; n < 1000 && !flag; n++) {
flag = 1;
memset(yuShu, 0, sizeof(yuShu));
memset(shang, 0, sizeof(shang));
for (int i = 0; i < n; i++) {//生成光棍数
yuShu[i] = 1;
}
for (int i = n - lb; i >= 0; i--) {
while (ability_to_subtraction(yuShu, chuShu, i, lb)) {
for (int j = 0; j < lb; j++) {
yuShu[i + j] -= chuShu[j];
if (yuShu[i + j] < 0) {
yuShu[i + j] += 10;
yuShu[i + j + 1] -= 1;
}
}
shang[i]++;
}
}
for (int i = 0; i < 4; i++) {
if (yuShu[i] != 0)flag = 0;
}
}
if (flag) {
for (ld = 1002; ld >= 0; ld--)
if (shang[ld - 1] != 0)break;
for (int i = ld - 1; i >= 0; i--) {
printf("%d", shang[i]);
}
printf(" %d", n-1);
}
return 0;
}