快速幂 学习笔记

说在前面

突然发现自己学了这么久的OI竟然还不会默写快速幂模板,这令我这个蒟蒻十分痛心。

于是花5min的时间复习了快速幂的原理并写下了这个笔记防止自己遗忘。

Pt.1 快速幂是什么

快速幂是一个很常用的trick,它的用途从它的名字就可以看出来,是用来快速求幂的。

这个东东在很多时候可以用到,甚至说与其他算法结合将一些看似不可能再优化的问题继续优化(比如矩阵快速幂)。

顺便说说,本文并不会对这些拓展的东西展开讲解,只是关注最底层的快速幂原理。

Pt.2 如何快速幂

快速幂,简单来说就是利用分治的思想将通过将指数拆分为二进制,并让底数反复平方,从而在 \(O(logn)\) 步内完成幂运算并达到优化的目的。

它基于一个叫整数的 b 进制表示定理的数学原理,表述为:

任意正整数 \(N\) 可唯一表示为:

[N = \sum_{i=0}^{k} a_i \cdot b^i]

其中 \(b \ge 2\)\(a_i \in \mathbb{Z}\),且 \(0 \le a_i < b\)\(a_k \neq 0\)

看起来挺绕的不是吗?其实大概就是说任意一个整数,都能用“1、2、4、8、16……”这种翻倍的值(即指数的幂次),有且只有一种方式刚好凑出来。具体翻几倍则是以几进制(也就是 \(b\) )决定。

还有一个很重要的点。初一数学大家都学过吧?\(a^n \times a^m = a^{n+m}\).

所以我们就可以将指数拆分为二的几次方的和的形式,也就是 \(a^{2^k} \times a^{2^{k+1}} \times ......\) 这样,然后对于每个\(a^{2^k}\)用一个变量不断自乘翻倍来计算,最后再用一个变量把它们乘起来就可以了。

具体为什么是二进制,大概就是假设我们拆成 \(b\) 进制,计算某个数的 \(k\) 次方,循环次数是 \(\log_b k\) 次,但每一次迭代,底数要从 \(a\) 变成 \(a^b\)(需要 \(b-1\) 次乘法),并且还要处理当前位上的系数(最多乘 \(b-1\) 次),这就导致大进制的乘法运算次数甚至比二进制的次数还多。而且计算机本身底层逻辑便是二进制,二进制在硬件性能上就占有天然的优势。

Pt.3 实战应用

其实快速幂一般应用就是套个板子上去就完事了,一般不会有什么改动,也很少会说让你从另外一个东东建模到快速幂的。

目前暂时没找到什么好题,讲究着看吧。

P1226 【模板】快速幂

题目描述

给你三个整数 \(a,b,p\),求 \(a^b \bmod p\)

输入格式

输入只有一行三个整数,分别代表 \(a,b,p\)

输出格式

输出一行一个字符串 a^b mod p=s,其中 \(a,b,p\) 分别为题目给定的值, \(s\) 为运算结果。

输入输出样例 #1
输入 #1
2 10 9

输出 #1
2^10 mod 9=7

说明/提示

样例解释

\(2^{10} = 1024\)\(1024 \bmod 9 = 7\)

数据规模与约定

对于 \(100\%\) 的数据,保证 \(0\le a,b < 2^{31}\)\(a+b>0\)\(2 \leq p \lt 2^{31}\)


快速幂模板。没啥好说的实在要讲什么就看回上面Pt2吧,上代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int x,y,mod; 
int fp(int a,int b){//求a^b
	int res=1;//保存结果
	while(b){//对指数进行分解
        //记得取模
		if(b&1)//等于"if(b%2==1)",但是位运算效率会更快
            res*=a,res%=mod;
		a*=a;//翻倍自增
		a%=mod;
		b/=2;
	}
	return res;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>x>>y>>mod;
	cout<<x<<'^'<<y<<' '<<"mod "<<mod<<'='<<fp(x,y);
	return 0;
}

THE END

感谢您的阅读!

posted @ 2026-07-09 21:45  fireclouds_11  阅读(10)  评论(0)    收藏  举报