C++算法·高精度运算

高精度的定义

什么是高精度?
高精度运算是指在计算机编程或数学计算中,处理超出标准数据类型范围的技术。通常用于需要几十位甚至上千位有效数字的场景

为什么不能直接用正常数据类型运算呢?
下面给出一些数据类型的存储范围:
int: − 2 31 -2^{31} 231~ 2 31 − 1 2^{31}-1 2311
long long: − 2 63 -2^{63} 263~ 2 63 − 1 2^{63}-1 2631
unsigned long long: 0 0 0~ 2 64 − 1 2^{64}-1 2641
这些数据类型的存储范围无法满足我们的巨大数据
这时候,有2种选择,

  1. 高精度
  2. int128_

先讲int128_
int128_就是类似于利用读写模板改造成的小型高精度
那他的范围有多大呢?
给个具体数据
i n t : − 2 , 147 , 483 , 648 int:-2,147,483,648 int:2,147,483,648~ 2 , 147 , 483 , 647 2,147,483,647 2,147,483,647 ( ( (± 10 位 ) 10位) 10)

l o n g long long l o n g : − 9 , 223 , 372 , 036 , 854 , 775 , 808 long:-9,223,372,036,854,775,808 long:9,223,372,036,854,775,808~ 9 , 223 , 372 , 036 , 854 , 775 , 807 9,223,372,036,854,775,807 9,223,372,036,854,775,807 ( ( (± 19 位 ) 19位) 19)

u n s i g n e d unsigned unsigned l o n g long long l o n g : long: long: 0 0 0~ 18 , 446 , 744 , 073 , 709 , 551 , 615 18,446,744,073,709,551,615 18,446,744,073,709,551,615 ( ( (+ 20 位 ) 20位) 20)
i n t 128 int128 int128_ : : : − 2 127 -2^{127} 2127 2 127 − 1 2^{127}-1 21271,即

− 170141183460469231731687303715884105728 -170141183460469231731687303715884105728 170141183460469231731687303715884105728~ 170141183460469231731687303715884105727 170141183460469231731687303715884105727 170141183460469231731687303715884105727

( ( (± 39 位 ) 39位) 39),很大的范围,但是不如高精,只能满足部分数据
用这个就是图个方便,下面上代码

int128_模板

#include <bits/stdc++.h>
using namespace std;
inline __int128 read(){
    __int128 x=0;
    short f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-'){
        	f=-1;
        }
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
inline void print(__int128 x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
		print(x/10);
	putchar(x%10+'0');
}
int main(){
	__int128 a=read();
	__int128 b=read();
	print(a+b);
	cout <<endl;
	return 0;
}

OK继续正文:高精度
回到刚刚2选1的选择,如果我们选了1,那么
高精度可以处理几千位的运算,但是代码要更复杂一些
不像int128_,高精度的运算需要把 + − × ÷ +-×÷ +×÷分开写函数

高精度的原理

原理就是用 s t r i n g string string(字符串)或者 c h a r char char数组代替int等数据类型
在输入时就用 s t r i n g string string(字符串)或者 c h a r char char数组存储

  • 如果是加法:模拟手工加法,逐位相加并处理进位
  • 如果是减法:先确保 a − b a-b ab( a ≥ b a≥b ab),再逐位相减并处理借位
  • 如果是乘法:模拟手工乘法,逐位相乘后累加
  • 如果是除法:模拟手工除法,逐位试商,从高位到低位处理

加法模板

int a[10001],b[10001],c[10001];
string add(string a1,string b1){
	int lena=a1.size();
	int lenb=b1.size();
	for(int i=0;i<lena;i++){
		a[i]=a1[lena-1-i]-'0';
	}
	for(int i=0;i<lenb;i++){
		b[i]=b1[lenb-1-i]-'0';
	}
	int lenc=max(lena,lenb),t=0;
	for(int i=0;i<lenc;i++){
		c[i]=a[i]+b[i]+t;
		if(c[i]>=10){
			t=1;
			c[i]%=10;
		}else{
			t=0;
		}
	}
	if(t==1){
		lenc++;
		c[lenc-1]=1;
	}
	string s;
	for(int i=lenc-1;i>=0;i--){
		s+=c[i]+'0';
	}
	return s;
}//输入两个相加的数的字符串即可返回结果

减法模板

int a[10001],b[10001],c[10001];
bool cmp(string s1,string s2){
	if(s1.size()<s2.size())
		return true;
	else if(s1.size()>s2.size())
		return false;
	else{
		if(s1<s2)
			return true;
		return false;
	}
}
string sub(string a1,string b1){ 
	if(cmp(a1,b1)){
		putchar('-');
		swap(a1,b1);
	}
	int lena=a1.size();
	int lenb=b1.size();
	for(int i=0;i<lena;i++){
		a[i]=a1[lena-1-i]-'0';
	}
	for(int i=0;i<lenb;i++){
		b[i]=b1[lenb-1-i]-'0';
	}
	int lenc=lena;
	for(int i=0;i<lenc;i++){
		if(a[i]<b[i])
		{
			a[i+1]--;
			a[i]+=10;
		}
		c[i]=a[i]-b[i];
	}
	while(lenc>=1&&c[lenc-1]==0){
		lenc--;
	}
	string s;
	for(int i=lenc-1;i>=0;i--){
		s+=c[i]+'0';
	}
	return s;
}//输入两个字符串即可返回相减的结果

乘法模板

int a[10001],b[10001],c[10001];
string mul(string a1,string b1){
	int lena=a1.size();
	int lenb=b1.size();
	int lenc=lena+lenb;
	for(int i=0;i<lena;i++){
		a[i]=a1[lena-i-1]-'0';
	}
	for(int i=0;i<lenb;i++){
		b[lenb-i-1]=b1[i]-'0';
	}
	for(int i=0;i<lena;i++){
		for(int j=0;j<lenb;j++)
		{
			c[i+j]+=a[i]*b[j];
			if(c[i+j]>=10)
			{
				c[i+j+1]+=c[i+j]/10;
				c[i+j]%=10;
			}
		}
	}
	while(lenc>1&&c[lenc-1]==0){
		lenc--;
	}
	string s;
	for(int i=lenc-1;i>=0;i--){
		s+=c[i]+'0';
	}
	return s;
}//输入两个字符串即可返回相乘结果

除法模板

高精除以低精 无余数

int a[10001],c[10001];
string div(string a1,int b){
	int lena=a1.size();
	for(int i=0;i<lena;i++){
		a[i]=a1[i]-'0';
	}
	long long r=0,cnt=0;
	for(int i=0;i<lena;i++){
		r=r*10+a[i];
		cnt++;
		c[cnt]=0;
		if(r>=b)
		{
			c[cnt]=r/b;
			r%=b;
		}
	}
	int i=0;
	while(c[i]==0&&(cnt-i)>0){
		i++;
	}
	string s;
	for(;i<=cnt;i++){
		s+=c[i]+'0';
	}
	return s;
}

高精除以低精 带余数(注意函数是 void \text{void} void

int a[10001],c[10001];
void div(string a1,int b){
	int lena=a1.size();
	for(int i=0;i<lena;i++){
		a[i]=a1[i]-'0';
	}
	long long r=0,cnt=0;
	for(int i=0;i<lena;i++){
		r=r*10+a[i];
		cnt++;
		c[cnt]=0;
		if(r>=b)
		{
			c[cnt]=r/b;
			r%=b;
		}
	}
	int i=0;
	while(c[i]==0&&(cnt-i)>0){
		i++;
	}
	for(;i<=cnt;i++){
		cout<<c[i];
	}
	cout<<endl<<r<<endl;
}//c是商,r是余数

例题

例题来源于 洛谷 洛谷 洛谷(例题太多了,不在这里展示)
例题 1 例题1 例题1←高精度加法
例题 2 例题2 例题2←高精度减法
例题 3 例题3 例题3←高精度乘法
例题 4 例题4 例题4←高精度除法

补充&优化

  1. 压位储存
    使用10000或更高进制减少计算次数
  2. FFT加速乘法
    对于超大数乘法,可使用快速傅里叶变换(FFT)复杂度从 O ( n 2 ) O(n^2) O(n2)降至 O ( n log ⁡ n ) O(n \log n) O(nlogn)
  3. 预处理与缓存
    频繁使用的运算结果可预先计算并缓存(如阶乘表)

高精度的应用场景

  • 大数阶乘
    计算 1000 ! 1000! 1000!等超出long long范围的结果
  • 高精度幂运算:
    2 1000 2^{1000} 21000的精确值
  • 数论问题
    大素数判定、模运算等

~ 完结撒花 完结撒花 完结撒花 ~

附:Python语言不需要高精qwq,Py自带高精~
推荐 洛谷 洛谷 洛谷作为你的刷题区域
下一篇预告:你猜

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值