快速幂算法可谓是基础但极其巧妙而优美并且非常有用的的一类算法=w=
这里介绍三种相关应用:1、快速乘法 2、快速幂 3、矩阵快速幂
一、整数运算
(a*b) mod c == ( (a mod c) * (b mod c) ) mod c
对于2进制,2^n可用1后接n个0来表示、
对于8进制,可用公式 i+3*j == n (其中 0<= i <=2 ),
对于16进制,可用 i+4*j==n(0 <= i <=3)来推算,
表达形式为2^i 后接 j 个0。
1、a*b mod p(快速乘法)
当然,一定会有吃瓜群众黑人问号脸,a*b直接算不接行了,快速乘法个鬼=。=
其实大部分情况下确实是没必要用快速乘法
But
乘法在计算机中的运算并没有你想象中的那么快,也要拆分成加法来做;
更常用的情况就是,在还没取模的时候,a*b已经爆了int64(long long),这时则显然要用快速乘法了=。=,(因为在加法中不会超而且可以直接取模=w=)
当然可能大家更熟悉的是快速幂,so ,这里先介绍快速乘法(就这么任性=A=)
快速乘法的基本思想 ,是二进制和乘法分配律的结合,
比如说,
7=(111)2 ,4*7=4*(111)2 ,
用分配律展开得 4*7 == 4*(100+10+1)2,
我们不难观察出,快速幂可以通过判断当前的位(bit)是1还是0,推断出是否需要做求和操作,每次移动到下一位(bit)时,就对ans进行*2操作,等待是否求和。
(这也可以看作是二分思想的应用)
这种算法将b进行二分从而减少了不必要的运算=w=
时间复杂度是log(n)
=w=
function quick_power(a,b:int64):longint;//具体类型视题目而定
var
ans:longint;
begin
ans:=0;
while (b<>0) do
begin
if (b and 1=1) then ans:=(ans+a) mod p;
b:=b >>1;
a:=(a+a) mod p;
end;
exit(ans);
end;
2、a^b mod p
快速幂其实可以看作是快速乘法的特例
在快速幂中,我们不再对ans进行*2操作,因为b已经从乘数变成了指数(人家已经升级了,而你如果还*2的话就out了=。=),指数的相加意味着底数相乘,
But
二分的思想我们还是仍然继续可以沿用的,把b转化为2进制数
这样,我们就将原本用循环需要O(n)的算法,改进为O(logN)的算法。
=w=
var
a,b,p,s:longint;
begin
read(a,b,p);
s:=1;
while (b<>0) do
begin
if b mod 2=1 then s:=s*a mod p;
b:=b >> 1;
a:=a*a mod p;
end;
writeln(s);
end.
二、矩阵快速幂
由于考试中矩阵快速幂+矩阵乘法已经忘得跟不认识似的导致吃了10个wa而被NM同学吐槽了一番=。=
矩阵的快速幂运算,其实思路和上面的整数快速幂是一样的,对指数进行二分,只不过刚才的a是整型而现在变成了矩阵而已,所以唯一的区别就是刚刚简单的 * ,如今要变成矩阵乘法的运算
矩阵快速幂的练习题可以去交一交Codevs的Fibonacci的一系列,用矩阵乘法+快速幂来优化Fibonacci可谓是经典中的模板题,多交几次没什么坏处(我就在今天的考试中给跪了=。=)
=w=
下面是Codevs 1732 AC代码
const
mo=1000000007;
var
n :int64;
f1,f2 :array[0..2,0..2] of longint;
a,c :array[0..2] of longint;
procedure mul1;
var
j,k:longint;
begin
fillchar(c,sizeof(c),0);
for j:=1 to 2 do
for k:=1 to 2 do
c[j]:=(c[j]+(a[k] mod mo) * (f1[k,j] mod mo) mod mo) mod mo;
a:=c;
end;
procedure mul2;
var
i,j,k:longint;
begin
fillchar(f2,sizeof(f2),0);
for i:=1 to 2 do
for j:=1 to 2 do
for k:=1 to 2 do
f2[i,j]:=(f2[i,j] +(f1[i,k] mod mo)*(f1[k,j] mod mo) mod mo) mod mo;
f1:=f2;
end;
begin
read(n);
while not EOF do
begin
f1[1,1]:=0;f1[1,2]:=1;f1[2,1]:=1;f1[2,2]:=1;
a[1]:=1;a[2]:=1;
dec(n);
while (n<>0) do //矩阵快速幂
begin
if (n mod 2<>0) then mul1;
n:=n div 2;
mul2;
end;
writeln(a[1]);
read(n);
end;
end.
——by Eirlys
转载请注明出处=w=