高精度算法 万字详解(C++)
目录
高精度算法
总起代码
加法高精度
减法高精度
乘法高精度
除法高精度
高精度算法
当我们利用计算机进行数值计算,有时候会遇到这样的问题: n!的精确结果是多少?
当n小于30的时候,我们当然可以通过电脑自带的计算器计算出来。但是当我们遇到 100! 的时候就没有办法直接计算出精确的结果。再比如,求两个20000位的数的和。所谓高精度运算,是指参与运算的数(加数,减数,因子……)范围大大超过了标准数据类型(整型,实型)能表示的范围的运算。
高精度处理,实际上就是模拟法,模拟手算,它的原理与我们用竖式计算时一样的,不过在处理过程中要注意高精度数据的读入、转换储存、数据的计算、结果位数的计算、输出等几个问题。高精度计算中需要处理如下几个问题:
数据的输入:
数据的输入,需要考虑输入的位数问题(可能成百上千位),也要考虑输入的数可能带字母(可能是16进制)等问题。所以我们可以采用字符串输入或者字符数组输入。
//方式一:
string s1,s2;
cin>>s1>>s2;
//方式二,使用字符数组的时候要注意,字符数组要分配足够的空间
char s1[200];
char s2[200];
cin>>s1>>s2;
数据的转换储存:
数据是通过字符串输入的,不能够直接进行计算,需要转换成数字。我们选用最基本的转换储存方式,字符串的一个字符对应数组中的一个元素,把字符串的数字全部转换到一个数组中,其中数组下标为1对应输入数的个位,数组下标为2的对应输入数的十位,以此类推。
//使用字符串类型接收
string s1,s2;
int a[1000],b[1000];
void transfer(){int len1=s1.length();a[0]=len1;//将输入数据的位数存储在整型数组的第一个元素for(int i=0;i<len1;i++){a[i+1]=s1[len1-1-i]-'0';//逆序存储}int len2=s2.length();b[0]=len2;for(int i=0;i<len2;i++){b[i+1]=s2[len2-1-i]-'0';//逆序存储}
}
//使用字符数组接收
char s1[1010],s2[1010];
int a[1000],b[1000];
void transfer(){int len1=strlen(s1);a[0]=len1;//将输入数据的位数存储在整型数组的第一个元素for(int i=0;i<len1;i++){a[i+1]=s1[len1-1-i]-'0';//逆序存储}int len2=strlen(s2);b[0]=len2;for(int i=0;i<len2;i++){b[i+1]=s2[len2-1-i]-'0';//逆序存储}
}
通过这样,我们就可以把输入的数据很好的放入数组当中了。
数据的计算
参考相应的高精度运算专题版(包括加法、减法、乘法和除法)
结果位数的计算
去除最高位的0,在各个算法中都有体现
结果的输出
输出计算结果。
每种高精度运算的过程,大家可以通过一个竖式来提高自己的认识。对于除法的高精度可以了解一下,其他几个高精度的算法,必须掌握。
总起代码
#include <iostream>
#include <string>
using namespace std;const int N = 1e3;
string str1,str2;//输入操作数int num1[N],num2[N];//转化后的操作数
int num3[N];
void transfer(string s, int n[]);
void print(int n[]);void add(int n1[],int n2[],int n3[]);
void sub(int n1[],int n2[],int n3[]);
bool comp(int n1[],int n2[]);void mul(int n1[],int k,int n3[]);
void mul(int n1[],int n2[],int n3[]);void div(int n1[N],int k,int n3[]);
int main(){//1.读入cin>>str1>>str2;//2.存储转化transfer(str1,num1);transfer(str2,num2);//3.计算
// add(num1,num2,num3);//3.减法
// if(comp(num1,num2)){
// sub(num1,num2,num3);
// print(num3);
// }else{
// sub(num2,num1,num3);
// cout<<"-";
// print(num3);
// }//3.乘法:高*低
// mul(num1,100,num3);
// mul(num1,num2,num3);
// print(num3);div(num1,99,num3);print(num3);return 0;
}void transfer(string s, int n[]){int len = s.size();n[0] = len;//操作数的长度;for (int i = 0; i < len ; ++i) {n[i+1] = s[len - 1 - i] - '0';}
}void print(int n[]){for (int i = n[0]; i >= 1 ; i--) {cout<<n[i];}
}/*** n1 + n2 -> n3* @param n1* @param n2* @param n3*/
void add(int n1[],int n2[],int n3[]){int d[N] = {0};int len = max(n1[0],n2[0]);for (int i = 1; i <= len ; ++i) {d[i] += n1[i] + n2[i];d[i + 1] = d[i] / 10;d[i] = d[i] % 10;}//4.处理前导0int l = len + 1;while(d[l] == 0 && l > 1){l --;}d[0] = l;memcpy(n3,d, N * sizeof(int));
}void sub(int n1[],int n2[],int n3[]){int d[N] = {0};int len = n1[0];for(int i = 1;i <= len;i++){if(n1[i] < n2[i]){n1[i + 1] --;n1[i] += 10;}d[i] = n1[i] - n2[i];}int l = len;while(d[l] == 0 && l > 1){l--;}d[0] = l;memcpy(n3,d,N * sizeof(int));
}bool comp(int n1[],int n2[]){if(n1[0] != n2[0]){return n1[0] > n2[0];}else{int len = n1[0];for (int i = len; i >= 1 ; i--) {if(n1[i] != n2[i]){return n1[i] > n2[i];}}return true;}
}/*** 高精度*低精度* @param n1* @param k* @param n3*/
void mul(int n1[],int k,int n3[]){int d[N] = {0};int len = n1[0];for (int i = 1; i <= len ; ++i) {d[i] += n1[i] * k;d[i + 1] = d[i] / 10;d[i] = d[i] % 10;}//处理进位int l = len + 1;while (d[l] >= 10){d[l + 1] = d[l] / 10;d[l] = d[l] % 10;l++;}//前导0while(d[l] == 0 && l > 1){l--;}d[0] = l;memcpy(n3,d,N * sizeof(int));
}void mul(int n1[],int n2[],int n3[]){int d[N] = {0};//n1[i] * n2[j]结果放在d[i+j-1]for (int i = 1; i <= n1[0] ; ++i) {for (int j = 1; j <= n2[0] ; ++j) {int t = i + j - 1;d[t] += n1[i] * n2[j];d[t + 1] += d[t] / 10;d[t] = d[t] % 10;}}int l = n1[0] + n2[0];while(d[l] == 0 && l > 1){l -- ;}d[0] = l;memcpy(n3,d, N * sizeof(int));
}void div(int n1[N],int k,int n3[]){int d[N] = {0};int r = 0;for (int i = n1[0]; i >= 1 ; i--) {int sum = r * 10 + n1[i];d[i] = sum / k;r = sum % k;}//前导0int l = n1[0];while(d[l] == 0 && l > 1){l -- ;}d[0] = l;memcpy(n3,d,N * sizeof(int));
}
下面来详细讲解 ↓
加法高精度
加法:
两个正整数相加,c数组=a数组+b数组(注:a[1]表示a数据的个位),对应位置相加,得到的新数字如果超过9,把个位留下,高位进上去,完全模拟加法的竖式运算,注意各位是从数组下标1开始的,运用如下代码。
/**
*
* @param a 加法
* @param b 加数
* @param c 和
* @param len 数组c的长度
*/
void add(int a[],int b[],int c[],int len){int d[len]={0};int m=max(a[0],b[0]);for(int i=1;i<=m;i++){d[i]=a[i]+b[i];d[i+1]=d[i]/10; //进位d[i]=d[i]%10; //取进位后余下的数}memcpy(c,d,len* sizeof(int));
}
去掉计算结果中最高位的0,因为两数相加,可能不产生进位,因此需要把这种情况下最高位的0去掉,其实就是减小和的长度。
//与上面的add函数相比,多了去掉高位0的操作
/**
*
* @param a 加法
* @param b 加数
* @param c 和
* @param len 数组c的长度
*/
void add(int a[],int b[],int c[],int len){int d[len]={0};int m=max(a[0],b[0]);for(int i=1;i<=m;i++){d[i]=a[i]+b[i];d[i+1]=d[i]/10; //进位d[i]=d[i]%10; //取进位后余下的数}int l=m+1;while(d[l]==0&&l>1){l--;}d[0]=l;memcpy(c,d,len* sizeof(int));
}
输出结果
void printResult(int c[]){for(int i=c[0];i>=1;i--){cout<<c[i];}
}
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int a[2000],b[2000],c[2000];
char al[2000],bl[2000];
int main(){int la,lb,lc,x=0;gets(al); gets(bl);la=strlen(al); lb=strlen(bl);for(int i=0;i<=la-1;++i) a[la-i]=al[i]-48;for(int i=0;i<=lb-1;++i) b[lb-i]=bl[i]-48;lc=1;while(lc<=la||lc<=lb){c[lc]=a[lc]+b[lc]+x;x=c[lc]/10;c[lc]%=10;lc++;}c[lc]=x;while(c[lc]==0) lc--;for(int i=lc;i>=1;--i) cout<<c[i];cout<<endl;return 0;
}
减法高精度
比较数组a和数组b的大小从而确定结果的正负号。
两个正整数相减,a数组=大a数组-小b数组,通常情况下,需要比较参与运算的两个整数的大小,通过大数减小数,模拟普通的减法,从低位开始对应的减,不足就向高位借一。
//比较两个数的大小,使用大数减去小数
bool compare(int a[],int b[]){if(a[0]!=b[0]){return a[0]>b[0];}else{for(int i=a[0];i>=1;i--){if(a[i]!=b[i]){return a[i]>b[i];}}return 1;}
}
使用大数减小数,并且去掉最高位的0。
/**
*
* @param a
* @param b a,b为参与计算的两个数
* @param c 差
* @param len 数组c的长度
*/
void sub(int a[], int b[], int c[], int len) {int d[len] = {0};for (int i = 1; i <= a[0]; i++) {if (a[i] < b[i]) {a[i] += 10;a[i + 1]--;}d[i] = a[i] - b[i];}int l = a[0];while (d[l] == 0 && l > 1) {l--;}d[0] = l;memcpy(c, d, len * sizeof(int));
}
打印输出结果
void printResult(int c[]){if(!compare(a,b)){cout<<"-";}for(int i=c[0];i>=1;i--){cout<<c[i];}
}
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char s1[10001],s2[10001],n[10001];
int a[10001],b[10001];
int main(){scanf("%s",s1); scanf("%s",s2);if(strlen(s1)==strlen(s2)){cout<<"0";}else if(strlen(s1)<strlen(s2)||strlen(s1)==strlen(s2)&&strcmp(s1,s2)<0){strcpy(n,s1);strcpy(s1,s2);strcpy(s2,n);printf("-");}int len1=strlen(s1),len2=strlen(s2);for(int i=len1-1;i>=0;--i) a[len1-(i+1)+1]=s1[i]-'0';for(int i=len2-1;i>=0;--i) b[len2-(i+1)+1]=s2[i]-'0';for(int i=1;i<=len1;++i){a[i]-=b[i];if(a[i]<0) a[i]+=10,a[i+1]--;}while(! a[len1]) len1--;for(int i=len1;i>=1;--i) printf("%d",a[i]);return 0;
}
乘法高精度
乘法
这里有2种方法:
1:
一大正整数乘以小正整数,即高精度数乘以一个整型数,c数组=a数组*k;
同加法类似,每一位都乘以k,再加上之前的进位,如果新得到的数字超过了10,那么个位留下,高位进上去。
/**
*
* @param a 被乘数
* @param k 乘数
* @param c 积
* @param len 数组c的长度
*/
void multipy(int a[],int k,int c[],int len){int d[len]={0};for(int i=1;i<=a[0];i++){d[i]+=a[i]*k;d[i+1]=d[i]/10;d[i]=d[i]%10;}int p=a[0]+1;//防止出现大于10的位while(d[p]>=10){d[p+1]=d[p]/10;d[p]=d[p]%10;p++;}
}
去除最高位的0,首先需要确定这两个数相乘可能的最大位数长度:
//计算k的位数
int f=k;
int size=0;
while(f!=0){size++;f/=10;
}
//size是乘数k的位数,乘积的长度
int l=a[0]+size;
完整的乘法函数
/**
*
* @param a 被乘数
* @param k 乘数
* @param c 积
* @param len 数组c的长度
*/
void multipy(int a[],int k,int c[],int len){int d[len]={0};for(int i=1;i<=a[0];i++){d[i]+=d[i]+a[i]*k;d[i+1]=d[i]/10;d[i]=d[i]%10;}int l=a[0]+1;int t=d[l];while(t!=0){d[l]=t%10;t/=10;l++;}
//去除前导0while(d[l]==0&&l>1){l--;}d[0]=l;memcpy(c,d,len* sizeof(int));
}
2:
两大正整数相乘,c数组=a数组*b数组
模拟普通乘法竖式计算。
/**
*
* @param a 被乘数
* @param b 乘数
* @param c 乘积
* @param len 数组c的长度
*/
void multiply(int a[],int b[],int c[],int len){int d[len]={0};for(int i=1;i<=a[0];i++) {for (int j = 1; j <= b[0]; j++){d[i + j - 1] = a[i] * b[j] + d[i + j - 1];d[i+j]+= d[i + j - 1] / 10;//进位d[i + j - 1] = d[i + j - 1] % 10;}}int l=a[0]+b[0];while(d[l]==0&&l>1){l--;}d[0]=l;
//数组d拷贝导数组cmemcpy(c,d,len* sizeof(int));
}
输出结果
void printResult(int c[]){for(int i=c[0];i>=1;i--){cout<<c[i];}
}
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
char a2[1001],b2[1001];
int a[1001],b[1001],s,ss,s3,x,c[10001];
int main(){gets(a2);gets(b2);s=strlen(a2);ss=strlen(b2);for(int i=0;i<=s-1;i++)a[s-i]=a2[i]-'0';for(int i=0;i<=ss-1;i++)b[ss-i]=b2[i]-'0';for(int i=1;i<=s;i++){x=0;for(int j=1;j<=ss;j++){c[i+j-1]=a[i]*b[j]+x+c[i+j-1];x=c[i+j-1]/10;c[i+j-1]%=10;}c[i+ss]=x;}s3=s+ss;while(c[s3]==0&&s3>1)s3--;for(int i=s3;i>=1;i--) cout<<c[i];cout<<endl;return 0;
}
除法高精度
除法
这里也有2种方法:
1:
大正整数除以小正整数,c数组=a数组÷b数,模拟除法竖式计算
/**
*
* @param a 被除数
* @param k 除数
* @param c 商
* @param l
*/
void divide(int a[],int k,int c[],int len){int d[len]={0};int x=0; //从高位开始除for(int i=a[0];i>=1;i--){d[i]=(x*10+a[i])/k;x=(x*10+a[i])%k;}
//计算k的位数int f=k;int size=0;while(f!=0){size++;f/=10;}
//size是成数k的位数int l=max(a[0]-size+1,1);
//去除前导0while(d[l]==0&&l>1){l--;}d[0]=l;
//把数组d的数据拷贝给cmemcpy(c,d,len* sizeof(int));
}
2:
大正整数除以小正整数,c数组=a数组÷b数组
找到运算结果的最高位,从最高位一直循环到个位:
for(int i=a[0]-b[0]+1;i>=1;i--){//a[0]-b[0]+1运算结果的最高位
//除法运算
}
数组a从最高位到当前位与数组b进行比较
/**
* 比较大小
* @param a 被除数
* @param b 除数
* @param current 从最高位数到当前位置
* @return
*/
bool compareab(int a[],int b[],int current){if(a[0]-current+1>b[0]){return true; //大}else if(a[0]-current+1<b[0]){return false;//小}else{for(int i=a[0],j=b[0];i>=current;i--,j--){if(a[i]==b[j]){continue;}else{return a[i]>b[j];}}return 1;}
}
数组a从最高位到当前位减数组b
/**
* 减法
* @param a 被除数
* @param b 除数
* @param current 从最高位数到当前位置
* @return
*/
void deduce(int a[],int b[],int current){for(int i=current,j=1;i<=a[0];i++,j++){if(a[i]<b[j]){a[i]+=10;a[i+1]--;}a[i]=a[i]-b[j];}int len=a[0]; //减完以后把最高位的0去掉while(a[len]==0){len--;}a[0]=len; //
}
补充前面循环中的除法处理代码:
/**
*
* @param a
* @param b
* @param c
* @param len 数组a和数组c的长度,通常情况下我们把a,b,c三个数组的长度都设置成相同
*/
void divide(int a[],int b[],int c[],int len){int d[len]={0};int a1[len];
//使用a1进行计算,因为在计算过程中,被除数会被改变,因此拷贝一个数组参与运算memcpy(a1,a,len* sizeof(int));d[0]=1;//防止输入的被除数小于除数,无输出结果。for(int i=a[0]-b[0]+1;i>=1;i--){int t=0;while(compareab(a1,b,i)){deduce(a1,b,i);t++;}d[i]=t;}int l=max(a[0]-b[0]+1,1);
//去除前导0while(d[l]==0&&l>1){l--;}d[0]=len;
//把数组d的数据拷贝给cmemcpy(c,d,len* sizeof(int));
}
输出结果
void printResult(int c[]){for(int i=c[0];i>=1;i--){cout<<c[i];}
}
代码:
#include<iostream>
#include<cstring>
using namespace std;
int a[100],b[100],c[100];
int compare(int a[],int b[])
{ int i; if(a[0]>b[0]) return 1; if(a[0]<b[0]) return -1; for(i=a[0];i>0;i--){ if(a[i]>b[i]) return 1; if(a[i]<b[i]) return -1; } return 0;
} void subduction(int a[],int b[])
{ int flag; int i; flag=compare(a,b); if(flag==0){ a[0]=0; return; } if(flag==1){ for(i=1;i<=a[0];i++) { if(a[i]<b[i]) { a[i+1]--; a[i]+=10; } a[i]-=b[i]; } while(a[0]>0&&a[a[0]]==0) a[0]--; return; }
}
int main()
{ char str1[100],str2[100]; int i,j; memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); memset(c,0,sizeof(c)); cin>>str1>>str2; a[0]=strlen(str1); b[0]=strlen(str2); for(i=1;i<=a[0];i++) a[i]=str1[a[0]-i]-'0'; for(i=1;i<=b[0];i++) b[i]=str2[b[0]-i]-'0'; int temp[100]; c[0]=a[0]-b[0]+1; for(i=c[0];i>0;i--) { memset(temp,0,sizeof(temp)); for(j=1;j<=b[0];j++)temp[j+i-1]=b[j]; temp[0]=b[0]+i-1; while(compare(a,temp)>=0) { c[i]++; subduction(a,temp); } } while(c[0]>0&&c[c[0]]==0) c[0]--; if(c[0]==0) cout<<0<<endl; else { for(i=c[0];i>0;i--) cout<<c[i]; cout<<endl; } if(a[0]==0) cout<<0<<endl; else { for(i=a[0];i>0;i--) cout<<a[i]; cout<<endl; } return 0;
}
注意除以0的情况!!
高精度算法 万字详解(C++)相关推荐
- 万字详解5G车联网技术
车路协同.车联网.智慧交通.智能网联车.自动驾驶.无人驾驶.高精度地图资料汇总与整理 车路协同优质资料整理地址: 语雀:车路协同.自动驾驶.车联网.智能网联车 · 语雀 (yuque.com) 因阿里 ...
- JVM---垃圾收集器(万字详解)
垃圾收集器万字详解 垃圾回收器 垃圾收集器万字详解 前言 垃圾收集器说明与发展史 垃圾收集器简要说明 垃圾收集器发展史 垃圾收集器的分类 衡量垃圾收集器性能的指标 不同的垃圾收集器概述 7款经典的垃圾 ...
- [Python从零到壹] 十一.数据分析之Numpy、Pandas、Matplotlib和Sklearn入门知识万字详解(1)
欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...
- [论文阅读] (06) 万字详解什么是生成对抗网络GAN?经典论文及案例普及
<娜璋带你读论文>系列主要是督促自己阅读优秀论文及听取学术讲座,并分享给大家,希望您喜欢.由于作者的英文水平和学术能力不高,需要不断提升,所以还请大家批评指正,非常欢迎大家给我留言评论,学 ...
- 推荐系统整体架构及算法流程详解
省时查报告-专业.及时.全面的行研报告库 省时查方案-专业.及时.全面的营销策划方案库 知识图谱在美团推荐场景中的应用实践 搜索场景下的智能实体推荐 机器学习在B站推荐系统中的应用实践 小红书推荐系统 ...
- 万字详解什么是生成对抗网络GAN
摘要:这篇文章将详细介绍生成对抗网络GAN的基础知识,包括什么是GAN.常用算法(CGAN.DCGAN.infoGAN.WGAN).发展历程.预备知识,并通过Keras搭建最简答的手写数字图片生成案. ...
- [Python从零到壹] 十五.文本挖掘之数据预处理、Jieba工具和文本聚类万字详解
欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...
- CORDIC算法原理详解及其Verilog实现
CORDIC算法原理详解及其Verilog实现 本文的verilog代码 链接:https://pan.baidu.com/s/1GGbRjxO5CxoIODQAg1l6Lw 提取码:jo0h *本文 ...
- [Python从零到壹] 五十一.图像增强及运算篇之图像灰度直方图对比分析万字详解
欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...
- [Python图像处理] 三十三.图像各种特效处理及原理万字详解(毛玻璃、浮雕、素描、怀旧、流年、滤镜等)...
此文转载自:https://blog.csdn.net/Eastmount/article/details/111568397#commentBox 该系列文章是讲解Python OpenCV图像处理 ...
最新文章
- Android UI设计与开发】第03期:引导界面(三)仿微信引导界面以及动画效果
- 在emu8086中学习汇编语言加减法程序
- Lesson 5.分类模型决策边界与模型评估指标
- C++中的指针与引用
- [JUC-4]ThreadPoolExecutor源码分析
- 20145227鄢曼君《网络对抗》Web安全基础实践
- DateUtils.paraseDate(String sDate,String[] parasePatterns);转换时间不对,pattern数组顺序
- 如何在Java中将字节数组转换为InputStream和OutputStream
- OJ1061: 顺序输出各位数字(C语言)(谈程序优化问题及pow函数耗时问题)
- linux系列之-—01 shell编程笔记
- Java关键字与保留字
- 帆软 在线Cron表达式
- GAN评价指标代码(FID、LPIPS、MS-SSIM)
- 网课答案查询单页源码+免费题库API接口
- 程序员有哪些类型,分别薪资是多少,带你一起走进程序员的大门
- Android使用Bugly实现静默安装/自动安装app
- 坦克大战第一阶段代码
- 活化N-羟基琥珀酰亚胺酯疏水染料CAS: 201998-61-0,BDP 576 NHS溶于极性有机溶剂该试剂是染料的活化N-羟基琥珀酰亚胺酯,与伯氨基和仲氨基具有反应性。
- 高盛:DeFi 的互操作性可能会增加系统性风险
- 【华为OJ】【067-求最小公倍数】