高精度算法
高进度算法一共分为 6 6 6个主题,分别是高精度加法、高精度减法、高精度乘法、高精度除法、高精度开根和压位。
一、高精度加法
现在有一道题目,如下
给你两个整数a和b,请你算出它们的和。
数据范围:1<=a,b<=10^1000。
一看这道题,就感觉是基础的
a
+
b
a+b
a+b问题,其实不然——数据范围肯定会爆。
那么我们怎么做呢?
我们回顾一下小学时候学的竖式加法。
假设
a
=
145
,
b
=
44
a=145,b=44
a=145,b=44,竖式如下图
其实我们只用从最低位开始加,然后加到最高位就行了。
假设两个加数分别存入
a
a
a和
b
b
b数组,答案存入
c
c
c数组,
y
y
y表示进位,则
c
i
=
(
a
i
+
b
i
+
y
)
m
o
d
10
c_i=(a_i+b_i+y)\:mod\:10
ci=(ai+bi+y)mod10
y
=
⌊
a
i
+
b
i
+
y
10
⌋
y=\lfloor \frac{a_i+b_i+y}{10}\rfloor
y=⌊10ai+bi+y⌋
那么就可以直接用一个循环做了。
时间复杂度为
O
(
m
a
x
(
n
,
m
)
)
O(max(n,m))
O(max(n,m))。
n
n
n和
m
m
m分别表示两个数的位数。
二、高精度减法
那么高精度减法是不是也可以用竖式的办法解决呢,答案是对。
假设被减数
a
=
100
a=100
a=100,减数
b
=
78
b=78
b=78,竖式如下图
计算过程见右边。
其实我们只要判断一下当前这个位置是否够减,如果够,就减;否则,就加上
10
10
10再减,并把前一位减去
1
1
1。
程序如下:
for(int i=1;i<=n;i++)
{
if(a[i]<b[i])
{
a[i]+=10;
a[i+1]--;
}
c[i]=a[i]-b[i];
}
注意:最后要删除前导
0
0
0。
那如果
a
<
b
a<b
a<b,怎么办?
因为
a
−
b
=
−
(
b
−
a
)
a-b=-(b-a)
a−b=−(b−a),而
b
>
a
b>a
b>a,所以我们只用计算
b
−
a
b-a
b−a就行了,输出时在前面加个负号。
时间复杂度为
O
(
m
a
x
(
n
,
m
)
)
O(max(n,m))
O(max(n,m))。
n
n
n和
m
m
m分别表示两个数的位数。
三、高精度乘法
上面我们解决了加法和减法,那么乘法是怎么样的呢?
乘法需要细心找规律才能找到。
假设乘数分别是
12
12
12和
13
13
13,竖式如下图
我们看一下上图,可以发现一个规律:第
i
i
i位和第
j
j
j位相乘的结果应该存在第
i
+
j
−
1
i+j-1
i+j−1位上。
举例一下:
第
1
1
1位
2
2
2乘以第
2
2
2位
1
1
1,结果应该放在第
2
2
2位,因为
1
+
2
−
1
=
2
1+2-1=2
1+2−1=2。
如果不明白可以看下图:
根据这个规律,我们就可以直接计算出答案,这里就不多说了。
注意:答案要累加进一个数组,因为可能会有
i
+
j
−
1
i+j-1
i+j−1重复的情况。
时间复杂度为
O
(
n
m
)
O(nm)
O(nm)。
n
n
n和
m
m
m分别表示两个乘数的位数。
四、高精度除法
高精度除法有两种方法,第一种是二分答案,第二种是利用减法来求。
第一种方法
由于乘法是除法的逆运算,所以我们直接寻找一个数看这个数是否满足乘法运算就行了。
找数的方法很简单,就是二分答案。
如果没学过二分答案,可以自己去网上学一学,以后我会出关于二分答案的内容。
二分答案时,我们只要做乘法就行了。
时间复杂度大概为
O
(
n
m
log
a
2
)
O(nm\log_a^2)
O(nmloga2)。
n
n
n表示被除数的位数,
m
m
m表示除数的位数,
a
a
a表示被除数。
其实复杂度远没有这么多。
第二种方法
我们只用一次一次的用被除数减去除数,直到被减数等于
0
0
0为止,答案就是次数。
这种时间复杂度很高,应该是
O
(
a
b
)
O(\frac{a}{b})
O(ba)
a
a
a表示被除数,
b
b
b表示除数。
五、高精度开根
高精度怎么开根呢,方法和高精度除法一样,都可以用二分答案来解决。
我们二分答案时,就只用算一下乘法,然后调整位置。
时间复杂度为
O
(
n
m
log
a
2
)
O(n^m\log_a^2)
O(nmloga2)。
n
n
n表示被开方数的位数,
m
m
m表示根指数,
a
a
a表示被开方数。
六、压位
那么有什么方法可以使高精度算法的速度更快呢?
答案是,压位。
假如我们现在要算两个数的加法。
我们可以一次就算
5
5
5位,这样只用开
i
n
t
int
int。
我们可以一次就算
12
12
12位,这样只用开
l
o
n
g
l
o
n
g
long long
longlong。
那么时间复杂度就变成了
O
(
a
b
)
O(\frac{a}{b})
O(ba)。
a
a
a表示原来的时间复杂度,
b
b
b表示压位的位数。
这样就可以大大的缩短时间了。