惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
aimingoo的专栏
aimingoo的专栏
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 聂微东
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
Blog — PlanetScale
Blog — PlanetScale
大猫的无限游戏
大猫的无限游戏
Vercel News
Vercel News
D
DataBreaches.Net
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
WordPress大学
WordPress大学
L
LangChain Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
F
Fortinet All Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
J
Java Code Geeks
Recent Announcements
Recent Announcements
Jina AI
Jina AI
G
Google Developers Blog
腾讯CDC
博客园_首页
博客园 - 【当耐特】

博客园 - 糖豆爸爸

【树上DP前导知识汇总】 反向建图+拓扑排序 卡特兰数专题(Catalan) AcWing 126. 最大的和 AcWing 431. 守望者的逃离 AcWing 414. 数字游戏 AcWing 468. 魔法阵 AcWing 463. 求和 洛谷 P1632 点的移动 P1056 NOIP2008 普及组 排座椅 洛谷 P1889 士兵站队 洛谷 P1862 输油管道问题 CF444C DZY Loves Colors P2253 好一个一中腰鼓! SCOI2010 P2572 序列操作 P4344 SHOI2015 脑洞治疗仪 T125847 【模板】动态开点线段树 P3373 【模板】线段树 2 Physical Education Lessons
快速幂、龟速乘总结
糖豆爸爸 · 2023-11-13 · via 博客园 - 糖豆爸爸

快速幂、龟速乘总结

一、快速幂

\(a^b\ mod \ p\) 的结果。

\(Code\)

// 快速幂(不加mod)
int qmi(int a, int b) {
    int res = 1;
    while (b) {
        if (b & 1) res = res * a;
        b >>= 1;
        a = a * a;
    }
    return res;
}

// 快速幂
int qmi(int a, int b) {
    int res = 1;
    while (b) {
        if (b & 1) res = (res * a) % MOD;
        b >>= 1;
        a = a * a % MOD;
    }
    return res;
}

解释一下

假如我们需要计算\(2^{10}\),正常的办法是

   int s = 1;
   for (int i = 1; i <= 10; i++) s = s * 2;
   cout << s << endl;

毫无疑问,这个算法是正确的。但它执行的次数是\(10\)次,有没有什么办法可以优化一下运算次数呢?

优化

\(10\)进行二进制分解\((10)_{10}=(1010)_2\)
这样处理后,从后向前,借助于我们熟悉的数位分离模板,就是遍历二进制的每一位。

此时,我们发现,最左面的数字\(1\),权值是\(8\),第三位的数字权值是\(2\)

同时,\(2^8*2^2=2^{10}\)

为什么会有这么神奇的现象呢?其实就是因为幂运算的性质造成:

\[\large 2^{10}=2^{8+2}=2^8*2^2 \]

\(Q:\)为啥非得拆成\(8+2\),为啥不拆成\(7+3\)呢?
就是因为类似于 倍增 的办法在计算中好处理呗!

\(a\)在代码中的使命就是:我不管你用不用的上,反正我每次是翻倍!
而枚举\(b\)的每一个数位,就是看看这个位置上的当前\(a\)是不是需要乘进来!幂运算的性质成功的把幂与二进制加法结合起来了。

把倍增的思路\(2,4,8,16,32,...\)这样长上来的,这是因为
\(2^1*2^1=2^2\)
\(2^2*2^2=2^4\)
\(2^4*2^4=2^8\)
\(2^8*2^8=2^{16}\)
\(2^{16}*2^{16}=2^{32}\)

模板题 : \(P1226\) 【模板】快速幂||取余运算

二、龟速乘

\(a*b\ mod \ p\) 的结果,\(a,b,p\) 都是 \(10^{18}\) 级别。

\(Code\)

// 龟速乘,快速加
int qadd(int a, int b) {
    int res = 0;
    while (b) {
        if (b & 1) res = (res + a) % MOD;
        b >>= 1;
        a = (a + a) % MOD;
    }
    return res;
}

这东西怎么理解呢?

举栗子吧

\(5*6_{10}=5*(110)_2\)

最右侧的\(0\)对就的权值是\(5^1=5\)
右侧第二的\(1\)对就的权值是\(5*2=10\)
右侧第三的\(1\)对就的权值是\(10*2=20\)

\(② ③\)对应的位置上有数字\(1\),有效,\(①\)无效
结果就是\(10+20=30\)