当前位置: 首页 > news >正文

高精度算法--四则运算模板(附例题)

高精度加法

A + B    适用范围:A和B的位数在10^6以内(注意是位数,不是数字大小),A、B均为非负数

模版中用到的函数:

size()函数:获取容器(字符串,vector数组)中的元素数量,返回容器中元素的个数,类型为size_t .

size_t 是无符号的,不能表示负数,在写循环时需要注意 i 的类型.

push_back()函数: 在容器末尾添加元素.

#include<iostream>
#include<vector>
using namespace std;// 大整数相加函数,A和B为逆序存储的数字数组(低位在前,高位在后)
vector<int> add(vector<int> &A, vector<int> &B) 
{vector<int> C; // 存储结果的数组int t = 0; // 当前位的累加值(包含进位)for (size_t i = 0; i < A.size() || i < B.size() || t; i++) {// 将当前位的两个数字相加,并加上进位if (i < A.size()) t += A[i];if (i < B.size()) t += B[i];C.push_back(t % 10); // 取个位作为当前位的结果t /= 10;             // 计算进位,用于下一位的累加}return C;
}int main() 
{string a, b;cin >> a >> b; // 输入两个大整数字符串// 将字符串逆序转换为数字数组,方便从低位开始相加vector<int> A, B;for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0'); // 字符转数字,逆序存储for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');auto C = add(A, B); // 结果数组为逆序存储,逆序输出即为正确的高位在前顺序for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);return 0;
}

高精度减法

A - B    适用范围:A和B的位数在10^6以内,A、B均为非负数

模版中用到的函数:

back()函数:返回容器vector的最后一个元素.

pop_back()函数:移除容器末尾的元素.

注意:减法运算先比较两数大小,模拟运算时需要处理低位向高位借一

#include<iostream>
#include<vector>
using namespace std;/* 比较两个大整数的大小
A > B 时返回true,否则返回false
比较规则:1.位数不同时位数多的大 2.位数相同时从高位开始逐位比较 */
bool cmp(vector<int> &A, vector<int> &B)
{if(A.size() != B.size()) return A.size() > B.size(); // 位数不同直接比较位数	// 位数相同则从高位向低位逐位比较for(int i = A.size() - 1; i >= 0; i--)if(A[i] != B[i])return A[i] > B[i]; // 高位数字不同时比较当前位return true; // 所有位都相等时视为A>=B
}// 减法函数(A>=B)
vector<int> sub(vector<int> &A, vector<int> &B) 
{vector<int> C;       int t = 0;    // 借位标志(0无借位,1有借位)for (size_t i = 0; i < A.size(); i++) {t = A[i] - t;   // 减去上一位的借位if(i < B.size()) t -= B[i];  // 对应位相减// 处理借位:+10确保取到正数,%10取个位数C.push_back((t + 10) % 10);  // 更新借位状态:t<0说明需要借位if(t < 0) t = 1;  else t = 0;}// 去除前导零(保留至少1位)while(C.size() > 1 && C.back() == 0) C.pop_back();return C;
}int main() 
{string a, b;cin >> a >> b;  vector<int> A, B;for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');  for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');if(cmp(A, B)){  // A>=Bauto C = sub(A, B); for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);}else{  // A < B,计算B-A并添加负号auto C = sub(B, A); printf("-");  for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);}return 0;
}

高精度乘法

A * B    适用范围:A的位数在10^6以内,B的值在10000以内(注意),A、B均为非负数.

如果A、B都是大数该如何解决呢?见例题1.

乘法模版相对简单易记

#include<iostream>
#include<vector>
using namespace std;
vector<int> mul(vector<int> &A, int b) 
{vector<int> C; int t = 0;       // 进位值(包含当前位的累计值)for (size_t i = 0; i < A.size() || t; i++) {// 当前位乘法计算(如果还有未处理的位)if(i < A.size()) t += A[i] * b;  // 核心计算:当前位×b + 进位C.push_back(t % 10);  // 取个位作为当前结果t /= 10;          }// 处理前导零while(C.size() > 1 && C.back() == 0) C.pop_back();return C;
}int main() 
{string a;int b;cin >> a >> b;vector<int> A;for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0'); auto C = mul(A, b);for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);return 0;
}

高精度除法

A / B    适用范围:A的位数在10^6以内,B的值在10000以内(注意),A、B均为非负数.

商C,余数r(通过引用返回)

存储顺序的特殊处理说明:
        在加减乘运算中,计算均从低位(个位)开始,结果自然以逆序存储在数组中(如结果123存储为[3,2,1]),主函数通过逆序输出即可得到正确的高位在前顺序。

        但除法运算需从高位开始逐位计算,导致商值的每一位按正序生成(如商500初始存储为[5,0,0])。为保持与其他运算一致的存储规范,需对结果进行反转,使其转换为逆序存储([5,0,0] → [0,0,5])。此时主函数沿用统一的逆序输出逻辑,即可正确展示高位在前的最终结果(输出500)。

模版中用到的函数:

reverse()函数:反转容器(如 vector、string、数组等)中元素的顺序.

头文件需包含 <algorithm>.

begin()函数:返回指向容器第一个元素.

end()函数:返回指向容器最后一个元素下一个位置.

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> div(vector<int> &A, int b, int &r) 
{vector<int> C; r = 0;       // 初始化余数为0// 从高位到低位逐位处理(原数字的最高位对应A的最后一个元素)for (int i = A.size() - 1; i >= 0; i--) {r = r * 10 + A[i];      // 将余数进位并加上当前位C.push_back(r / b);     // 计算当前位的商r %= b;                 // 更新余数}// 此时商数组是正序存储(高位在前),需要反转得到逆序存储reverse(C.begin(), C.end());// 去除前导零while(C.size() > 1 && C.back() == 0) C.pop_back();return C;
}int main() 
{string a;int b;   cin >> a >> b;vector<int> A;for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');  int r;          // 存储余数auto C = div(A, b, r); // 逆序输出商(高位在前)for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);cout << endl << r << endl;  // 输出余数return 0;
}

自此,四则运算模版已解决,下面带入到具体题目中使用.

例题1 (洛谷P1303 A*B Problem)

题目背景

高精度乘法模板题。

题目描述

给出两个非负整数,求它们的乘积。

输入格式

输入共两行,每行一个非负整数。

输出格式

输出一个非负整数表示乘积。

说明/提示

每个非负整数不超过 10^2000。

分析:用已有模版A * b (大数乘小数)模拟A * B(大数乘大数),可以将B进行拆解,每次只用A乘B的一位,再将每次得到的结果处理后相加即可

输入:

A = 123(逆序 [3,2,1])

B = 45(逆序 [5,4])

循环过程:

i=0(处理 B 的个位 5)

    D = 123×5 = 615 → 逆序 [5,1,6]

    移位 i=0 → 保持 D=[5,1,6]

    C = [5,1,6](值 615)

i=1(处理 B 的十位 4)

    D = 123×4 = 492 → 逆序 [2,9,4]

    移位 i=1 → 插入 1 个零 → [0,2,9,4](值 4920)

累加后 C = [5,1,6] + [0,2,9,4] = [5,3,5,5](值 615+4920=5535)

最终输出:

C 逆序输出 5535

#include<iostream>
#include<vector>
using namespace std;
vector<int> mul(vector<int> &A, int b)  //高精度乘法
{vector<int> C;int t = 0;for(size_t i = 0; i < A.size() || t; i++){if(i < A.size()) t += A[i] * b;C.push_back(t % 10);t /= 10;}return C;
}
vector<int> add(vector<int> &A, vector<int> &B)  //高精度加法
{vector<int> C;int t = 0;for(size_t i = 0; i < A.size() || i < B.size(); i++){if(i < A.size()) t += A[i];if(i < B.size()) t += B[i];C.push_back(t % 10);t /= 10;}if(t) C.push_back(1);return C;
}
vector<int> solve(vector<int> &A, vector<int> &B)
{vector<int> C;//遍历 B 的每一位(从低位到高位)for(size_t i = 0; i < B.size(); i++){//计算A与当前位的乘积vector<int> D = mul(A, B[i]);//移位处理:在结果后添加i个0//因为存储是逆序的,所以先添加尾部的0,再添加逆序的数字vector<int> temp(i, 0); // 创建 i 个前导零(逆序存储时零在低位)temp.insert(temp.end(), D.begin(), D.end()); // 拼接乘积结果D = temp;  // 完成移位,相当于 D × 10^iC = add(C, D);}//逆序存储,从结果尾部删除前导0while(C.size() > 1 && C.back() == 0) C.pop_back(); return C;
}
int main()
{string a, b;vector<int> A, B;cin >> a >> b;for(int i = a.size() - 1; i >= 0; i--)A.push_back(a[i] - '0');for(int i = b.size() - 1; i >= 0; i--)B.push_back(b[i] - '0');auto C = solve(A, B);for(int i = C.size() - 1; i >= 0; i--)printf("%d", C[i]);return 0;
}

例题2(P1009 阶乘之和)

题目描述
用高精度计算出 S=1!+2!+3!+⋯+n!(n≤50)。

其中 ! 表示阶乘,定义为 n!=n×(n−1)×(n−2)×⋯×1。例如,5!=5×4×3×2×1=120。

输入格式
一个正整数 n。

输出格式
一个正整数 S,表示计算结果。

分析:Sn = a1+ a2 + a3 + a4 + ... + an,其中an = a(n-1) * n,直接套用乘法与加法模版

#include<iostream>
#include<vector>
using namespace std;
vector<int> add(vector<int> &A, vector<int> &B)
{vector<int> C;int t = 0;for(size_t i = 0; i < A.size() || i < B.size() || t; i++){if(i < A.size()) t += A[i];if(i < B.size()) t += B[i];C.push_back(t % 10);t /= 10;}return C;
}
vector<int> mul(vector<int> &A, int b)
{vector<int> C;int t = 0;for(size_t i = 0; i < A.size() || t; i++){if(i < A.size()) t += A[i] * b;C.push_back(t % 10);t /= 10;}while(C.size() > 1 && C.back() == 0) C.pop_back();return C;
}
int main()
{int n;cin >> n;vector<int> S, A;  //S存储累加和,A存储当前阶乘值S.push_back(0);  //初始化和S=0A.push_back(1);  //初始化阶乘A=1for(int i = 1; i <= n; i++){A = mul(A, i);  //计算A = i!S = add(S, A);  //将当前阶乘值i!累加到总和S}for(int i = S.size() - 1; i >= 0; i--)printf("%d", S[i]);return 0;
}

例题3(P1045 麦森数)

题目描述
形如 2^P−1 的素数称为麦森数,这时 P 一定也是个素数。但反过来不一定,即如果 P 是个素数,2^P−1 不一定也是素数。到 1998 年底,人们已找到了 37 个麦森数。最大的一个是 P=3021377,它有 909526 位。麦森数有许多重要应用,它与完全数密切相关。

任务:输入 P(1000<P<3100000),计算 2^P−1 的位数和最后 500 位数字(用十进制高精度数表示)

输入格式
文件中只包含一个整数 P(1000<P<3100000)

输出格式
第一行:十进制高精度数 2^P−1 的位数。

第 2∼11 行:十进制高精度数 2^P−1 的最后 500 位数字。(每行输出 50 位,共输出 10 行,不足 500 位时高位补 0)

不必验证 2^P−1 与 P 是否为素数。

输入输出样例

输入:
1279

输出:
386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087

分析

计算数 N 的位数 D:D=⌊log⁡10N⌋+1

1000<P<3100000,很明显我们不能直接计算2^p-1,数值巨大,题中提示我们只需计算最后五百位数字

#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
vector<int> mul(vector<int> &A, int b)
{vector<int> C;int t = 0;for(size_t i = 0; i < A.size() || t; i++){if(i < A.size()) t += A[i] * b;C.push_back(t % 10);t /= 10;if(C.size() > 500) break;  //超过500位结束}while(C.back() == 0 && C.size() > 1) C.pop_back();return C;
}
vector<int> sub(vector<int> &A, int b)
{int t = 0;for(size_t i = 0; i < A.size(); i++){t = A[i] - t;if(i < 1) t -= b;A[i] = (t + 10) % 10;if(t < 0) t = 1;else break;  //借位处理完成后提前退出}while(A.back() == 0 && A.size() > 1) A.pop_back();return A;
}int main()
{long long p;cin >> p;long long n = p * log10(2) + 1;cout << n << endl;vector<int> A(1, 1);  //初始化为1for(;p > 13; p = p - 13){A = mul(A, 8192); //每次乘2^13,减少循环次数}A = mul(A, pow(2, p));//计算2^p - 1A = sub(A, 1);// 输出低500位(不足补0)for(int i = 499; i >= 0; i--) {if(static_cast<size_t>(i) < A.size()) cout << A[i];else cout << 0;if(i % 50 == 0) cout << endl;}return 0;
}

相关文章:

  • 心理咨询技能竞赛流程方案
  • day 47
  • 知识改变命运?如何有规划的学好计算机专业?
  • Codeforces Round 509 (Div. 2) C. Coffee Break
  • 【AI论文】反思、重试、奖励:通过强化学习实现大型语言模型的自我提升
  • Go 标准库 encoding/gob 快速上手
  • Python项目中添加环境配置文件
  • 【Elasticsearch】映射:Join 类型、Flattened 类型、多表关联设计
  • Python 函数全攻略:函数进阶(生成器、闭包、内置函数、装饰器、推导式)
  • 柯尼卡美能达Konica Minolta bizhub 205i打印机信息
  • 《机器学习》(周志华)第一章 绪论
  • 使用Python和Scikit-Learn实现机器学习模型调优
  • 机器学习-经典分类模型
  • 计算机视觉——相机标定
  • 深入解析HarmonyOS5 UIAbility组件:从核心架构到实战应用
  • CRMEB 中 PHP 快递查询扩展实现:涵盖一号通、阿里云、腾讯云
  • pcie 日常问答0604
  • Spring Cloud 多机部署与负载均衡实战详解
  • 【storage】
  • Android开发 系统签名jks制作和问题汇总
  • 网站开发检测用户微信号/推广app有哪些
  • 济南正规网站建设公司哪家好/百度推广登陆平台
  • 校园网站建设服务/品牌推广和营销推广
  • 做网站最好的软件是/软文广告是什么意思
  • 用html做企业门户网站/全网营销公司排名前十
  • 正邦设计面试/南宁求介绍seo软件