快速幂算法

引理

  1. (a * b) % c = ((a % c) * (b % c)) % c.
  2. &与运算,一般用于取位,如 a&1表示取a二进制下的个位
  3. >>位运算,表示右移,如>>1表示除以2,比通常除法效率更高
  4. \(a^b = a^{b_0}*a^{b_1*2}*...*a^{b_n*2^n}\)

用途

主要用于大数取模
朴素算法存在效率太低和计算过程中超出数据范围的情况,必须进行优化

代码

int quick_pow(int a, int k, int mod)  
{  
    int ans = 1;
    a = a % mod;
    while(k != 0)  
    {  
        if(k & 1) ans = (ans * a) % mod;
        k >>= 1;
        a=(a * a) % mod;  
    }  
    return ans;  
}
int qow(int x, int y, int z)
{return y ? (y & 1 ? x * qow(x, y-1,z) % z : qow(x * x % z, y / 2,z)) : 1;}

拓展——矩阵快速幂

对于矩阵的定义以及矩阵乘法,就不多赘述了,直接切入正题

思路

矩阵快速幂和普通快速幂过程是一样的,只要将对应的运算规则变成矩阵运算规则就好。这里我选择重载运算符,当然也可以写个函数来实现。
而矩阵一般会选择封装起来,我对C++不是很熟悉,更倾向于用结构体来封装。

代码

//k,mod,maxn为全局变量
struct Mat
{
	ll f[maxn][maxn];
	void cls(){memset(f, 0, sizeof(f));}//全部置为0 
	Mat() {cls();}
	friend Mat operator * (Mat a, Mat b)
	{
		Mat res;
		for(int i = 0; i < maxn; i++) for(int j = 0; j < maxn; j++)
			for(int k = 0; k < maxn; k++)
				(res.f[i][j] += a.f[i][k] * b.f[k][j]) %= mod;
		return res;
	}
};

Mat quick_pow(Mat a)  
{  
    Mat ans;
    for(int i = 0; i < maxn; i++) ans.f[i][i] = 1;
    int b = k;
    while(b != 0)  
    {
        if(b & 1) ans = ans * a;
        b >>= 1;
        a = a * a;
    }
    return ans;  
}

应用

讲了矩阵快速幂之后,这个东西到底有什么用呢?
众所周知的斐波那契数列\(f_{n} = f_{n-1} + f_{n-2}\)就可以用矩阵快速幂优化。

\[\left[ \begin{matrix} f_{n} & f_{n-1} \\ f_{n-1} & f_{n-2} \end{matrix} \right] = \left[ \begin{matrix} f_{n-1} & f_{n-2} \\ f_{n-2} & f_{n-3} \end{matrix} \right] \left[ \begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right] = \left[ \begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right] ^{n} \tag{1} \]

(1)式就可以用矩阵快速幂在Olog(n)内算出来

习题

POJ 3070
HDOJ 1575
POJ 3233
HDOJ 2604
HDOJ 1757
ZOJ 3497
ZOJ 2853

总结

快速幂说复杂也挺抽象(对我这个蒟蒻来说),说简单也不难。
仔细体会其中后就能发现其中的思想很巧妙很漂亮,需要用心理解。
顺便一提,快速幂是快速算乘方运算的,过程中可以加上取模运算,实现题目要求,当然也可以不取模。不要误以为快速幂只是用来解决要取模的问题。
实际上,取模应当是快速幂中一个比较费时的计算,虽然我们只需要写一行代码。

posted @ 2018-01-15 21:05  HackHarry  阅读(203)  评论(0编辑  收藏  举报