快速幂与矩阵快速幂

快速幂与矩阵快速幂

快速幂

洛谷p1226

求a^b mod p ,a的b次方模上p

求幂这种算法一般在初学程序设计语言的时候应该都有联系过,只要写一个简单的循环就能够搞定。

/**
 * 普通的求幂函数
 * @param base 底数
 * @param power  指数
 * @param p  模
 * @return  a^b mod p
   */
#define ll long long 
   ll normalPower(ll base,ll power,ll p){
   ll result=1;
   for(int i=1;i<=power;i++){
       result=result*base;
   }
   return result%p;
   }

但是这样求解是存在问题的

如果题目让你求2的100次方,貌似我们程序设计语言中最大的long lnog类型也无法承载这么大的数值,所以题目才不会要求你输出结果,因为结果可能会非常的大,大到没有任何类型可以承载。

指数的概念:

指数:在乘方a中,其中的a叫做底数,n叫做指数,结果叫幂。

f(x)=a^x , 随着x单位长度的递增,f(x)会呈“爆炸性”增长。

一张纸对折一次,厚度变成原来的2倍。再对折第二次,变为原来的2的2次方倍即4倍。以此类推,假设纸的厚度为0.1mm,则对折24次以后,长度超过1千米;对折39次达55000千米,超过地球赤道长度;对折42次达44万千米,超过地球至月球的距离;对折51次达22亿千米,超过地球至太阳的距离;对折82次为51113光年,超过银河系半径的长度。

“取模”运算的运算法则:

(a + b) % p = (a % p + b % p) % p 

(a - b) % p = (a % p - b % p ) % p 

(a * b) % p = (a % p * b % p) % p 

(a * b) % p = (a % p * b % p) % p ,我们仔细研究一下这个运算法则,会发现多个因子连续的乘积取模的结果等于每个因子取模后的乘积再取模的结果。也就是说,我们如果要求:

(abc)%d=(a%d *b%d *c%d )%d;

因此,我们可以借助这个法则,只需要在循环乘积的每一步都提前进行“取模”运算,而不是等到最后直接对结果“取模”,也能达到同样的效果。

/**

 * 普通的求幂函数
 * @param base 底数
 * @param power  指数
 * @return  求幂结果的最后3位数表示的整数
   */
   ll normalPower(ll base,ll power,ll p){
   long long result=1;
   for(int i=1;i<=power;i++){
       result=result*base;
       result=result%p;
   }
   return result%p;
   }

再次思考
虽然这个求幂的方法很有用,但是我们来考虑一下这个算法的时间复杂度,假设我们求2的100次方,那么将会执行100次循环。如果我们分析一下这个算法,就会发现这个算法的时间复杂度为O(N),其中N为指数。求一下小的结果还好,那如果我们要求2的1000000000次方呢?这个程序可能会运行很久很久

//ac 代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<b;i++)
#define loop(i,a,b) for(int i=(a);i<=b;i++)
#define pb push_back
#define ll long long 
#define el '\n'
#define gc getchar()
#define debug(x) cerr<<#x<<":"<<x<<endl
const int N=2e4+10;
int father[N];
ll fastPower(ll a,ll b,ll p){
    ll result=1;
    while(b){
        if(b&1) result=result*a%p;
        b>>=1;
        a=a*a%p;
    }
    return result;
}
int main()
{	
	ll a,b,p;
    cin>>a>>b>>p;
    cout<<a<<"^"<<b<<" mod "<<p<<"="<<fastPower(a,b,p)<<el;
	return 0;
}

矩阵快速幂

image-20230307000706747

洛谷p3390

img

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<b;i++)
#define loop(i,a,b) for(int i=(a);i<=b;i++)
#define pb push_back
#define el '\n'
#define gc getchar()
#define cl putchar('\n')
long long  n, k;
const int mod = 1e9 + 7;
struct An {
    long long  Data[103][103];
    An() {
        memset(Data, 0, sizeof Data);
        rep(i, 0, 103) Data[i][i] = 1;
    }
};
An muli(An a, An b) {
    An result;
    rep(i, 0, n) {
        rep(j, 0, n) {
            result.Data[i][j] = 0;
            rep(k,0,n)
                result.Data[i][j]=(result.Data[i][j]+a.Data[i][k] * b.Data[k][j]) % mod;
        }
    }
    return result;
}
void Print(An a) {
    rep(i, 0, n) {
        rep(j, 0, n)
        {
            if (j == 0)cout << a.Data[i][j];
            else cout << " " << a.Data[i][j];
        }
        cout << el;
    }
}

An fastPower(An a,long long  k) {
    An result;
    while (k) {
        if (k % 2 == 1) result = muli(result, a);
        a = muli(a, a);
        //Print(a);
        k/= 2;
    }
    return result;
}

int main()
{
    An a;
    
    cin >> n >> k;
    rep(i, 0, n)
        rep(j, 0, n) {
        cin >> a.Data[i][j];
    }
    a = fastPower(a, k);
    Print(a);
    return 0;
}

求解斐波那契数列

洛谷p1962

image-20230312002434310

输入输出样例

输入 #1复制

5

输出 #1复制

5

输入 #2复制

10

输出 #2复制

55
求解思路

img

image-20230312105247068

参考代码

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<b;i++)
#define loop(i,a,b) for(int i=(a);i<=b;i++)
#define pb push_back
#define ll long long 
#define el '\n'
#define gc getchar()
#define debug(x) cerr<<#x<<":"<<x<<endl
const int mod = 1e9 + 7;
#define ll long long
struct mat {
    long long Data[2][2];
    mat() {
        Data[0][0] = Data[0][1] = Data[1][0] = 1;
        Data[1][1] = 0;
    }
};
mat multi(mat a, mat b) {
    mat res;
    rep(i, 0, 2) {
        rep(j, 0, 2) {
            res.Data[i][j] = 0;
            rep(k, 0, 2)
                res.Data[i][j] += (a.Data[i][k] * b.Data[k][j]) % mod;
            res.Data[i][j] %= mod;
        }
    }
    return res;
}
long long  fast_power(long long  n) {
    mat a;
    mat res;
    memset(res.Data, 0, sizeof res.Data);
    rep(i, 0, 2) res.Data[i][i] = 1;
    while (n) {
        if (n % 2 == 1) res = multi(res, a);
        n /= 2;
        a = multi(a, a);
    }
    return res.Data[0][0]%mod;
}
int main()
{
    ll n;
    cin >> n;
    
    cout << fast_power(n - 1)%mod;
    return 0;
}

矩阵加速

https://www.luogu.com.cn/problem/P1939

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<b;i++)
#define loop(i,a,b) for(int i=(a);i<=b;i++)
#define pb push_back
#define ll long long 
#define el '\n'
#define gc getchar()
#define debug(x) cerr<<#x<<":"<<x<<endl
#define cl putchar('\n')
const int mod = 1e9 + 7;
#define ll long long
struct mat {
    long long da[3][3]= { {1,0,1},{1,0,0},{0,1,0} };
};
mat multi(mat a, mat b) {
    mat res;
    rep(i, 0, 3) {
        rep(j, 0, 3) {
            res.da[i][j] = 0;
            rep(k, 0, 3) {
                res.da[i][j] = (res.da[i][j] + a.da[i][k] * b.da[k][j]) % mod;
            }
        }
    }
    return res;
}
ll fast_power(ll n) {
    mat res;
    mat a;
    memset(res.da, 0, sizeof res.da);
    res.da[0][0] = res.da[1][1] = res.da[2][2] = 1;
    
       
    while (n) {
        if (n % 2 == 1) res = multi(res, a);
        n /= 2;
        a = multi(a, a);
    }
    /*
    * rep(i, 0, 3) {
        rep(j, 0, 3)
            cout << res.da[i][j];
        cl;
    }
    */
    return (res.da[0][0] + res.da[0][2]+res.da[0][1])%mod;
}
int main()
{
    ll time;
    cin >> time;
    while (time--) {
        ll n;
        cin >> n;
        if (n <= 3) cout << 1 << el;
        else
            cout << fast_power(n - 3) << el;
    }
}

广义斐波那契数列

https://www.luogu.com.cn/problem/P1349

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<b;i++)
#define loop(i,a,b) for(int i=(a);i<=b;i++)
#define pb push_back
#define ll long long 
#define el '\n'
#define gc getchar()
#define debug(x) cerr<<#x<<":"<<x<<endl
#define cl putchar('\n')
const int mod = 1e9 + 7;
#define ll long long
ll p, q;
ll  a1, a2, n, m;
struct mat {
	long long  da[2][2];
	mat() {
		da[0][0] = p, da[0][1] = q;
		da[1][0] = 1;
		da[1][1] = 0;
	}
};
mat multi(mat a, mat b) {
	mat res;
	rep(i, 0, 2) {
		rep(j, 0, 2) {
			res.da[i][j] = 0;
			rep(k, 0, 2) res.da[i][j] = (res.da[i][j] + a.da[i][k] * b.da[k][j]) % m;
		}
	}
	return res;
}
ll fi(ll n) {
	mat res;
	mat a;
	res.da[0][0] = res.da[1][1] = 1;
	res.da[0][1] = res.da[1][0]=0;
	while (n) {
		if (n % 2 == 1) res = multi(res, a);
		a = multi(a, a);
		n /= 2;
	}
	return (res.da[0][0] * a2 + res.da[0][1] * a1)%m;
}
int main()
{	
	cin >> p >> q >> a1 >> a2 >> n >> m;
	cout << fi(n - 2);

}

[NOI2012] 随机数生成器

https://www.luogu.com.cn/problem/P2044

#include<iostream>
#include<algorithm>
#define rep(i,a,b) for(int i=(a);i<b;i++)
#define el '\n'
using namespace std;
#define ll long long 
struct mat {
	ll a[2][2];
	mat() {
		a[0][0] = 1;
		a[0][1] = 0;
		a[1][0] = 0;
		a[1][1] = 1;
	}
};
ll g, m, a, c, X0, n;
ll slow_muti(ll a, ll b) {
	ll result = 0;
	while (b) {
		if (b % 2 == 1) (result += a)%=m;
		(a += a)%=m ;
		b /= 2;
	}
	return result%m;
}
mat muli(mat a, mat b) {
	mat result;
	result.a[0][0] = 0;
	result.a[1][1] = 0;
	rep(i, 0, 2) {
		rep(j, 0, 2) {
			rep(k,0,2)
				//(result.a[i][j] += a.a[i][k]* b.a[k][j]) %= m;
			(result.a[i][j] += slow_muti(a.a[i][k], b.a[k][j]))%=m;
		}
	}
	return result ;
}
ll fast_power(mat a, long long b) {
	mat result;
	while (b) {
		if (b % 2 == 1)  result = muli(result, a);
		a = muli(a, a);
		b /= 2;
	}
	/*rep(i, 0, 2) {
		rep(j, 0, 2) cout << result.a[i][j] << " ";
		cout << el;
	}*/
	return (slow_muti(result.a[0][0], X0) %m + slow_muti(result.a[0][1] , c)  )%m%g;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> m >> a >> c >> X0 >> n >> g;
	mat ans;
	ans.a[0][0] = a%m;
	ans.a[0][1] = 1;
	ans.a[1][0] = 0;
	ans.a[1][1] = 1;
	if (n == 0) cout << 0 << el;
	else 
	cout << fast_power(ans, n)<<el;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值