首页 > 其他分享 >快速幂

快速幂

时间:2024-06-07 19:43:50浏览次数:6  
标签:32 ll texttt long times 快速 105

大家好,我是Weekoder!

今天的内容是快速幂!(实际上是为了讲矩阵快速幂赶出来的嘻嘻

\[\texttt{Part 1 用处} \]

快速幂,顾名思义就是快速地计算出某个数的幂,形如 \(a^n\)。

\[\texttt{Part 2 思想} \]

为什么普通的幂运算慢?假设要计算 \(a^n\),则需要拆分成 \(a\times a\times\cdots\times a\times a\),运算 \(n\) 次,复杂度为 \(O(n)\)。当 \(n\) 很大时,这个算法明显就不行了。那要怎么优化呢?我们先来看一个简单一点的例子:当 \(n\) 为 \(2\) 的幂时,可以怎么做呢?假设 \(n\) 为 \(32\),可以这样计算:

\[\begin{aligned} a^1\times a^1=a^2 \\ a^2\times a^2=a^4 \\ a^4\times a^4=a^8 \\ a^8\times a^8=a^{16} \\ a^{16}\times a^{16}=a^{32} \end{aligned} \]

注意:\(a^x\times a^y=a^{x+y}\)。

可以发现,这样只计算了 \(5\) 次,相比于朴素算法的 \(32\) 次,将时间复杂度优化到了 \(O(\log n)\)。这其实是倍增的原理,相比于一个一个乘 \(a\),不如将 \(a\) 的数量翻倍乘。

那如果 \(n\) 不是 \(2\) 的幂呢?比如,\(n=105\) 的时候,该怎么办呢?虽然 \(105\) 不是 \(2\) 的幂,但是我们发现 \(105\) 可以拆分成 \(2\) 的幂之和,像这样:

\[105=1+8+32+64 \]

于是,我们可以把 \(a^{105}\) 拆分一下:

\[a^{105}=a^{1+8+32+64}=a^1\times a^8\times a^{32}\times a^{64} \]

我们在刚刚提到过,计算 \(n\) 是 \(2\) 的幂的情况是很容易的,所以我们只需要将它们相乘即可。

这个问题的关键在于,怎样将一个数拆分成 \(2\) 的幂之和?我们来看一下他们在二进制下的样子:

可以看到,\(105\) 的二进制中有 \(4\) 个 \(1\),而 \(2\) 的幂的数都只有一个 \(1\),并且刚好和 \(105\) 的四个 \(1\) 位置一样。所以,只要将 \(105\) 二进制中的 \(1\) 拆开,就能得到 \(2\) 的幂的数字是哪些了。

而因为一个数 \(n\) 的二进制最多只有 \(\log n\) 位,所以时间复杂度为 \(O(\log n)\)。

\[\texttt{Part 3 实现} \]

就决定是你了!快速幂模板

先上代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

ll expow(ll a, ll n, ll p) {
    ll r = 1;
    while (n) {
        if (n & 1) r = r * a % p;
        a = a * a % p, n >>= 1;
    }
    return r;
}

int main() {
    ll a, b, p;
    cin >> a >> b >> p;
    cout << a << "^" << b << " mod " << p << "=" << expow(a, b, p);
    return 0;
} 

输入和输出就不用我讲了,重点是 \(\text{expow}\) 函数。我把快速幂函数提取出来(先不取模):

typedef long long ll;

ll expow(ll a, ll n) {
    ll r = 1;
    while (n) {
        if (n & 1) r *= a;
        a *= a, n >>= 1;
    }
    return r;
}

比如计算 \(7^{105}\)。

首先,我们用一个 \(\color{yellow}\texttt{r}\color{#000000}\texttt{esult}\) 来存储结果,初始时为 \(1\)。接着,有一个 \(\text{while}\) 循环,其实就是遍历 \(n\) 在二进制下的每一位。如果当前这一位是 \(1\),即 \(n\) 对 \(1\) 按位与的结果为 \(1\),则可以拆分,将 \(r\) 乘上 \(a\)。每过一位,\(a\) 就变为 \(a^2\),即模拟倍增的过程。然后还要将 \(n\) 除以 \(2\),用位运算表示就是右移一位,获取下一位。最后返回结果 \(r\)。可以辅助图片理解。

这样就可以用 \(O(\log n)\) 的速度计算 \(a^n\) 了。

小扩展:幂取模

即计算 \(a^n \bmod p\)。

只需要在快速幂的模板里稍微改动一下。在做乘法运算时,顺带取模就行了。

幂取模模板代码如下:

typedef long long ll;

ll expow(ll a, ll n, ll p) {
    ll r = 1;
    while (n) {
        if (n & 1) r = r * a % p;
        a = a * a % p, n >>= 1;
    }
    return r;
}

\[\texttt{Part 4 小结} \]

综上所述,二进制快速幂的核心就是这些了。当然,快速幂除了计算 \(a^n\) 以外,还有很多用处等着你去发现。

再见!

标签:32,ll,texttt,long,times,快速,105
From: https://www.cnblogs.com/Weekoder/p/18237773

相关文章

  • 一键快速部署:Chat-Next-Web自己专属的ChatGPT服务对话平台
    一键快速部署:Chat-Next-Web自己专属的ChatGPT服务对话平台文章目录一键快速部署:Chat-Next-Web自己专属的ChatGPT服务对话平台导语:需要用到的链接汇总1、github项目直达地址2、vercel服务器直达地址3、Cloudflare加速地址一、Github项目`star`+Vercel部署1、......
  • 上位机快速开发框架
      右上角向下按钮->后台配置 系统菜单 角色管理分配权限用户管理设备配置通道管理首页界面设计 带反馈按钮,如:用户按键00105,PLC反馈状态00106 参数说明:TagName_Main:主要信息(通道号)TagName_Relation:关联信息(通道号#事件类型)TagUpdate:允许更新Tag......
  • 快速排序
    funcsortArray(nums[]int)[]int{quickSort(nums,0,len(nums)-1)returnnums}//快排,分治的思想,选一个中轴,//把小于中轴的元素放到左边,大于中轴的元素放到右边//然后递归处理中轴两边的元素funcquickSort(nums[]int,left,rightint){ifleft>=......
  • Ubuntu22.04 LAMP快速实战
    好的,我来为您详细说明如下步骤:安装LAMP更新软件源并安装必要的软件包:sudoaptupdatesudoaptinstallapache2mysql-serverphplibapache2-mod-phpphp-mysql测试LAMP安装是否成功:访问http://localhost查看Apache默认页面进入/var/www/html目录,创建info.......
  • .NET之Hangfire快速入门和使用
    原文地址:.NET之Hangfire快速入门和使用-追逐时光者-博客园(cnblogs.com)前言:定时任务调度问题,是一个老生常谈的问题。网上有许多定时任务调度的解决方案,对于我而言很早以前主要是使用Window计划和Window服务来做任务定时执行,然后就开始使用定时任务调度框架Quartz.N......
  • 适用于 Windows 11 的 10 款最佳视频转换器:快速转换高质量视频格式
    您是否遇到过由于格式不兼容而无法在设备上播放视频或电影的情况?您想随意播放从相机GoPro导入的视频,还是以最合适的格式将它们上传到媒体网站?您的房间里有一堆DVD光盘,并想将它们转换为数字格式以方便播放?...所有这些问题都可以通过一个有效的视频转换器一次性解决。实际......
  • Vue3快速上手(三)
    今天我要分享的是Vue3中pinia,组件通信,slot、Vue3中的其他API和新组件等相关知识,话不多说,赶紧上干货!!!5.pinia5.1【pinia简介】Pinia是一个用于Vue.js应用程序的状态管理库。这个库提供了一个简单且直观的API来管理Vue.js应用程序的状态。Pinia是作为Vue.js官方......
  • 快速排序(排序中篇)
    1.快速排序的概念及实现2.快速排序的时间复杂度3.优化快速排序4.关于快速排序的细节5.总代码1.快速排序的概念及实现1.1快速排序的概念快速排序的单趟是选一个基准值,然后遍历数组的内容把比基准值大的放右边,比基准值小的放在左边,下面的动图可以简单了解是这么排序的。......
  • 【入门教程】5分钟教你快速学会集成Java springboot ~
    介绍ApacheDolphinScheduler是一个分布式易扩展的开源分布式调度系统,支持海量数据处理,具有任务流程调度、任务流程编排、任务监控告警、工作流引擎等功能。本文将介绍如何将ApacheDolphinScheduler集成到JavaSpringboot项目中,以实现更灵活和便捷的调度功能。步骤步骤一:添......
  • Netty 快速入门
    什么是NettyNetty的官网:[https://netty.io/Netty是一个JavaNIO技术的开源异步事件驱动的网络编程框架,用于快速开发可维护的高性能协议服务器和客户端。往通俗了讲,可以将Netty理解为:一个将JavaNIO进行了大量封装,并大大降低JavaNIO使用难度和上手门槛的网络编程框架。Net......