快速幂&矩阵快速幂

快速幂

什么是快速幂呢?
试想,如果你想计算a^b,你该怎么做呢?
我们只需b个a相乘即可,但是这么要计算b次,当幂值很大时,消耗时间也很大,那么有什么办法可以减少次数呢?
这就要用到快速幂了
快速幂采用分治的策略,一分为二, 二分为四,四分为八…
a ^ b =( (a ^2) ^ (b/ 2) ) * a ^ (b % 2) = (a ^ (b/2))^ 2 * a ^ (b % 2)
= ( a ^ (b/2) * a ^ (b /2) ) * a ^ (b % 2) … 接着不断的分治
比如计算2^4
正常迭代要计算四次
如果使用快速幂就先计算2^2,再计算(2 ^ 2)^2,这样就只计算了两次,当数据很大时就减少了迭代次数
代码如下

int fun(int a, int b)
{
	int t;
	if (b == 1)
	{
		return a;
	}
	if (b % 2 == 0)
	{
		t = fun(a, b / 2);
		return t * t;
	}
	else {
		t = fun(a, b / 2);
		return t * t * a;
	}
}

你以为这样就完了吗?
我们想想还能不能再优化

优化

还记得位运算吗?
我们把b看作一个二进制数,如果最后一位是1就把a乘到答案了,每遍历一个位数,就把a平方,这样就最大的减少了计算次数

int power(int a,int b)
{
	int ans=1;
	for(;b;b=b>>1)//这里的中间单独一个b是判断b是否不为零 
	{
		if(b&1)ans=ans*a;
		a=a*a;
	}
	return ans;
}

举个例子
比如说求a的11次方,11转化成二进制是1011,那么就是a的1011次

1:1011&1=1;ans=a;a=a2
2:b=101;101&1=1;ans=a3; a=a4
3:b=10;10&1=0;ans=a3;a=a8
4:b=1;1&1=1;ans=a11;a=a22
5:return ans;

洛谷有个模板题p1226在这里插入图片描述
代码

#include<iostream>
using namespace std;
#define ll long long
ll power(ll a, ll b,ll k)
{
	ll ans = 1%k;
	for (; b; b = b >> 1)
	{
		if (b & 1)ans = ans * a%k;
		a = a * a%k;
	}
	return ans;
}
int main()
{
	ll b; ll p; ll k;
	cin >> b >> p >> k;
	ll s;
	s = power(b, p, k);
	cout << b << "^" << p << " mod " << k << "=" << s;
	return 0;
}

矩阵快速幂

矩阵快速幂的原理和快速幂一样,只不过底数变成了一个矩阵
代码如下

struct mat//定义一个矩阵
{
	int m[20][20];
}x,y;
mat mul(mat x, mat y,int n)//定义矩阵乘法,n为矩阵大小
{
	mat temp;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			for (int k = 1; k <= n; k++)
				temp.m[i][j] = temp.m[i][j] + (x.m[i][k] * y.m[k][j]);
				/*temp.m[i][j] = (temp.m[i][j] + (x.m[i][k] * y.m[k][j]) % mod) % mod;*/
	/*如果题目没说或者数据较小,可以不取余*/
	return temp;
}
mat matpower(mat a, int b, int n)//计算a^b,n为矩阵大小
{
	mat ans;
	for (int i = 1; i <= n; i++)//构造单位矩阵
		ans.m[i][i] = 1;
	while (b) {
		if (b & 1)
			ans = mul(ans, a, n);
		a = mul(a, a, n);
		b >>= 1;
	}
	return ans;
}

模板题p3390
在这里插入图片描述

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll; 
const int syk = 1e9 + 7;
ll a[105][105], b;
int n;
ll ans[105][105] = { 0 };

inline void mul1() {  
	ll c[105][105] = { 0 };
	for (int k = 1; k <= n; k++) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				c[i][j] = (c[i][j] + ans[i][k] * a[k][j]) % syk; //注意膜1e9+7
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			ans[i][j] = c[i][j];
		}
	}
}
inline void mul2() {
	ll c[105][105] = { 0 };
	for (int k = 1; k <= n; k++) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				c[i][j] = (c[i][j] + a[i][k] * a[k][j]) % syk;
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			a[i][j] = c[i][j];
		}
	}
}
int main() {
	
	cin >> n >> b;
	for (register int i = 1; i <= n; i++) {
		for (register int j = 1; j <= n; j++)
			cin >> a[i][j];
	}
	for (register int i = 1; i <= n; i++)
		ans[i][i] = 1;  
	while (b) {  
		if (b & 1)
			mul1();
		mul2();
		b >>= 1;
	}
	for (register int i = 1; i <= n; i++) { 
		for (register int j = 1; j <= n; j++)
			printf("%lld ", ans[i][j] % syk);
		printf("\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青云遮夜雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值