大一小白尝试高精度除高精度
引言
笔者是一位大一学生,在做题的过程中接触到了高精度除法,写下这篇博客帮助理清思路。希望这篇博客能够帮助到更多刚接触C语言的同学们。如果文中有错误,欢迎指正。
一、什么是高精度除法
高精度除法就是对于普通数据类型无法表示的大整数进行除法运算。
二、为什么需要高精度除法
在C语言中,常用的数据类型有int, long long, double等,但是这些数据类型的大小有限,当输入的数据过大,这些数据类型就无法实现其功能,强行使用可能会出现错误。在这种情况下,我们需要高精度除法来完成运算。
三、高精度除法的实现方式
思路
参考高精度乘法的思路,首先我们知道电脑笨笨的,其次我们知道可以回到小学寻求答案,于是高精度除法的实现思路就十分清晰了。那就是模拟手动运算除法的方式,也就是说,我们要通过竖式除法运算达到高精度运算的目的。
数据类型
与高精度乘法相类似的,我们选择字符串来存储很大很大的一些整数(真是太可恶了!)
竖式除法复习
这里我们只看整数的除法法则
1)从被除数的高位起,先看除数有几位,再用除数试除被除数的前几位,如果它比除数小,再试除多一位数;
2)除到被除数的哪一位,就在那一位上面写上商;
3)每次除后余下的数必须比除数小。
实现步骤
1.输入
2.试除(比大小移位)
3.结束
4.输出
准备工作
void High_Precision_Division(char c_dividend[], char c_divisor[], char c_quotient[]) {
int i;
int i_dividend[Number_Size];//被除数
int i_divisor[Number_Size];//除数
int i_quotient[Number_Size];//商
char temp_answer[Number_Size];
int start;
int flag;
int length_temp_answer;
int length_divisor = strlen(c_divisor);
int length_dividend = strlen(c_dividend);
memset(c_quotient, 0, length_quotient);
memset(i_dividend, 0, sizeof(i_dividend));
memset(i_divisor, 0, sizeof(i_divisor));
memset(i_quotient, 0, sizeof(i_quotient));
memset(temp_answer, 0, sizeof(temp_answer));//完成初始化
if (Size_Comparison(c_dividend, length_dividend, c_divisor, length_divisor) < 0) {
c_quotient[0] = '0';
return;
}
start = 0;
}
进行试除
我们知道,试除的过程实际上就是比大小,如果不够大则移动一位,如果够大则算出商。为了方便,我们这里引入一个比大小的函数。
int Size_Comparison(char str_1[], int length_1, char str_2[], int length_2) {
int i;
if (length_1 < length_2) {
return -1;
}
else if (length_1 > length_2) {
return 1;
}
else {
for (i = 0; i < length_1; i++) {
if (str_1[i] < str_2[i]) {
return -1;
}
else if (str_1[i] > str_2[i]) {
return 1;
}
}
return 0;
}
}
这个函数传入两个char*,两个int,若返回-1则第一个小,返回1则第一个大,返回0则相等。
现在为了能够算出商,我们需要引入高精度减法的函数。试除的数能减几次,商就是几。
//start代表的是被除数试除的起始位置
while (length_dividend - start >= length_divisor) {
if (length_dividend - start == length_divisor && Size_Comparison(&c_dividend[start], length_divisor, c_divisor, length_divisor) < 0) {
break;
}
//如果剩下的数不够除了(长度相等而小于)就退出循环
if (Size_Comparison(&c_dividend[start], length_divisor, c_divisor, length_divisor) < 0) {
//长度足够但是同位数时不够,就向后借一位
i_quotient[start + length_divisor]++;//对应位置商加一
High_Precision_Subtraction(&c_dividend[start], length_divisor + 1, c_divisor, length_divisor, temp_answer, 1, &flag);
//从对应位置减去除数
length_temp_answer = strlen(temp_answer);
//temp_answer里面存储的是差
memset(&c_dividend[start], 0, sizeof(char) * (length_divisor + 1));
start = start + length_divisor + 1 - length_temp_answer;
for (i = 0; i < length_temp_answer; i++) {
c_dividend[i + start] = temp_answer[i];
}
//这一段完成的是拷贝,当然也可以用函数,之后会进行优化
memset(temp_answer, 0, sizeof(temp_answer));
}
else {//与上一段同理,长度相同时可以继续,就直接作差
i_quotient[start + length_divisor - 1]++;
High_Precision_Subtraction(&c_dividend[start], length_divisor, c_divisor, length_divisor, temp_answer, 1, &flag);
length_temp_answer = strlen(temp_answer);
memset(&c_dividend[start], 0, sizeof(char) * length_divisor);
start = start + length_divisor - length_temp_answer;
for (i = 0; i < length_temp_answer; i++) {
c_dividend[i + start] = temp_answer[i];
}
memset(temp_answer, 0, sizeof(temp_answer));
}
}
//完成除的过程
处理商
首先将多余的0除去,在这里是前导零,然后转换回字符串
for (i = 0; i < length_dividend; i++) {
if (i_quotient[i]) {
start = i;
break;
}
}
length_quotient = length_dividend - start;
for (i = start; i < length_dividend; i++) {
c_quotient[i - start] = i_quotient[i] + '0';
}
//处理商并输出
全部代码
void High_Precision_Division(char c_dividend[], char c_divisor[], char c_quotient[], int length_quotient) {
int i;
int i_dividend[Number_Size];//被除数
int i_divisor[Number_Size];//除数
int i_quotient[Number_Size];//商
char temp_answer[Number_Size];
int start;
int flag;
int length_temp_answer;
int length_divisor = strlen(c_divisor);
int length_dividend = strlen(c_dividend);
memset(c_quotient, 0, length_quotient);
memset(i_dividend, 0, sizeof(i_dividend));
memset(i_divisor, 0, sizeof(i_divisor));
memset(i_quotient, 0, sizeof(i_quotient));
memset(temp_answer, 0, sizeof(temp_answer));//完成初始化
if (Size_Comparison(c_dividend, length_dividend, c_divisor, length_divisor) < 0) {
c_quotient[0] = '0';
return;
}
start = 0;
while (length_dividend - start >= length_divisor) {
if (length_dividend - start == length_divisor && Size_Comparison(&c_dividend[start], length_divisor, c_divisor, length_divisor) < 0) {//如果剩下的数不够除了就退出循环
break;
}
if (Size_Comparison(&c_dividend[start], length_divisor, c_divisor, length_divisor) < 0) {//长度足够但是同位数时不够,就向后借一位
i_quotient[start + length_divisor]++;//对应位置商加一
High_Precision_Subtraction(&c_dividend[start], length_divisor + 1, c_divisor, length_divisor, temp_answer, 1, &flag);
length_temp_answer = strlen(temp_answer);
memset(&c_dividend[start], 0, sizeof(char) * (length_divisor + 1));
start = start + length_divisor + 1 - length_temp_answer;
for (i = 0; i < length_temp_answer; i++) {
c_dividend[i + start] = temp_answer[i];
}
memset(temp_answer, 0, sizeof(temp_answer));
}
else {
i_quotient[start + length_divisor - 1]++;
High_Precision_Subtraction(&c_dividend[start], length_divisor, c_divisor, length_divisor, temp_answer, 1, &flag);
length_temp_answer = strlen(temp_answer);
memset(&c_dividend[start], 0, sizeof(char) * length_divisor);
start = start + length_divisor - length_temp_answer;
for (i = 0; i < length_temp_answer; i++) {
c_dividend[i + start] = temp_answer[i];
}
memset(temp_answer, 0, sizeof(temp_answer));
}
}
//完成除的过程
for (i = 0; i < length_dividend; i++) {
if (i_quotient[i]) {
start = i;
break;
}
}
length_quotient = length_dividend - start;
for (i = start; i < length_dividend; i++) {
c_quotient[i - start] = i_quotient[i] + '0';
}
//处理商并输出
return;
}
四、结语
这个函数使用的实际上是减法,因此在时间复杂度上是非常高的,处理很小的除数除很大的被除数十分容易超时,后续会对除数这一块进行优化。十分感谢各位的阅读!