我好羡慕会用java的人
为什么要用到高精度呢?
我们知道,
的范围是
,
的范围是
,那么当我们想要表示更往上的数字,应该怎么做?
我上小学
很计算机的一种方式,将每一位放在一个 中,这样,一个数字就变成一个数组,对数字的四则运算,也就变成了对数组的操作.
高精度加法
问:
答案是多少?答:
咳咳,按照小学的教法,我们知道,要列个竖式,对齐数位,一位一位相加,满
进
.
于是:
分析一下计算过程,我们发现,当我们用数组
,数组
,分别存下
和
后,从数组的最后一位开始
循环,用数组
保存和,
保存进位
可以得到
再将这个过程转化为代码,高精度加法就写出来了
BigNum BigNum::operator+(const BigNum &i_T)const //BigNum+BigNum
{
BigNum t(*this);
int big;
big = i_T.len > len ? i_T.len : len;
for (int i = 0; i < big; i++) {
t.a[i] += i_T.a[i];
if (t.a[i] > MAXN) {
t.a[i + 1]++;
t.a[i] -= MAXN + 1;
}
}
t.len = (t.a[big] != 0) ? big + 1 : big;
return t;
}
高精度减法
众所周知,减法是加法的逆运算.所以,我们将加法的过程反过来就是减法.
- 从头往后处理
- 保存向后一位的借位
- 处理负数的偷懒方式为,将第一位前加个符号,输出的时候就加上了符号
BigNum BigNum::operator-(const BigNum &i_T)const //num - num
{
int big, j;
bool flag;
BigNum t1, t2;
if (*this > i_T) {
t1 = *this;
t2 = i_T;
flag = 0;
}
else {
t1 = i_T;
t2 = *this;
flag = 1;
}
big = t1.len;
for (int i = 0; i < big; i++) {
if (t1. a[i] < t2.a[i]) {
j = i + 1;
while (t1.a[j] == 0)
j++;
t1.a[j--]--;
while (j > i)
t1.a[j--] += MAXN;
t1.a[i] += MAXN + 1 - t2.a[i];
}
else
t1.a[i] -= t2.a[i];
}
t1.len = big;
while (t1.a[t1.len - 1] == 0 && t1.len > 1) {
t1.len--;
big--;
}
if (flag)
t1.a[big - 1] = 0 - t1.a[big - 1];
return t1;
}
高精度乘法
我们现在还是小学
提问:
答案是多少
我们来列个式杂
那么分析一下这个过程.
BigNum BigNum::operator*(const BigNum &i_T)const
{
BigNum ret;
int up, i=0, j=0, temp, temp1;
for (i = 0; i < len; i++) {
up = 0;
for (j = 0; j < i_T.len; j++) {
temp = a[i] * i_T.a[j] + ret.a[i + j] + up;
if (temp > MAXN) {
temp1 = temp - temp / (MAXN + 1)*(MAXN + 1);
up = temp / (MAXN + 1);
ret.a[i + j] = temp1;
}
else {
up = 0;
ret.a[i + j] = temp;
}
}
if (up != 0)
ret.a[i + j] = up;
}
ret.len = i + j;
while (ret.a[ret.len - 1] == 0 && ret.len > 1)
ret.len--;
return ret;
}
高精度除法
好的,我们现在还是小学生
高精度除低精度
好的,首先,除法是乘法的逆元,所以我们~
倒着做回去
- 从头往后处理
- 储存余数
- 当余数+该位小于低精度的数时,我们向后延续一位
BigNum BigNum::operator/(const int &i_b)const
{
BigNum ret;
int down = 0;
for (int i = len - 1; i >= 0; i--) {
ret.a[i] = (a[i] + down * (MAXN + 1)) / i_b;
down = a[i] + down * (MAXN + 1) - ret.a[i] * i_b;
}
ret.len = len;
while (ret.a[ret, len - 1] == 0 && ret.len > 1)
ret.len--;
return ret;
}
完整代码
#define MAXN 9999 //MAXN控制每个a[i]内大小
#define DLEN 4 //DLEN控制a[i]中有几位
#define MAXSIZE 5010 //控制数字位数
class BigNum {
private:
int a[MAXSIZE];
int len;
public:
BigNum() {
len = 1;
memset(a, 0, sizeof(a));
}
BigNum(const int);
BigNum(const char*);
BigNum(const BigNum &);
BigNum &operator=(const BigNum &);
friend istream& operator>>(istream&, BigNum&);
friend ostream& operator<<(ostream&, BigNum&);
BigNum operator +(const BigNum &)const;
BigNum operator -(const BigNum &)const;
BigNum operator *(const BigNum &)const;
BigNum operator /(const int &)const;
BigNum operator ^(const int &)const;
long long operator %(const long long &)const;
bool operator >(const BigNum&i_T)const;
bool operator >(const int &i_T)const;
void print();
};
//int->BigNum
BigNum::BigNum(const int i_b)
{
int c, d = i_b;
len = 0;
memset(a, 0, sizeof(a));
while (d > MAXN) {
c = d - (d / (MAXN + 1))*(MAXN + 1);
d = d / (MAXN + 1);
a[len++] = c;
}
a[len++] = d;
}
//char->BigNum
BigNum::BigNum(const char *i_s)
{
int t, k, index, L;
memset(a, 0, sizeof(a));
L = strlen(i_s);
len = L / DLEN;
if (L%DLEN)
len++;
index = 0;
for (int i = L - 1; i >= 0; i -= DLEN) {
t = 0;
k = i - DLEN + 1;
if (k < 0)
k = 0;
for (int j = k; j <= i; j++)
t = t * 10 + i_s[j] - '0';
a[index++] = t;
}
}
//copy
BigNum::BigNum(const BigNum &i_T) :len(i_T.len)
{
memset(a, 0, sizeof(a));
for (int i = 0; i < len; i++)
a[i] = i_T.a[i];
}
//BigNum复制BigNum
BigNum&BigNum::operator=(const BigNum&i_n)
{
len = i_n.len;
memset(a, 0, sizeof(a));
for (int i = 0; i < len; i++)
a[i] = i_n.a[i];
return *this;
}
//cin>> BigNum
istream& operator >>(istream &in, BigNum &i_b)
{
char ch[MAXSIZE * DLEN];
in >> ch;
int L = strlen(ch), count = 0, sum = 0;
for (int i = L - 1; i >= 0;) {
sum = 0;
int t = 1;
for (int j = 0; j < DLEN && i >= 0; j++, i--, t *= 10)
sum += (ch[i] - '0')*t;
i_b.a[count] = sum;
count++;
}
i_b.len = count++;
return in;
}
//cout<<BigNum
ostream& operator <<(ostream& out, BigNum& i_b)
{
cout << i_b.a[i_b.len - 1];
for (int i = i_b.len - 2; i >= 0; i--)
printf("%04d", i_b.a[i]);
return out;
}
//高精度除低精度
BigNum BigNum::operator/(const int &i_b)const
{
BigNum ret;
int down = 0;
for (int i = len - 1; i >= 0; i--) {
ret.a[i] = (a[i] + down * (MAXN + 1)) / i_b;
down = a[i] + down * (MAXN + 1) - ret.a[i] * i_b;
}
ret.len = len;
while (ret.a[ret.len - 1] == 0 && ret.len > 1)
ret.len--;
return ret;
}
//高精度%低精度
long long BigNum::operator%(const long long &i_b)const
{
long long d = 0;
for (int i = len - 1; i >= 0; i--)
d = ((d*MAXN + 1) % i_b + a[i] * 1LL) % i_b;
return d;
}
//高精度求幂
BigNum BigNum::operator^(const int &n)const
{
int i;
BigNum t, ret(1);
if (n < 0)
exit(-1);
if (n == 0)
return 1;
if (n == 1)
return *this;
int m = n;
while (m > 1) {
t = *this;
for (i = 1; (i << 1) <= m; i <<= 1)
t = t * t;
m -= i;
ret = ret * t;
if (m == 1)
ret = ret * (*this);
}
return ret;
}
//高精与高精比较
bool BigNum::operator>(const BigNum &i_T)const
{
int ln;
if (len > i_T.len)
return true;
else if (len < i_T.len)
return false;
else {
ln = len - 1;
while (a[ln] == i_T.a[ln] && ln > 0)
ln--;
return (ln >= 0 && a[ln] > i_T.a[ln]);
}
}
//高精与低精度
bool BigNum::operator>(const int &i_T)const
{
BigNum b(i_T);
return *this > b;
}
//打印高精度
void BigNum::print()
{
printf("%d", a[len - 1]);
for (int i = len - 2; i >= 0; i--)
printf("%04d", a[i]);
printf("\n");
}
//高精度相乘
BigNum BigNum::operator*(const BigNum &i_T)const
{
BigNum ret;
int up, i=0, j=0, temp, temp1;
for (i = 0; i < len; i++) {
up = 0;
for (j = 0; j < i_T.len; j++) {
temp = a[i] * i_T.a[j] + ret.a[i + j] + up;
if (temp > MAXN) {
temp1 = temp - temp / (MAXN + 1)*(MAXN + 1);
up = temp / (MAXN + 1);
ret.a[i + j] = temp1;
}
else {
up = 0;
ret.a[i + j] = temp;
}
}
if (up != 0)
ret.a[i + j] = up;
}
ret.len = i + j;
while (ret.a[ret.len - 1] == 0 && ret.len > 1)
ret.len--;
return ret;
}
BigNum BigNum::operator+(const BigNum &i_T)const //BigNum+BigNum
{
BigNum t(*this);
int big;
big = i_T.len > len ? i_T.len : len;
for (int i = 0; i < big; i++) {
t.a[i] += i_T.a[i];
if (t.a[i] > MAXN) {
t.a[i + 1]++;
t.a[i] -= MAXN + 1;
}
}
t.len = (t.a[big] != 0) ? big + 1 : big;
return t;
}
BigNum BigNum::operator-(const BigNum &i_T)const //num - num
{
int big, j;
bool flag;
BigNum t1, t2;
if (*this > i_T) {
t1 = *this;
t2 = i_T;
flag = 0;
}
else {
t1 = i_T;
t2 = *this;
flag = 1;
}
big = t1.len;
for (int i = 0; i < big; i++) {
if (t1. a[i] < t2.a[i]) {
j = i + 1;
while (t1.a[j] == 0)
j++;
t1.a[j--]--;
while (j > i)
t1.a[j--] += MAXN;
t1.a[i] += MAXN + 1 - t2.a[i];
}
else
t1.a[i] -= t2.a[i];
}
t1.len = big;
while (t1.a[t1.len - 1] == 0 && t1.len > 1) {
t1.len--;
big--;
}
if (flag)
t1.a[big - 1] = 0 - t1.a[big - 1];
return t1;
}
高精度除高精度
好的,我们现在不做小学生了
考虑用除低精度的做法,太麻烦了.
那么我们再回到那句话,除法是乘法的逆元.
考虑:
于是,问题变为了加法和乘法的组合.
对于加法,一个个试的话,必定超时.考虑两种方式:二分法和牛顿法.
高精度用不了牛顿法其实我不会,使用二分法,复杂度为
.
对于乘法
- 普通的模拟 .
- 分治乘法:最简单的是 乘法,一般化以后有 乘法;
- 快速傅里叶变换
- :(为了避免精度问题,可以改用快速数论变换 ),时间复杂度 。参照 和
- 中国剩余定理:把每个数分解到一些互素的模上,然后每个同余方程对应乘起来就行
两者结合即可解决问题.
fft的话可以看一下
交完且
后,
还在敲代码.