高精度算法 (C/C++)

做ACM题的时候,经常遇到大数的加减乘除,乘幂,阶乘的计算,这时给定的数据类型往往不够表示最后结果,这时就需要用到高精度算法。高精度算法的本质是把大数拆成若干固定长度的块,然后对每一块进行相应的运算。这里以考虑4位数字为一块为例,且输入的大数均为正整数(也可以考虑其他位,但要注意在每一块进行相应运算时不能超出数据类型的数值范围;有负整数的话读入时判断一下正负号在决定运算)。

1. 高精度加法

以3479957928375817 + 897259321544245为例:

3479 9579 2837 5817
+897 +2593 +2154 +4245
= = = =
4376 12172 4991 10062
进位0 进位1 进位0 进位1
4377 2172 4992 0062

C语言实现代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 200 //整数乘幂运算函数
int Pow(int a, int b)
{
int i = 0, result = 1;
for(i = 0; i < b; ++i)
{
result *= a;
}
return result;
} //High Precision Of Addition
int main()
{
char stra[N], strb[N]; //字符串数组,以字符形式储存两个大数;
int i = 0, step = 4, carry = 0; //step表示块长,carry为进位位;
int lengtha, lengthb, maxlength, resultsize; //maxlength表示stra和strb二者长度较大的那个;
int numa[N], numb[N],numc[N]; //依次储存被加数,加数,和;
memset(numa, 0, sizeof(numa));
memset(numb, 0, sizeof(numb));
memset(numc, 0, sizeof(numc)); //初始化为零;
scanf("%s%s", stra, strb);
lengtha = strlen(stra);
lengthb = strlen(strb); //计算两个大数的长度
//字符数字转为四位一块的整数数字
for(i = lengtha-1; i >= 0; --i)
{
numa[(lengtha-1-i)/step] += (stra[i]-'0')*Pow(10,(lengtha-1-i)%step);
}
for(i = lengthb-1; i >= 0; --i)
{
numb[(lengthb-1-i)/step] += (strb[i]-'0')*Pow(10,(lengthb-1-i)%step);
}
maxlength = lengtha > lengthb ? lengtha : lengthb; //逐块相加,并进位
for(i = 0; i <= maxlength/step; ++i)
{
numc[i] = (numa[i] + numb[i])%Pow(10, step) + carry; //计算和
carry = (numa[i] + numb[i])/Pow(10, step); //计算进位
} //计算最后和的块的总数
resultsize = numc[maxlength/step] > 0 ? maxlength/step : maxlength/step - 1;
printf("%d", numc[resultsize]);
for(i = resultsize-1; i >= 0; --i)
{
printf("%04d", numc[i]); //右对齐,补零输出;
}
printf("\n");
return 0;
}

2. 高精度减法

与加法类似,不同的是要注意正负号和显示位数的变化。以99999037289799 - 100004642015000为例:

先判断被减数和减数哪个大,显然这里减数大,故输出结果为负数。在用大数减去小数,(若某一块相减为负数,则要向高位块借位)如下表

100 0046 4201 5000
-99 -9990 -3728 -9799
1 56 473 5201
借位0 借位1 借位0 借位1
0 56 472 5201

C语言实现代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 200 //整数乘幂运算函数
int Pow(int a, int b)
{
int i = 0, result = 1;
for(i = 0; i < b; ++i)
{
result *= a;
}
return result;
} //High Precision Of Subtraction
int main()
{
char stra[N], strb[N]; //字符串数组,以字符形式储存两个大数;
int i = 0, step = 4, borrow = 0, mark = 0; //step表示块长,borrow为借位位, mark为结果符号位;
int lengtha, lengthb, maxlength, resultsize; //maxlength表示stra和strb二者长度较大的那个;
int numa[N], numb[N],numc[N], *maxnum, *minnum; //依次储存被减数,减数,和;
memset(stra, 0, sizeof(stra));
memset(strb, 0, sizeof(strb));
memset(numa, 0, sizeof(numa));
memset(numb, 0, sizeof(numb));
memset(numc, 0, sizeof(numc)); //初始化为零;
scanf("%s%s", stra, strb);
lengtha = strlen(stra);
lengthb = strlen(strb); //计算两个大数的长度
maxlength = lengtha >= lengthb ? lengtha : lengthb; //字符数字转为四位一块的整数数字
for(i = lengtha-1; i >= 0; --i)
{
numa[(lengtha-1-i)/step] += (stra[i]-'0')*Pow(10,(lengtha-1-i)%step);
}
for(i = lengthb-1; i >= 0; --i)
{
numb[(lengthb-1-i)/step] += (strb[i]-'0')*Pow(10,(lengthb-1-i)%step);
} //找出较大的数
maxnum = numa;
minnum = numb;
mark = 1;
for(i = (maxlength-1)/step; i >= 0; --i)
{
if(numa[i] > numb[i])
{
maxnum = numa;
minnum = numb;
mark = 1;
break;
}
else if(numa[i] < numb[i])
{
maxnum = numb;
minnum = numa;
mark = -1;
break;
}
} //逐块相减,并借位
for(i = 0; i <= maxlength/step; ++i)
{
numc[i] = (maxnum[i] - minnum[i] + Pow(10, step) + borrow)%Pow(10,step); //计算差
borrow = (maxnum[i] - minnum[i] + Pow(10, step) + borrow)/Pow(10, step) - 1; //计算借位
} //计算最后和的块的总数
resultsize = maxlength/step;
while(!numc[resultsize]) --resultsize;
printf("%d", mark*numc[resultsize]);
for(i = resultsize-1; i >= 0; --i)
{
printf("%04d", numc[i]); //右对齐,补零输出;
}
printf("\n");
return 0;
}

3. 高精度乘法

乘法可以看作是乘数每一位与被乘数相乘后再相加,以4296556241 x 56241为例:

被乘数 42 9655 6241
乘数 5 6 2 4 1
被乘数x乘数 42 9655 6241
1 42 9655 6241
4 168*10 38620*10 24964*10
2 84*100 19310*100 12482*100
6 252*1000 57930*1000 37446*1000
5 210*10000 48275*10000 31205*10000
累加和 2362122 543006855 351000081
进位(从低位向高位) 241 54304 35100
241 6426 1955 0081

C语言实现代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 200 //整数乘幂运算函数
int Pow(int a, int b)
{
int i = 0, result = 1;
for(i = 0; i < b; ++i)
{
result *= a;
}
return result;
} //High Precision Of Multiplication
int main()
{
char stra[N], strb[N]; //字符串数组,以字符形式储存两个大数;
int i = 0, j = 0, k = 0, step = 4, carry = 0; //step表示块长,carry为进位位;
int lengtha, lengthb, resultsize, tmpsize, eachnum; //resultsize储存块的总数,eachnum用来储存乘数的每一位
int numa[N], numb[N], numc[N], tmp[N]; //依次储存被乘数数&积,乘数;
memset(numa, 0, sizeof(numa));
memset(numb, 0, sizeof(numb));
memset(numc, 0, sizeof(numc)); //初始化为零;
scanf("%s%s", stra, strb);
lengtha = strlen(stra);
lengthb = strlen(strb); //计算两个大数的长度
//将被乘数字符数字转为四位一块的整数数字
for(i = lengtha-1; i >= 0; --i)
{
numa[(lengtha-1-i)/step] += (stra[i]-'0')*Pow(10,(lengtha-1-i)%step);
}
//将乘数数字字符数字转为一位一块的整数数字
for(i = lengthb-1; i >= 0; --i)
{
numb[lengthb-1-i] = strb[i]-'0';
} resultsize = tmpsize = (lengtha-1)/step;
//取乘数的每一位与被乘数的逐块相乘,并进位;
for(i = 0; i < lengthb; ++i)
{
memcpy(tmp, numa, sizeof(numa)); //将numa数组赋值给tmp数组; k = i/step; //k储存每一块需要向高位块移动的次数;
if(k)
{
for(j = tmpsize; j >= 0; --j)
{
tmp[j+k] = tmp[j];
tmp[j] = 0;
}
tmpsize += k;
} //乘以乘数每一位扩展成的块;
eachnum = numb[i]*Pow(10, i%step);
for(j = 0; j <= tmpsize; ++j)
{
tmp[j] *= eachnum;
} //大数相加
carry = 0; //进位置零;
for(j = 0; j <= resultsize; ++j)
{
numc[j] += tmp[j] + carry;
carry = numc[j]/Pow(10,step);
numc[j] %= Pow(10, step);
}
if(carry)
{
++resultsize;
numc[j] += carry;
}
} //输出
printf("%d", numc[resultsize]);
for(i = resultsize-1; i >= 0; --i)
{
printf("%04d", numc[i]); //右对齐,补零输出;
}
printf("\n");
return 0;
}

4. 高精度除法

高精度除法有两种,一种是高精度除以低精度,另一种是高精度除以高精度。前者只需将每一块除以低精度除数即可;后者则考虑用高精度减法来实现,即每次减去高精度除数,直到减到小于除数,则减的次数即为商,剩余的即为余数。

  • 高精度除以低精度

    以9876342876 / 343为例:
被除数 98 7634 2876
除数 343
向低位块进位 98 137 190
0 2879 4002
余数 190

C语言代码实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 200 //整数乘幂运算函数
int Pow(int a, int b)
{
int i = 0, result = 1;
for(i = 0; i < b; ++i)
{
result *= a;
}
return result;
} //High Precision Of division
//(1)高精度除以低精度
int main()
{
char stra[N]; //字符串数组,以字符形式储存高精度被除数;
int i = 0, step = 4, carry = 0; //step表示块长,carry为高位向低位进位位;
int lengtha, resultsize;
int numa[N], numb, numc[N], numd; //依次储存被除数,除数,商, 余数;
memset(numa, 0, sizeof(numa));
memset(numc, 0, sizeof(numc)); //初始化为零;
scanf("%s%d", stra, &numb);
lengtha = strlen(stra); //计算被除数的长度 //字符数字转为四位一块的整数数字
for(i = lengtha-1; i >= 0; --i)
{
numa[(lengtha-1-i)/step] += (stra[i]-'0')*Pow(10,(lengtha-1-i)%step);
} carry = 0; //高位向低位进位位置零
resultsize = (lengtha-1)/step;
//逐块相除,高位向低位进位
for(i = resultsize; i >= 0; --i)
{
numc[i] = (numa[i] + carry*Pow(10,step))/numb; //计算商
carry = (numa[i] + carry*Pow(10,step))%numb; //计算进位
}
numd = carry; //最低位块的余数即为整个除法的余数 //计算最后和的块的总数
while(!numc[resultsize]) --resultsize;
//输出商
printf("%d", numc[resultsize]);
for(i = resultsize-1; i >= 0; --i)
{
printf("%04d", numc[i]); //右对齐,补零输出;
}
//输出余数
printf("\n%d\n", numd);
return 0;
}
  • 高精度除以高精度

    以176342876 / 3453452为例:
被除数 176342876
- (51 x 除数) 51 x 3453452
余数 216824
51

C语言代码实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 200 //整数乘幂运算函数
int Pow(int a, int b)
{
int i = 0, result = 1;
for(i = 0; i < b; ++i)
{
result *= a;
}
return result;
} //High Precision Of division
//(2)高精度除以高精度
int main()
{
char stra[N], strb[N]; //字符串数组,以字符形式储存两个大数;
int i = 0, step = 4, borrow = 0; //step表示块长,borrow为进位位;
int lengtha, lengthb, tmpnum, numbsize, numcsize, numdsize, maxsize, mark; //maxlength表示stra和strb二者长度较大的那个;
int numa[N], numb[N], numc[N], numd[N]; //依次储存被除数数,除数数,商,余数;
memset(stra, 0, sizeof(stra));
memset(strb, 0, sizeof(strb));
memset(numa, 0, sizeof(numa));
memset(numb, 0, sizeof(numb));
memset(numc, 0, sizeof(numc));
memset(numd, 0, sizeof(numd)); //初始化为零;
scanf("%s%s", stra, strb);
lengtha = strlen(stra);
lengthb = strlen(strb); //计算两个大数的长度 //字符数字转为四位一块的整数数字
for(i = lengtha-1; i >= 0; --i)
{
numa[(lengtha-1-i)/step] += (stra[i]-'0')*Pow(10,(lengtha-1-i)%step);
}
for(i = lengthb-1; i >= 0; --i)
{
numb[(lengthb-1-i)/step] += (strb[i]-'0')*Pow(10,(lengthb-1-i)%step);
}
memcpy(numd, numa, sizeof(numa));
numbsize = (lengthb-1)/step;
numcsize = 0;
numdsize = (lengtha-1)/step; do
{
maxsize = numdsize > numbsize ? numdsize : numbsize;
//计算剩余数是否小于除数
mark = 1;
for(i = maxsize; i >= 0; --i)
{
if(numd[i] > numb[i])
{
mark = 1;
break;
}
else if(numd[i] < numb[i])
{
mark = -1;
break;
}
} //判断是否余数已经小于除数
if(!(mark+1)) break; borrow = 0; //借位置零;
//逐块相减,并借位
for(i = 0; i <= maxsize; ++i)
{
tmpnum = (numd[i] - numb[i] + Pow(10, step) + borrow)%Pow(10,step); //计算差
borrow = (numd[i] - numb[i] + Pow(10, step) + borrow)/Pow(10,step) - 1; //计算借位
numd[i] = tmpnum;
}
while(!numd[numdsize]) --numdsize; //每减一个除数,商加一;
borrow = 1;
for(i = 0; i <= numcsize; ++i)
{
numc[i] += borrow;
borrow = numc[i]/Pow(10,step);
numc[i] %= Pow(10,step);
}
if(borrow)
{
++numcsize;
numc[i] += borrow;
}
}while(1); printf("%d", numc[numcsize]);
for(i = numcsize-1; i >= 0; --i)
{
printf("%04d", numc[i]); //右对齐,补零输出;
}
printf("\n");
printf("%d", numd[numdsize]);
for(i = numdsize-1; i >= 0; --i)
{
printf("%04d", numd[i]); //右对齐,补零输出;
}
printf("\n");
return 0;
}

高精度算法(C/C++)的更多相关文章

  1. c++减法高精度算法

    c++高精度算法,对于新手来说还是一大挑战,只要克服它,你就开启了编程的新篇章,算法. 我发的这个代码并不是很好,占用内存很多而且运行时间很长(不超过0.02秒),但是很好理解,很适合新手 高精算法的 ...

  2. c++加法高精度算法

    c++高精度算法,对于新手来说还是一大挑战,只要克服它,你就开启了编程的新篇章,算法. 我发的这个代码并不是很好,占用内存很多而且运行时间很长(不超过1秒),但是很好理解,很适合新手 高精算法的本质就 ...

  3. 转载:C++之高精度算法

    C++之高精度算法 注意:本文转载自http://blog.sina.com.cn/s/blog_4fdb102b010087ng.html,十分感谢原作者:忍者    前言:由于计算机运算是有模运算 ...

  4. #AcWing系列课程Level-2笔记——5.高精度“+”算法

    高精度"+"算法 编写高精度"+",记住下面的过程,代码也就游刃有余了! 1.首先我们要明白大整数是如何存储的? 2.其次存储完,如何运算? 高精度" ...

  5. LeetCode43,一题让你学会高精度算法

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode系列第22篇文章,今天讲的内容是高精度算法. 今天和大家讨论的算法是高精度,对应的LeetCode是第43题.题面其实 ...

  6. 【蓝桥杯C组】备赛基础篇之高精度算法

    一.高精度加法 思路: 运用vector数组(c选手可用len来记录数组长度,数组去保存数字)将存入字符串里面的数字符倒叙保存,按照小学的加法列式,相加保存进位即可.具体参考代码. 详细代码解析: # ...

  7. 【洛谷】P1009 阶乘之和——高精度算法

    题目描述 用高精度计算出S = 1! + 2! + 3! + - + n!  ( n ≤  50 ) S = 1! + 2! + 3! + - + n! ( n ≤ 50 ) 其中"!&qu ...

  8. [SinGuLaRiTy] 高精度算法代码库

    [SinGuLaRiTy-1001] Copyrights (c) SinGuLaRiTy 2017. All Rights Reserved. 加法: #include<stdio.h> ...

  9. 证明最大公约数Stein算法(高精度算法)

    E:even 奇数  O:odd 偶数 若(a,b)为(e,e),则gcd(a,b)=2*gcd(a/2,b/2) 若(a,b)为(e,o),则gcd(a,b)=gcd(a/2,b) 若(a,b)为( ...

随机推荐

  1. dotnet core 用值初始化整个数组

    如果想要创建一个数组,在这个数组初始化一个值,有多少不同的方法? 本文告诉大家三个不同的方法初始化 在开发中,会不会用很多的时间在写下面的代码 var prime = new bool[1000]; ...

  2. Openstack生产环境部署(一)

  3. c# 写个简单的爬虫。注:就一个方法,没有注释,自己猜~哈哈

    和我,在成都的街头走一走,哦~喔~哦~ public JsonResult GetHtml() { string url = "http://www.xxxxxxxxxxxxxxxxxx.c ...

  4. Java 工程师应该掌握的知识

    以 Java 工程师应该掌握的知识为例,按重要程度排出六个梯度: 第一梯度:计算机组成原理.数据结构和算法.网络通信原理.操作系统原理. 第二梯度:Java 基础.JVM 内存模型和 GC 算法.JV ...

  5. 在Linux中执行.sh脚本,异常/bin/sh^M

    在Linux中执行.sh脚本,异常/bin/sh^M 在linux中执行.sh脚本,异常/bin/sh^M: bad interpreter: No such file or directory. 分 ...

  6. javascript 闭包的理解(一)

    过很多谈如何理解闭包的方法,但大多数文章,都是照抄或者解释<Javascript高级程序设计(第三版)>对于闭包的讲解,甚至例程都不约而同的引用高程三181页‘闭包与变量’一节的那个“返回 ...

  7. Mysql(超级详细)

    Mysql(超级详细) (黑小子-余) 一.Mysql介绍 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理 ...

  8. 从零开始のcocos2dx生活(三)Scheduler

    文章目录 取模 Timer() 变量 设置定时器Timer() 一些成员函数 Scheduler() 变量 初始化 哈希表 构造函数schedule() 开启定时器Update() 析构函数~Upda ...

  9. HTML和JavaScript代码分离、平稳退化(1)

    使用的编辑器是Hbuilder,浏览器是Chrome. HTML和JavaScript代码分离,会使得修改网页功能和代码的阅读与维护会轻松的许多,不用在DOM中阅读大量的JavaScript代码. 文 ...

  10. 洛谷$P3227\ [HNOI2013]$切糕 网络流

    正解:网络流 解题报告: 传送门! 日常看不懂题系列,,,$QAQ$ 所以先放下题目大意趴$QwQ$,就说有个$p\cdot q$的矩阵,每个位置可以填一个$[1,R]$范围内的整数$a_{i,j}$ ...