大整数运算的来源
常用的数据类型及其表示范围 :
UInt32/unsigned int: 0~4294967295
Int32/int: -2147483648~2147483647
Int64/long long: -9223372036854775807~9223372036854775808
UInt64/unsigned long long:0 ~18446744073709551615
float: -3.4E-38~3.4E+38
double : 1.7E-308~1.7E+308
首先看看python怎么写
a=int(input())
b=int(input())
print(a*b)
唉,流泪
由于c++跟python不一样,它的数据类型范围是有限的,最大能表示的正整数也只有20位,数据再大一些的时候就会发生数据溢出,这时候我们可以借助字符串来进行大整数运算;
1.大整数运算仿照小学加减乘除计算方法(就是手算时在草稿本上的步骤,写代码的时候脑子里时刻想象着这个步骤来写代码),进行模拟运算;
2.大整数加减乘法是借助int数组(或者vector数组)和字符串进行操作,字符串默认没有前导0
3.加减乘每次计算前都将字符串先翻转一下,从最低位开始求
4.加法和乘法最后不需要判断最高位是不是0,减和除需要判断前导0
大整数加法
大整数加法是最简单的
参考代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100005;
int ans[N];
string a,b;
int main()
{
cin>>a>>b;
reverse(a.begin(),a.end()); //翻转
reverse(b.begin(),b.end());
int lena=a.length();
int lenb=b.length();
int i=0;
int jin=0;
while(i<lena&&i<lenb) //a与b相同长度的部分
{
int t=a[i]-'0'+(b[i]-'0')+jin;
ans[i]=t%10;
jin=t/10;
i++;
}
while(i<lena)
{
int t=a[i]-'0'+jin;
ans[i]=t%10;
jin=t/10;
i++;
}
while(i<lenb)
{
int t=b[i]-'0'+jin;
ans[i]=t%10;
jin=t/10;
i++;
}
if(jin) ans[i++]=jin; //最后还有进位
for(int j=i-1;j>=0;j--)
cout<<ans[j];
return 0;
}
大整数减法
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100005;
int ans[N];
string a,b;
int main()
{
cin>>a>>b;
if(a==b)
{
cout<<0;return 0;
}
if(a.length()<b.length()||(a.length()==b.length()&&a<b))
{
cout<<"-"; //a比b小就输出负数
string t=a;a=b;b=t;
}//保证a比b大
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
int lena=a.length();
int lenb=b.length();
int i=0;
int jin=0;
while(i<lena&&i<lenb)
{
int t=(a[i]-'0')-(b[i]-'0')+jin;
if(t<0)
{
t+=10;jin=-1;
}
else jin=0;
ans[i]=t;
i++;
}
while(i<lena)
{
int t=a[i]-'0'+jin;
if(t<0)
{
t+=10;jin=-1;
}
else jin=0;
ans[i]=t;
i++;
}
while(i<lenb)
{
int t=b[i]-'0'+jin;
if(t<0)
{
t+=10;jin=-1;
}
else jin=0;
ans[i]=t;
i++;
}
if(jin==-1) ans[i++]--;
bool f=false;
for(int j=i-1;j>=0;j--)
{
if(ans[j]!=0) f=true;
if(f) cout<<ans[j];
}
return 0;
}
大整数乘法
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1000005;
int ans[N];
string a,b;
int main()
{
cin>>a>>b;
if(a=="0"||b=="0")
{
cout<<0;return 0;
}
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
int lena=a.length();
int lenb=b.length();
int ind=0;
for(int i=0;i<lenb;i++)
{
ind=i;
for(int j=0;j<lena;j++)
{
int t=(a[j]-'0')*(b[i]-'0')+ans[ind];
ans[ind]=t%10;
ans[ind+1]+=t/10;
ind++;
}
}
if(ans[ind]!=0) cout<<ans[ind];
for(int i=ind-1;i>=0;i--)
cout<<ans[i];
return 0;
}
大整数除法
不需要进行字符串的翻转,模拟草稿纸上的计算
被除数用string类型表示,除数默认是小数据,用int型表示
一般不处理高精度除以高精度的问题,复杂度会很高
#include<iostream>
#include<algorithm>
#include<sstream>
using namespace std;
const int N=1000005;
int ans[N];
string a;
int b;
int main()
{
cin>>a>>b;
if(a=="0")
{
cout<<0<<endl;
cout<<0<<endl;return 0;
}
stringstream ss; //a有可能比b小,单独考虑
ss<<b;
string bi=ss.str();
if(a.length()<bi.length()||(a.length()==bi.length()&&a<bi))
{
cout<<0<<endl;
cout<<a<<endl; return 0;
}
int lena=a.length();
int achu=0,bchu=0,yushu=0;
int i=0;
while(i<lena) //也是模拟在草稿纸上进行除法运算的过程,主要是b的数值不大,就可以直接做整数之间的除法,就会简单一些
{
achu+=a[i]-'0';
ans[i]=achu/b;
yushu=achu%b;
achu=yushu;
achu*=10;
i++;
}
bool f=false;
for(int j=0;j<i;j++)
{
if(ans[j]!=0) f=true;
if(f) cout<<ans[j];
}
cout<<endl;
cout<<yushu<<endl;
return 0;
}
大整数阶乘
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
int max=36000;
int a[max];
for(int i=0;i<max;i++)
a[i]=0; //数组不是默认为0,先要给数组赋值
int c;
a[0]=1;
int sum;
for(int i=2;i<=n;i++) //从2乘到n
{
c=0;
for(int j=0;j<max;j++)
{
sum=a[j]*i+c; //乘积加进位
a[j]=sum%10;
c=sum/10;
}
}
bool flag=false;
for(int l=max-1;l>=0;l--)
{
if(!flag)
{
if(a[l]>0)
{
flag=true;
cout<<a[l];
}
}
else cout<<a[l];
}
return 0;
}
大整数阶乘和
阶乘和是利用一个临时数组进行阶乘,然后利用大整数加法累加到结果数组里面
#include<bits/stdc++.h>
using namespace std;
const int ma=100; // int max=100;
int ans[ma];
int jc[ma];
void leijia() //大整数加法
{
int up=0;
for(int i=0;i<ma;i++)
{
int temp=ans[i]+jc[i]+up;
up=temp/10;
ans[i]=temp%10;
}
}
int main()
{
int n;cin>>n;
jc[0]=1;
for(int k=1;k<ma;k++)
jc[k]=0;
memset(ans,0,sizeof(ans));
int up;
for(int k=1;k<=n;k++)
{
jc[0]=1;
for(int m=1;m<ma;m++)
jc[m]=0;
for(int i=k;i>=1;i--)
{
up=0;
for(int j=0;j<ma;j++)
{
int temp=jc[j]*i+up;
jc[j]=temp%10;
up=temp/10;
}
}
leijia();
}
bool flag=true;
for(int i=ma-1;i>=0;i--)
{
if(ans[i]!=0||!flag)
{
flag=false;
cout<<ans[i];
}
}
return 0;
}
其中一个因子不是很大
在大整数的加减乘除等方法中是将两个因子都看成了字符串来表示,但是在实际做题的过程中可能会出现以下两种情况,可以对上面的算法进行稍微的改进
- x +、-、*、/ y,但是y用int或者long long能够表示的情况,这时候就只需要将x用字符串表示,y直接保持原来的数据类型就行
- 如果题目对算法的时间复杂度要求只能是O(n),将x和y都看成字符串的情况(主要是乘法)需要两重循环,这时候基本上题目也隐含了y是可以直接保持原来的数据类型不变的,外层循环只需要遍历一边x的每一位,对每一位进行加减乘除y(对于除法运算一般都默认y是整数了,不然复杂度会很高)
参考代码如下(乘法):
string cheng(string a,int b)
{
if(a=="0"||b==0) return "0";
int lena=a.length();
int now=0;
vector<int>ans;
for(int ai=lena-1;ai>=0;ai--) //当a还没结束
{
now+=(a[ai]-'0')*b;
ans.push_back(now%10);
now/=10; //进位
}
while(now)
{
ans.push_back(now%10);
now/=10;
}
//去掉前导0
string res="";
for(int i=ans.size()-1;i>=0;i--) res+=(ans[i]+'0');
return res;
}