首页 > 编程语言 >信息学集训 | 17 高精度算法理论与实现2

信息学集训 | 17 高精度算法理论与实现2

时间:2022-11-14 23:31:20浏览次数:45  
标签:信息学 17 h2 MAX HP h1 len data 集训



戳一戳!和我一起走进信息学的世界

导读

信息学能够有助于孩子未来工作发展,提升孩子的综合能力。


这一节课是我们这一期课程的最后一节课,我们继续学习高精度算法,回顾如何高精度算法的定义,输出及加减法运算,并学习如何实现高精度的乘除法。


​ ​ ​ ​ ​


1 回顾

大家还记得我们前面使用结构体实现了高精度加法和高精度减法,我们先来回顾一下吧!


高精度算法就是当我们不能再用普通的数据类型来存放整数时,我们就要考虑用其他方式存放,并实现数据的运算。


当然,我们也可以把整数推广到小数,要注意的就是我们要用一位来存放小数点。高精度算法的实现方式不唯一。例如我们实现高精度,就是用结构体,然后数据是个位对齐:


信息学集训 | 17 高精度算法理论与实现2_c++


我们就可以按照如下方式定义高精度结构体:


#include<iostream>
using namespace std;
#define MAX 100
struct HP {
int data[MAX] = {};
int len;
};
HP str2Arr(string s){
HP h;
int i = 0;
while(s[i] == '0' && i<s.size()-1) i++;
s = s.substr(i);
h.len = s.size();
for(int i = 0;i<h.len;i++) h.data[MAX-i-1] = s[h.len-i-1] - '0';

return h;
}
void print(HP h){
for(int i = MAX-h.len;i<MAX;i++) cout<<h.data[i];
cout<<endl;
}

int main(){
string s1 = "00045926",s2 = "123456";
HP h1 = str2Arr(s1),h2 = str2Arr(s2);
print(h1);
print(h2);
return 0;
}


有了高精度结构体,我们就可以实现高精度的加法和高精度的减法了。


//高精度加法
HP add(HP h1, HP h2){
HP h;
int jw = 0,t, max;
if(h1.len>h2.len) max = h1.len;
else max = h2.len;
for(int i = MAX-1;i>=MAX-1-max;i--){
t = h1.data[i]+h2.data[i]+jw;
h.data[i] = t%10;
jw = t/10;
}
if(h.data[MAX-1-max]) h.len = max+1;
else h.len = max;
return h;
}

//高精度减法
HP sub(HP h1, HP h2){
HP h;
int jw = 0,t, max;
max = h1.len;
for(int i = MAX-1;i>=MAX-max;i--){
t = h1.data[i]-h2.data[i]-jw;
if(t<0){
h.data[i] = t+10;
jw = 1;
}
else{
h.data[i] = t;
jw = 0;
}
}
if(h.data[MAX-max]) h.len = max;
else h.len = max-1;
return h;
}


对于加减法,我们都考虑了最简单的情况,上节课的题目中,我们提到,如果我们不确定被减数和减数谁更大的时候,就要分情况讨论了



如果被减数≥减数,最高位存0,输出原数
如果被减数<减数,最高位存1,在前面输出﹣号,然后再输出减数-被减数。


所以我们首先要写一个函数判断两个数谁更大:


如果位数不同,位数多的大;
如果位数相同,从第一位往后比,某一位大的,这个数更大。


下面的函数,如果被减数大,返回true,否则返回false:


bool Max(HP h1, HP h2){
int l1 = h1.len,l2 = h2.len;
if(l1>l2) return true;
if(l1==l2){
while(l1>0&&l2>0&&h1.data[MAX-l1] == h2.data[MAX-l2]){
l1--;
l2--;
}
return h1.data[MAX-l1] >= h2.data[MAX-l2];
}
return false;
}


然后我们就可以重写减法了,如果被减数大,那就还是按照之前的做法,如果减数大,那就用减数减去被减数,然后最高位存负号:


HP sub(HP h1, HP h2){
HP h;
int jw = 0,t, max;
if(Max(h1,h2)) {
max = h1.len;
for(int i = MAX-1;i>=MAX-max;i--){
t = h1.data[i]-h2.data[i]-jw;
if(t<0){
h.data[i] = t+10;
jw = 1;
}
else{
h.data[i] = t;
jw = 0;
}
}
if(h.data[MAX-max]) h.len = max;
else h.len = max-1;
}
else{
h.data[0] = 1;
max = h2.len;
for(int i = MAX-1;i>=MAX-max;i--){
t = h2.data[i]-h1.data[i]-jw;
if(t<0){
h.data[i] = t+10;
jw = 1;
}
else{
h.data[i] = t;
jw = 0;
}
}
if(h.data[MAX-max]) h.len = max;
else h.len = max-1;

}
return h;
}


我们还需要重写一下输出函数,因为这个时候,最高位表示符号了,我们需要判断一下最高位是否为0,如果不为0,说明该数是负数,要输出负号。代码如下:


void print(HP h){
if(h.data[0]) cout<<"-";
for(int i = MAX-h.len;i<MAX;i++) cout<<h.data[i];
cout<<endl;
}


举个栗子:


int main(){
string s1 = "00045926",s2 = "123456";
HP h1 = str2Arr(s1),h2 = str2Arr(s2);
print(h1);
print(h2);

HP h = sub(h1, h2);
print(h);
return 0;
}


执行结果如下:


信息学集训 | 17 高精度算法理论与实现2_i++_02


接下来,就让我们走进乘除法的世界吧。

2 高精度乘除法

高精度乘除法要考虑的,跟我们小学学习的做法是一样的。让我们直接走进乘除法吧。

1 高精度乘法

首先我们先来看高精度的加法。高精度的加法思想就是我们小学数学加法的思想,我们以351×18为例说明:


信息学集训 | 17 高精度算法理论与实现2_c++_03


这种做法就是只要有进位,我们就会进位。在具体实现过程中,如果我们频繁进位,算法复杂度相对较高,我们可以先统一计算,最后统一进位:


信息学集训 | 17 高精度算法理论与实现2_i++_04


然后我们需要考虑,a的第i位和b的第j位相乘,得到的是c的第i+j-1位。即:


c[i+j-1] += a[i]*b[j];


这个时候的i和j是从后往前的。真正在数组中存放的时候,个位存放在索引为MAX-1上,十位存放在MAX-2上。这部分代码如下:


for(int i = 1;i<=h1.len;i++){
for(int j = 1;j<=h2.len;j++){
h.data[MAX-i-j+1] += h1.data[MAX-i]*h2.data[MAX-j];
}
}


我们得到上面的数据之后,就要开始进位了,一个a位数和一个b位数相乘,如果最高位没有进位,乘积就是a+b-1位,如果最高位有进位,乘积就是a+b位。


我们默认最高位有进位,一直计算到最高位,然后再判断最高位是否为0,如果为0,则乘积就是a+b-1位,如果最高位不是0,乘积就是a+b位。代码如下:


for(int i = 1;i<h1.len+h2.len;i++){
h.data[MAX-i-1] += h.data[MAX-i]/10;
h.data[MAX-i] %= 10;
}

if(h.data[MAX-h1.len-h2.len]) h.len = h1.len+h2.len;
else h.len = h1.len+h2.len-1;


完整代码如下:




HP mul(HP h1, HP h2){
HP h;

for(int i = 1;i<=h1.len;i++){
for(int j = 1;j<=h2.len;j++){
h.data[MAX-i-j+1] += h1.data[MAX-i]*h2.data[MAX-j];
}
}

for(int i = 1;i<h1.len+h2.len;i++){
h.data[MAX-i-1] += h.data[MAX-i]/10;
h.data[MAX-i] %= 10;
}

if(h.data[MAX-h1.len-h2.len]) h.len = h1.len+h2.len;
else h.len = h1.len+h2.len-1;
return h;
}


我们写如下主函数:


int main(){
string s1 = "000999",s2 = "0000999";
HP h1 = str2Arr(s1),h2 = str2Arr(s2);
print(h1);
print(h2);

HP h = mul(h1, h2);
print(h);
return 0;
}


执行结果为:


信息学集训 | 17 高精度算法理论与实现2_c++_05


如果是最高位不进位,结果如下:


信息学集训 | 17 高精度算法理论与实现2_i++_06


2 高精度除法

高精度除法一般我们考虑除数是非高精度数,即我们可以使用int类型进行存储。然后我们做高精度除法,不仅要得到商,还要得到余数。


例如我们计算  。我们要先找到商的最高位,我们发现3,36和366都比999小,所以最高位应该是 


这个过程就是说,我们要先从最高位开始找起,直到找到的部分比除数大。当然,也存在被除数本身就比除数小,那么我们找到最后一个,也不会使得找的部分大于除数。所以我们需要两个判断条件,一个是找到的部分比除数小,另一个是找的位数不为0(最极端就是找到位数为1,也就是个位)。这部分代码如下:


int l = h1.len, t = 0;
while(t<b&&l){
t = t*10 + h1.data[MAX-l];
l--;
}


这个时候,如果l为0,那就说明被除数的位数已经找完,这个时候,商的位数为1位。如果l不为0,因为l多减了一次,l比商的位数小1。所以商的位数为l+1。商的最高位是目前的部分除以除数。


h2.len = l+1;
h2.data[MAX-l-1] = t/b;


然后,如果我们还没有计算到被除数的个位,也就是说l还不为0,那我们就要继续运算。每次运算,都要用上次计算的余数加上后面的一位数(这个加是附加,也就是余数*10+后一位数),然后商的对应位就是得到的数除以除数:


while(l){
t = t%b*10+h1.data[MAX-l];
h2.data[MAX-l] = t/b;
l--;
}


当l为0的时候,就说明我们已经运算到最后一位了,我们就可以求余数了:


r = t%b;


完整代码如下:


#include<iostream>
using namespace std;
#define MAX 100
struct HP {
int data[MAX] = {};
int len;
};
HP str2Arr(string s){
HP h;
int i = 0;
while(s[i] == '0' && i<s.size()-1) i++;
s = s.substr(i);
h.len = s.size();
for(int i = 0;i<h.len;i++) h.data[MAX-i-1] = s[h.len-i-1] - '0';

return h;
}


void div(HP h1, int b, HP &h2, int &r){
int l = h1.len, t = 0;
while(t<b&&l){
t = t*10 + h1.data[MAX-l];
l--;
}
h2.len = l+1;
h2.data[MAX-l-1] = t/b;

while(l){
t = t%b*10+h1.data[MAX-l];
h2.data[MAX-l] = t/b;
l--;
}
r = t%b;
}

void print(HP h){
if(h.data[0]) cout<<"-";
for(int i = MAX-h.len;i<MAX;i++) cout<<h.data[i];
cout<<endl;
}

int main(){
string s1 = "000351";
HP h1 = str2Arr(s1),h2;
int b=18,r;
print(h1);

div(h1, b, h2, r);
cout<<h2.len<<endl;
print(h2);
cout<<r<<endl;
return 0;
}


执行结果如下:


信息学集训 | 17 高精度算法理论与实现2_c++_07


3 作业

本节课的作业,就是复习上面的所有知识,能够默写出所有高精度代码。


​ ​ ​ ​ ​


AI与区块链技术

信息学集训 | 17 高精度算法理论与实现2_c++_08



标签:信息学,17,h2,MAX,HP,h1,len,data,集训
From: https://blog.51cto.com/u_12001271/5851236

相关文章

  • CF1743F Intersection and Union 题解
    更好体验线段的贡献不好统计,考虑统计每一个点在不同情况中的被覆盖次数,那么每个点的被覆盖次数总和即为答案。设\(f_{i,j,0/1}\)表示\(i\)点在扫描到线段\(j\)时是......
  • day17
    【0151.翻转字符串里的单词】classSolution{public:stringreverseWords(strings){intslow=0;intfast=1;while(fast<s......
  • 【CF1750F】Majority(容斥+DP)
    题目链接规定一个\(01\)串\(s\)是好的,当且仅当可以经过若干次下面的操作将它变成全\(1\):选择一对\(i,j\)满足\(s_i=s_j=1\)且\(\sum_{k=i}^js_k\ge\frac{j-i+1......
  • CF1750F Majority
    题面传送门看到这个题目觉得非常神奇。首先我们考虑设\(g_i\)表示\(i\)长度的答案,但是显然不好转移。考虑容斥,用总方案数减去不能消成一个的方案数,这里的总方案数要求两......
  • 乘风破浪,遇见新一代工业互联网(Industrial Internet)之工业机器人赛道,自2017年以来,中
    工业机器人工业机器人是先进制造业的关键支撑设备,是一个国家成为制造业强国的基础。而全球工业机器人的市场长期由日本和欧洲公司主导,如瑞士ABB,德国KUKA,日本川崎重工等行......
  • CF1748B Diverse Substrings
    题链:cfluogu诈骗题。Description给你一个数字(\(0\sim9\))组成的字串,问有多少个子串满足:不同数字种类数不少于相同数字的最多出现次数。Analysis暴力思路很好想其实......
  • ES8(2017)
     for(const[key,value]ofObject.entries(target)){  //需px单位基础样式  if(needUnit.includes(key)){   result[key]=unit(value)  ......
  • 配置环境变量的意义 win10配置vs2017环境变量
    你虽然装了很多软件,有很多功能,但是系统并不知道。配置环境变量就是告诉系统,我们可以用哪些功能和到哪里调用。参考: Windows10下配置VS2017环境变量_明卿的博客-CSDN博客......
  • day17-Servlet06
    Servlet0615.HttpServletResponse15.1HttpServletResponse介绍每次HTTP请求,Tomcat都会创建一个HttpServletResponse对象传递给Servlet程序使用HttpServletRequest表示......
  • 2022-2023-1 20221317《计算机基础与程序设计》第十一周学习总结
    作业信息这个作业属于哪个课程:首页-2022-2023-1-计算机基础与程序设计-北京电子科技学院-班级博客-博客园(cnblogs.com)这个作业的要求在:2022-2023-1《计算......