高精度算法 (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. vue移动端图片上传压缩

    上传压缩方法 import {api} from '../../api/api.js'; import axios from 'axios'; export function imgPreview ( ...

  2. dotnet 通过 HttpClient 下载文件同时报告进度的方法

    本文告诉大家一个简单的方法通过 HttpClient 下载文件,同时报告下载进度 通过 HttpClient 的 ContentLength 很多时候都可以拿到下载的内容的长度,通过 ReadAsyn ...

  3. 微信小程序样式wxss各种问题总结(不断更新)

    1)加入其它样式文件 @import '/css/dialog.wxss'; //注意:必须结尾用分号 2)隐藏滚动条 ::-webkit-scrollbar { width:; height:; c ...

  4. python中交换两个变量值的方法

    a = 4b = 5 #第1种c = 0c = aa = bb = c #第2种a = a+bb = a-ba = a-b #第3种a,b = b,a print("a=%d,b=%d&qu ...

  5. Kubernetes基本概念和术语之《Pod》

    Pod是Kubernetes的最重要也最基本的概念.我们看到每个Pod都有一个特殊的被称为“根容器”的Pause容器对应的镜像属于Kubernetes平台的一部分.除了Pause容器,每个Pod还包含 ...

  6. 017 Ceph的集群管理_3

    一.验证OSD 1.1 osd状态 运行状态有:up,in,out,down 正常状态的OSD为up且in 当OSD故障时,守护进程offline,在5分钟内,集群仍会将其标记为up和in,这是为了防 ...

  7. redis 本地安装

    1.redis介绍 Redis是有名的NoSql数据库,一般Linux都会默认支持.但在Windows环境中,可能需要手动安装设置才能有效使用.简单介绍一下Windows下Redis服务的安装方法. ...

  8. status100到500http响应对应状态解释

    1xx-信息提示 这些状态代码表示临时的响应.客户端在收到常规响应之前,应准备接收一个或多个1xx响应. 100-继续. 101-切换协议. 2xx-成功 这类状态代码表明服务器成功地接受了客户端请求 ...

  9. 【Python3爬虫】反反爬之解决前端反调试问题

    一.前言 在我们爬取某些网站的时候,会想要打开 DevTools 查看元素或者抓包分析,但按下 F12 的时候,却出现了下面这一幕: 此时网页暂停加载,也就没法运行代码了,直接中断掉了,难道这就能阻止 ...

  10. HDU3555 Bomb 题解 数位DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 题目大意:求 \([1,n]\) 范围内有多少数包含"49". 解题思路: ...