能用int表示的数据视为单精度,否则为高精度。所有函数的设计均采用带返回值的形式。
本文包含:1.高精度加法;2.高精度减法;
3.高精度乘法 1)高乘高朴素算法 2)高乘高FFT优化算法 3)高精度乘单精度
4.高精度除法 1)高精度除高精度 2)高精度除单精度
5.高精度取模 1)高精度对高精度取模 2)高精度对单精度取模
6.高精度阶乘;7.高精度幂;8.高精度GCD;9.高精度进制转换;10.高精度求平方根
1.高精度加法
传入参数约定:传入参数均为string类型,返回值为string类型
算法思想:倒置相加再还原。
算法复杂度:o(n)
#include<iostream> #include<cstring> #include<algorithm> using namespace std; const int L=110; string add(string a,string b)//只限两个非负整数相加 { string ans; int na[L]={0},nb[L]={0}; int la=a.size(),lb=b.size(); for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0'; for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0'; int lmax=la>lb?la:lb; for(int i=0;i<lmax;i++) na[i]+=nb[i],na[i+1]+=na[i]/10,na[i]%=10; if(na[lmax]) lmax++; for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0'; return ans; } int main() { string a,b; while(cin>>a>>b) cout<<add(a,b)<<endl; return 0; }
2.高精度减法
传入参数约定:传入参数均为string类型,返回值为string类型
算法思想:倒置相减再还原。
算法复杂度:o(n)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int L=110;
string sub(string a,string b)//只限大的非负整数减小的非负整数
{
string ans;
int na[L]={0},nb[L]={0};
int la=a.size(),lb=b.size();
for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
int lmax=la>lb?la:lb;
for(int i=0;i<lmax;i++)
{
na[i]-=nb[i];
if(na[i]<0) na[i]+=10,na[i+1]--;
}
while(!na[--lmax]&&lmax>0) ;lmax++;
for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
return ans;
}
int main()
{
string a,b;
while(cin>>a>>b) cout<<sub(a,b)<<endl;
return 0;
}
传入参数约定:传入参数均为string类型,返回值为string类型
算法思想:倒置相乘,然后统一处理进位,再还原。
算法复杂度:o(n^2)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int L=110;
string mul(string a,string b)//高精度乘法a,b,均为非负整数
{
string s;
int na[L],nb[L],nc[L],La=a.size(),Lb=b.size();//na存储被乘数,nb存储乘数,nc存储积
fill(na,na+L,0);fill(nb,nb+L,0);fill(nc,nc+L,0);//将na,nb,nc都置为0
for(int i=La-1;i>=0;i--) na[La-i]=a[i]-'0';//将字符串表示的大整形数转成i整形数组表示的大整形数
for(int i=Lb-1;i>=0;i--) nb[Lb-i]=b[i]-'0';
for(int i=1;i<=La;i++)
for(int j=1;j<=Lb;j++)
nc[i+j-1]+=na[i]*nb[j];//a的第i位乘以b的第j位为积的第i+j-1位(先不考虑进位)
for(int i=1;i<=La+Lb;i++)
nc[i+1]+=nc[i]/10,nc[i]%=10;//统一处理进位
if(nc[La+Lb]) s+=nc[La+Lb]+'0';//判断第i+j位上的数字是不是0
for(int i=La+Lb-1;i>=1;i--)
s+=nc[i]+'0';//将整形数组转成字符串
return s;
}
int main()
{
string a,b;
while(cin>>a>>b) cout<<mul(a,b)<<endl;
return 0;
}
2)高精度乘高精度FFT优化算法
传入参数约定:传入参数均为string类型,返回值为string类型
算法思想:将两个高精度乘数每个数位上的数视为多项式对应的系数,用o(n*log(n))的复杂度转成点值形式,再利用o(n)的复杂度相乘,最后对点值进行差值,用o(n*log(n))的复杂度还原成多项式的形式,即原来的形式。
算法复杂度:o(n*log(n))
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
#define L(x) (1 << (x))
const double PI = acos(-1.0);
const int Maxn = 133015;
double ax[Maxn], ay[Maxn], bx[Maxn], by[Maxn];
char sa[Maxn/2],sb[Maxn/2];
int sum[Maxn];
int x1[Maxn],x2[Maxn];
int revv(int x, int bits)
{
int ret = 0;
for (int i = 0; i < bits; i++)
{
ret <<= 1;
ret |= x & 1;
x >>= 1;
}
return ret;
}
void fft(double * a, double * b, int n, bool rev)
{
int bits = 0;
while (1 << bits < n) ++bits;
for (int i = 0; i < n; i++)
{
int j = revv(i, bits);
if (i < j)
swap(a[i], a[j]), swap(b[i], b[j]);
}
for (int len = 2; len <= n; len <<= 1)
{
int half = len >> 1;
double wmx = cos(2 * PI / len), wmy = sin(2 * PI / len);
if (rev) wmy = -wmy;
for (int i = 0; i < n; i += len)
{
double wx = 1, wy = 0;
for (int j = 0; j < half; j++)
{
double cx = a[i + j], cy = b[i + j];
double dx = a[i + j + half], dy = b[i + j + half];
double ex = dx * wx - dy * wy, ey = dx * wy + dy * wx;
a[i + j] = cx + ex, b[i + j] = cy + ey;
a[i + j + half] = cx - ex, b[i + j + half] = cy - ey;
double wnx = wx * wmx - wy * wmy, wny = wx * wmy + wy * wmx;
wx = wnx, wy = wny;
}
}
}
if (rev)
{
for (int i = 0; i < n; i++)
a[i] /= n, b[i] /= n;
}
}
int solve(int a[],int na,int b[],int nb,int ans[])
{
int len = max(na, nb), ln;
for(ln=0; L(ln)<len; ++ln);
len=L(++ln);
for (int i = 0; i < len ; ++i)
{
if (i >= na) ax[i] = 0, ay[i] =0;
else ax[i] = a[i], ay[i] = 0;
}
fft(ax, ay, len, 0);
for (int i = 0; i < len; ++i)
{
if (i >= nb) bx[i] = 0, by[i] = 0;
else bx[i] = b[i], by[i] = 0;
}
fft(bx, by, len, 0);
for (int i = 0; i < len; ++i)
{
double cx = ax[i] * bx[i] - ay[i] * by[i];
double cy = ax[i] * by[i] + ay[i] * bx[i];
ax[i] = cx, ay[i] = cy;
}
fft(ax, ay, len, 1);
for (int i = 0; i < len; ++i)
ans[i] = (int)(ax[i] + 0.5);
return len;
}
string mul(string sa,string sb)
{
int l1,l2,l;
int i;
string ans;
memset(sum, 0, sizeof(sum));
l1 = sa.size();
l2 = sb.size();
for(i = 0; i < l1; i++)
x1[i] = sa[l1 - i - 1]-'0';
for(i = 0; i < l2; i++)
x2[i] = sb[l2-i-1]-'0';
l = solve(x1, l1, x2, l2, sum);
for(i = 0; i<l || sum[i] >= 10; i++) // 进位
{
sum[i + 1] += sum[i] / 10;
sum[i] %= 10;
}
l = i;
while(sum[l] <= 0 && l>0) l--; // 检索最高位
for(i = l; i >= 0; i--) ans+=sum[i] + '0'; // 倒序输出
return ans;
}
int main()
{
cin.sync_with_stdio(false);
string a,b;
while(cin>>a>>b) cout<<mul(a,b)<<endl;
return 0;
}
传入参数约定:传入第一个参数为string类型,,第二个参数为int型,返回值为string类型
算法思想:倒置相乘,然后统一处理进位,再还原。
算法复杂度:o(n)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int L=100005;
int na[L];
string mul(string a,int b)//高精度a乘单精度b
{
string ans;
int La=a.size();
fill(na,na+L,0);
for(int i=La-1;i>=0;i--) na[La-i-1]=a[i]-'0';
int w=0;
for(int i=0;i<La;i++) na[i]=na[i]*b+w,w=na[i]/10,na[i]=na[i]%10;
while(w) na[La++]=w%10,w/=10;
La--;
while(La>=0) ans+=na[La--]+'0';
return ans;
}
int main()
{
string a;
int b;
while(cin>>a>>b) cout<<mul(a,b)<<endl;
return 0;
}
C++高精度运算模板
(来自https://blog.csdn.net/code4101/article/details/23020525 膜拜dalao qwq)
另一种版本的高精度类 功能:正整数的加、减、乘、除、取余、大小关系运算。
#include <cassert> // assert #include <cstdio> // printf,sprintf #include <cstring> // strlen #include <iostream> // cin,cout #include <string> // string类 #include <vector> // vector类 #include <algorithm> // max using namespace std; const int maxn = 1000; struct BigInteger { typedef unsigned long long LL; static const int BASE = 100000000; static const int WIDTH = 8; vector<int> s; BigInteger& clean(){while(!s.back()&&s.size()>1)s.pop_back(); return *this;} BigInteger(LL num = 0) {*this = num;} BigInteger(string s) {*this = s;} BigInteger& operator = (long long num) { s.clear(); do { s.push_back(num % BASE); num /= BASE; } while (num > 0); return *this; } BigInteger& operator = (const string& str) { s.clear(); int x, len = (str.length() - 1) / WIDTH + 1; for (int i = 0; i < len; i++) { int end = str.length() - i*WIDTH; int start = max(0, end - WIDTH); sscanf(str.substr(start,end-start).c_str(), "%d", &x); s.push_back(x); } return (*this).clean(); } BigInteger operator + (const BigInteger& b) const { BigInteger c; c.s.clear(); for (int i = 0, g = 0; ; i++) { if (g == 0 && i >= s.size() && i >= b.s.size()) break; int x = g; if (i < s.size()) x += s[i]; if (i < b.s.size()) x += b.s[i]; c.s.push_back(x % BASE); g = x / BASE; } return c; } BigInteger operator - (const BigInteger& b) const { assert(b <= *this); // 减数不能大于被减数 BigInteger c; c.s.clear(); for (int i = 0, g = 0; ; i++) { if (g == 0 && i >= s.size() && i >= b.s.size()) break; int x = s[i] + g; if (i < b.s.size()) x -= b.s[i]; if (x < 0) {g = -1; x += BASE;} else g = 0; c.s.push_back(x); } return c.clean(); } BigInteger operator * (const BigInteger& b) const { int i, j; LL g; vector<LL> v(s.size()+b.s.size(), 0); BigInteger c; c.s.clear(); for(i=0;i<s.size();i++) for(j=0;j<b.s.size();j++) v[i+j]+=LL(s[i])*b.s[j]; for (i = 0, g = 0; ; i++) { if (g ==0 && i >= v.size()) break; LL x = v[i] + g; c.s.push_back(x % BASE); g = x / BASE; } return c.clean(); } BigInteger operator / (const BigInteger& b) const { assert(b > 0); // 除数必须大于0 BigInteger c = *this; // 商:主要是让c.s和(*this).s的vector一样大 BigInteger m; // 余数:初始化为0 for (int i = s.size()-1; i >= 0; i--) { m = m*BASE + s[i]; c.s[i] = bsearch(b, m); m -= b*c.s[i]; } return c.clean(); } BigInteger operator % (const BigInteger& b) const { //方法与除法相同 BigInteger c = *this; BigInteger m; for (int i = s.size()-1; i >= 0; i--) { m = m*BASE + s[i]; c.s[i] = bsearch(b, m); m -= b*c.s[i]; } return m; } // 二分法找出满足bx<=m的最大的x int bsearch(const BigInteger& b, const BigInteger& m) const{ int L = 0, R = BASE-1, x; while (1) { x = (L+R)>>1; if (b*x<=m) {if (b*(x+1)>m) return x; else L = x;} else R = x; } } BigInteger& operator += (const BigInteger& b) {*this = *this + b; return *this;} BigInteger& operator -= (const BigInteger& b) {*this = *this - b; return *this;} BigInteger& operator *= (const BigInteger& b) {*this = *this * b; return *this;} BigInteger& operator /= (const BigInteger& b) {*this = *this / b; return *this;} BigInteger& operator %= (const BigInteger& b) {*this = *this % b; return *this;} bool operator < (const BigInteger& b) const { if (s.size() != b.s.size()) return s.size() < b.s.size(); for (int i = s.size()-1; i >= 0; i--) if (s[i] != b.s[i]) return s[i] < b.s[i]; return false; } bool operator >(const BigInteger& b) const{return b < *this;} bool operator<=(const BigInteger& b) const{return !(b < *this);} bool operator>=(const BigInteger& b) const{return !(*this < b);} bool operator!=(const BigInteger& b) const{return b < *this || *this < b;} bool operator==(const BigInteger& b) const{return !(b < *this) && !(b > *this);} }; ostream& operator << (ostream& out, const BigInteger& x) { out << x.s.back(); for (int i = x.s.size()-2; i >= 0; i--) { char buf[20]; sprintf(buf, "%08d", x.s[i]); for (int j = 0; j < strlen(buf); j++) out << buf[j]; } return out; } istream& operator >> (istream& in, BigInteger& x) { string s; if (!(in >> s)) return in; x = s; return in; }
介绍-1:基本模板 (bign=BigInteger)
说明:注释出现的“[int]”形式,代表这是int类型的变量名或者一个int值。其它[bign]、[char*]同理。
#include <iostream> // 要用cin、cout #include <string> // 要用string类 #include <cstring> // 要用strlen #include <cstdio> // 要用sprintf using namespace std; const int maxn = 2000; // 大整数的最高位数限制 struct bign{ int d[maxn], len; // 去掉大数的前导0 void clean() { while(len > 1 && !d[len-1]) len--; } // 初始化:默认初始化为值0 bign() { memset(d, 0, sizeof(d)); len = 1; } // 初始化:可以用“bign [bign] = [int];”或“bign [bign]([int]);” bign(int num) { *this = num; } // 初始化:可以用“bign [bign] = [char*];”或“bign [bign](char*);” bign(char* num) { *this = num; } // 赋值:可以用“[bign] = [char*];” bign operator = (const char* num){ len = strlen(num); for(int i = 0; i < len; i++) d[i] = num[len-1-i] - '0'; clean(); return *this; } // 赋值:可以用“[bign] = [int];” bign operator = (int num){ char s[maxn]; sprintf(s, "%d", num); *this = s; return *this; } // 将int数组存储的值转换为高精度的字符串形式 string str() const{ string res; for(int i = 0; i < len; i++) res = char(d[i]+'0') + res; return res; } }; // 可以用“cin >> [bign];”的方式输入 istream& operator >> (istream& in, bign& x) { string s; in >> s; x = s.c_str(); return in; } // 可以用“cout << [bign];”的方式输出 ostream& operator << (ostream& out, const bign& x) { out << x.str(); return out; }
不论做哪类高精度运算最好都完整抄录该部分代码,可以让bign类型用起来跟int一样方便。
四个头文件包含了模板中需要用到的数据类型和函数。
常量maxn代表会出现的最大整数位数,这个值定太小会出错,但也不要太大浪费过多内存。
介绍-2:扩展bign的运算功能
该部分代码包括上面所有重载的运算符:+、-、*、/、%、+=。
模板的减法要注意不能用小数减大数,要算绝对值,可以用(a<b?b-a:a-b)。
虽然重载的运算符两边数据类型都是bign,但因为自动强制转换,所以用“[bign]*[int]”也不会错。
(这在应用中带来了相当大的便利)
这里的除法与C语言中两个整数相除的效果相同,会舍尾取整。且除法里其实包含取余运算了,最后的a就是。
末尾写了个重载“+=”的代码,主要是方便bign类型的使用。读者可以根据自己喜好,
①把“*=”等的重载代码写上方便使用,②或者在使用bign类型中不要用这类运算符。
最后说一下效率问题,因为我的代码可以进行大数对大数的运算,如大数除大数、大数对大数取余,
所以在大数除int、大数对int取余时,效率不及专门功能的函数,这里牺牲效率增加通用性。
介绍-3:扩展bign的比较功能
只要定义了“<”符号,即可用它定义其他所有比较运算符。实际题目中根据需要抄录小于和其它需要的运算符,不必全部写入(虽然到了高级运算,如取余,就环环相扣,很难删除某一部分了),在“介绍-2”中的运算符也是一样,这样在ACM比赛中能加快解题速度。