快速幂算法

递归快速幂

在这里插入图片描述

template<typename T>
T qpow(T a, T n)
{
    if (n == 0)
        return T(1);
    else if (n % 2 == 1)
        return qpow(a, n - 1) * a;
    else
    {
        T temp = qpow(a, n / 2);
        return temp * temp;
    }
}

非递归快速幂

a n = a b i 2 i + . . . + b 1 2 1 + b 0 = a b i 2 i × . . . × a b 1 2 1 × a b 0 a^n=a^{b_i2^i+...+b_12^1+b_0}=a^{b_i2^i}\times...\times a^{b_12^1}\times a^{b_0} an=abi2i+...+b121+b0=abi2i×...×ab121×ab0
因此用各项 a b i 2 i = { a 2 i , 如 果 b i = 1 1 , 如 果 b i = 0 a^{b_i2^i}=\left\{\begin{matrix} a^{2^i}& ,如果b_i=1\\ 1&, 如果b_i=0 \end{matrix}\right. abi2i={a2i1,bi=1,bi=0的积来计算 a n a^n an,也就是连续项 a 2 i a^{2^i} a2i的乘积,其中跳过了那些二进制位 b i b_i bi为0的项。因为 a 2 i = ( a 2 i − 1 ) 2 a^{2^i}=(a^{2^{i-1}})^2 a2i=(a2i1)2。所以我们可以利用二进制位串从右到左来计算 a n a^n an
在这里插入图片描述

template<typename T>
T RightLeftBinaryExponentiation(T a, vector<T> b)
// 输入:一个数字a和存放二进制位b_i,...,b_0的数组b(n)
// 		这些位来自于非负整数n的二进制展开式,但是在存储时b_0存放在b[0],b_i存放在b[i]
// 输出:a^n的值
{
    T term = a;
    T product;
    if(b[0] == 1) {
    	product = a;
    }
    else {
    	product = 1;
    }
    for(int i=1; i< b.size(); ++i) {
		term = term * term;
		if(b[i] == 1) {
			product = product * term;
		}
	}
	return product;
}

如果我们现在不输入位串数组,直接输入n,那么就可以得到非递归快速幂的写法。

template<typename T>
T qpow(T a, T n)
{
	T term = a;
    T product = 1;
    while(n) {
	    if(n&1) { // 按位与运算,对应位置上有一个为0结果为0,两个都是1结果为1,n&1==1,说明n的二进制位的最低位为1
	    	product = product * term;
    	}
    	term = term * term;
    	n >>= 1;
	}
	return product;
}

进一步因为我们只想获得最后的值,所以我们可以直接对a进行操作。

template<typename T>
T qpow(T a, T n)
{
    T product = 1;
    while(n) {
	    if(n&1) { // 按位与运算,对应位置上有一个为0结果为0,两个都是1结果为1,n&1==1,说明二进制位的最低位为1
	    	product = product * a;
    	}
    	a = a * a;
    	n >>= 1;
	}
	return product;
}

应用:
用快速幂计算斐波那契数
斐波那契数列:
F n = { F n − 1 + F n − 2 , n ≥ 2 , n ∈ N ∗ 0 , n = 0 1 , n = 1 F_n=\left\{\begin{matrix} F_{n-1}+F_{n-2}&,n\geq 2,n\in N^* \\ 0&,n=0\\ 1&,n=1 \end{matrix}\right. Fn=Fn1+Fn201,n2,nN,n=0,n=1
首先我们可以构建这样一个递推关系
( 1 1 1 0 ) ( F n F n − 1 ) = ( F n + F n − 1 F n ) = ( F n + 1 F n ) \begin{pmatrix} 1&1 \\ 1& 0 \end{pmatrix}\begin{pmatrix} F_n \\ F_{n-1} \end{pmatrix}=\begin{pmatrix} F_n+F_{n-1} \\ F_{n} \end{pmatrix}=\begin{pmatrix} F_{n+1} \\ F_{n} \end{pmatrix} (1110)(FnFn1)=(Fn+Fn1Fn)=(Fn+1Fn)
所以:
( F n + 1 F n ) = ( 1 1 1 0 ) n ( F 1 F 0 ) \begin{pmatrix} F_{n+1} \\ F_{n} \end{pmatrix}=\begin{pmatrix} 1&1 \\ 1& 0 \end{pmatrix}^n\begin{pmatrix} F_{1} \\ F_{0} \end{pmatrix} (Fn+1Fn)=(1110)n(F1F0)
M = ( 1 1 1 0 ) M=\begin{pmatrix} 1&1 \\ 1& 0 \end{pmatrix} M=(1110),则 ( F n + 1 F n ) = M n ( F 1 F 0 ) \begin{pmatrix} F_{n+1} \\ F_{n} \end{pmatrix}=M^n\begin{pmatrix} F_{1} \\ F_{0} \end{pmatrix} (Fn+1Fn)=Mn(F1F0),那么 ( F n F n − 1 ) = M n − 1 ( F 1 F 0 ) \begin{pmatrix} F_{n} \\ F_{n-1} \end{pmatrix}=M^{n-1}\begin{pmatrix} F_{1} \\ F_{0} \end{pmatrix} (FnFn1)=Mn1(F1F0)
我们可以利用快速幂来计算 M n − 1 M^{n-1} Mn1.
我们这里就处理n比较小的情形,如果涉及n较大的情形,要涉及到高精度的问题。

#include<vector>
#include<iostream>
using namespace std;

vector<vector<int>> matrix_multiply(vector<vector<int>>& a, vector<vector<int>>& b) {
    vector<vector<int>> c{{0, 0}, {0, 0}};
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
        }
    }
    return c;
}

vector<vector<int>> matrix_pow(vector<vector<int>>& a, int n) {
    vector<vector<int>> ret{{1, 0}, {0, 1}};
    while (n) {
        if (n & 1) {
            ret = matrix_multiply(ret, a);
        }
        n >>= 1;
        a = matrix_multiply(a, a);
    }
    return ret;
}

int fib(int n) {
    if (n < 2) {
        return n;
    }
    vector<vector<int>> q{{1, 1}, {1, 0}};
    vector<vector<int>> res = matrix_pow(q, n - 1);
    return res[0][0];
}

int main() {
    for (int i = 0; i < 10;++i) {
        cout << fib(i) << endl;
    }
    system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值