·SOL 给可爱的第 4 届学生准备的讲义·
【
SOL
给可爱的第
4
届学生准备的讲义】
倍增与分治
零、从一个美好的愿望开始
郭氏猜想:
O(n)=O(log(n))
。
唐氏猜想:
O(log(n))=O(1)
。
推论:任何复杂度均等于
O(1)
。
(然而可惜的是,上面三个命题都是错误的……)
一直以来,人们都希望能够降低算法的时间复杂度。小到常数级别的优化,大到能够使
得复杂度降低几个数量级的剪枝;工程学上能够进行更大规模的运算,算法竞赛中也能为选
手增加到更多的得分。在漫长的研究过程中人们发现,在已有的很多算法中,有些包含了大
量可以被跳过的无用过程,还有一些能够保存运算中间结果以减少冗余;在进行了相应的优
化之后,本来
O(n)
的遍历过程能够被神奇地削减到
O(log(n))
,大大减少了“不必要的
麻烦”。归纳总结后,算法界将优化这些算法的思路分为了两个大类:倍增和分治。
一、倍增思想
1.1
从快速幂计算法初窥倍增思想
“求
m
个
n
自乘的乘积”这个问题应该没有人会觉得陌生。在数学中,我们已经定义
了幂运算,这个乘积也就被表示为
n
m
。
一我们应该如何计算这个数值呢?最简单的做法,就是用一个循环,将
n
自乘
m
次。
当
m
很大的时候,这个方法的效率是很低的;即便我们需要的是模意义下的幂值、不需要
使用高精度,大量的重复乘法、取模运算的时间开销仍然是难以接受的。
我们不妨观察二进制下计算乘法
n
×
m
的过程(此处我们视为
m
个
n
相加)。此时,若
m
的最低位为
1
,则我们往答案中加入一个
1n
;若
m
的第二位为
1
,则我们往答案中加入
一个
2n
……若
m
的第
i
位为
1
,则我们往答案中加入一个
in
。这样,当
m
的所有
1
位都
被访问过后,
n
×
m
的值就计算出来了。
上面这个算法中,我们计算
in
可以使用递推法(已知
(i-1)n
,则
in=(i-1)n+n
);
这样一来,递推需要
log
2
(m)
次加法、需要最多
log
2
(m)
次加入答案的过程,因此总的时
间复杂度即为
log
2
(m)
。实际上,这种做法的代码实现被称为“快速积”,用于计算可能
会导致溢出的乘法(转化为加法后就不再会溢出了)。
这一思路是否可以推广到求幂呢?当然可以,若我们需要计算
n
m
,则若
m
的第
i
位为
1
,则我们往答案中乘入一个
n
i
即可。和快速积一样,这个被称为快速幂的计算法的时间复
杂度也是
log
2
(m)
。
long long power(long long a,long long b,long long mod)
{
long long res=1;
while(b)
{