高精度算法(C/C++)
高精度算法 (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++)的更多相关文章
- c++减法高精度算法
c++高精度算法,对于新手来说还是一大挑战,只要克服它,你就开启了编程的新篇章,算法. 我发的这个代码并不是很好,占用内存很多而且运行时间很长(不超过0.02秒),但是很好理解,很适合新手 高精算法的 ...
- c++加法高精度算法
c++高精度算法,对于新手来说还是一大挑战,只要克服它,你就开启了编程的新篇章,算法. 我发的这个代码并不是很好,占用内存很多而且运行时间很长(不超过1秒),但是很好理解,很适合新手 高精算法的本质就 ...
- 转载:C++之高精度算法
C++之高精度算法 注意:本文转载自http://blog.sina.com.cn/s/blog_4fdb102b010087ng.html,十分感谢原作者:忍者 前言:由于计算机运算是有模运算 ...
- #AcWing系列课程Level-2笔记——5.高精度“+”算法
高精度"+"算法 编写高精度"+",记住下面的过程,代码也就游刃有余了! 1.首先我们要明白大整数是如何存储的? 2.其次存储完,如何运算? 高精度" ...
- LeetCode43,一题让你学会高精度算法
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode系列第22篇文章,今天讲的内容是高精度算法. 今天和大家讨论的算法是高精度,对应的LeetCode是第43题.题面其实 ...
- 【蓝桥杯C组】备赛基础篇之高精度算法
一.高精度加法 思路: 运用vector数组(c选手可用len来记录数组长度,数组去保存数字)将存入字符串里面的数字符倒叙保存,按照小学的加法列式,相加保存进位即可.具体参考代码. 详细代码解析: # ...
- 【洛谷】P1009 阶乘之和——高精度算法
题目描述 用高精度计算出S = 1! + 2! + 3! + - + n! ( n ≤ 50 ) S = 1! + 2! + 3! + - + n! ( n ≤ 50 ) 其中"!&qu ...
- [SinGuLaRiTy] 高精度算法代码库
[SinGuLaRiTy-1001] Copyrights (c) SinGuLaRiTy 2017. All Rights Reserved. 加法: #include<stdio.h> ...
- 证明最大公约数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)为( ...
随机推荐
- Linux 内核VLB 总线
另一个对 ISA 的扩展是 VESA Local Bus(VLB) 接口总线, 它扩展了 ISA 连接器, 通过 添加第 3 个知道长度的槽位. 一个设备可只插入这个额外的连接器(不用插入 2 个关联 ...
- Microsoft Ignite The Tour Beijing 记录: Learn Connect Explore
坦率的说,这是我第一次以讲师的身份参加微软的Ignite大会.同时我也很开心能作为微软社区MVP来参加这个活动.而我的演讲主题也和我的社区有关——Unity.C#以及跨平台开发. 这篇用来记录MSIg ...
- 牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树)
牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树) 链接:https://ac.nowcoder.com/acm/problem/15706 现在需要您来帮忙维护这个名册, ...
- UE4 中的 C++ 与 蓝图交互
1.Unreal 引擎提供了两种创建新 Gameplay 元素的方法:C++ 和 蓝图视觉脚本. 通过 C++,程序员构建基础游戏系统:设计师可以基于此系统为场景 / 游戏创建自定义的游戏玩法. 这种 ...
- 【Docker Compose】简介与安装
1.简介 Compose 是一个用户定义和运行多个容器的 Docker 应用程序.在 Compose 中你可以使用 YAML 文件来配置你的应用服务.然后,只需要一个简单的命令,就可以创建并启动你配置 ...
- 浮点数NaN和INF(#IND, #INF)
NaN&INF定义在一些情况会出现无效的浮点数,例如除0,例如负数求平方根等,像这类情况,获取到的浮点数的值是无效的. NaN 即 Not a Number 非数字 INF ...
- 第二阶段:2.商业需求分析及BRD:4.产品需求分析总结
产品的需求筛选 战略定位要考虑公司的战略问题.产品定位要分阶段,各个阶段的需求不同. 其实现在需求分析跟筛选都是非常快的. 不把需要当成需求,意思就是不要用户说需要什么就是什么,用户需要引导. 先分类 ...
- 20191024-2 Alpha阶段贡献分配
此作要求参见: https://edu.cnblogs.com/campus/nenu/2019fall/homework/9858 要求1 每位组员的贡献分值 罗杨美慧:9 徐丽君:11 魏鑫:12 ...
- FRPC 双向socket通讯 转发请求类轮子
一直在找一个能双向通讯的C#库 学识浅薄没有找到 于是封装一个预计BUG奇多的轮子 他是基于SuperSocket开发的 这样的 它跟传统的 架构不一样 它的最小架构 或者 客户端即是服务端 比如一个 ...
- 1048 数字加密 (20 分)C语言
本题要求实现一种数字加密方法.首先固定一个加密用正整数 A,对任一正整数 B,将其每 1 位数字与 A 的对应位置上的数字进行以下运算:对奇数位,对应位的数字相加后对 13 取余--这里用 J 代表 ...