快速幂算法详解(C++实现)

news2024/11/28 9:21:27

文章目录

  • 1. 什么是快速幂
  • 2. 暴力求解
    • 代码实现
    • 缺陷分析
  • 3. 优化一:取模运算的性质
  • 4. 优化二:快速幂算法的核心思想
  • 5. 终极优化:位运算优化
  • 6. 源码

这篇文章我们来一起学习一个算法——快速幂算法。

1. 什么是快速幂

顾名思义,快速幂就是快速算底数的n次幂。其时间复杂度为 O(log₂N), 与朴素的O(N)相比效率有了极大的提高。

那快速幂算法呢一般就是用来解决如下的问题:

在这里插入图片描述
我们看到它的取值范围是比较大的,所以我们可以用long long

2. 暴力求解

代码实现

那这个问题呢乍一看很简单:

我们可以考虑用循环(或者使用pow函数)直接计算a^b的值,然后对c去模即可。
在这里插入图片描述

缺陷分析

但是呢,这样写我们的算法其实是有去缺陷的:

首先它的时间复杂度是O(b),而上面题目中b的取值是【0,10^18】。
所以当b的取值比较大的时候,时间复杂度就会很大,那么算法的效率就比较低。
此外这里的ret不断乘等以a,它的范围是很有可能超过long long 的。
在这里插入图片描述
那一旦溢出的话,结果可能就错了。

所以我们要想办法对该算法进行优化

3. 优化一:取模运算的性质

首先我们可以根据取模运算的性质进行第一重优化:

取模运算是满足这样一条性质的
(a*b)%c=((a%c)*(b%c))%c
大家有兴趣可以自己证明一下
那这样的话我们之前是每次让ret*=a,乘等b次,最后再去模。
那现在我们可以在每次ret*=a之后都对ret进行一次取模
在这里插入图片描述
那这样的话ret就不太容易溢出了。

long long fastPow(long long a, long long b, long long c)
{
	long long ret = 1;
	for (int i = 0; i < b; i++)
	{
		ret *= a;
		ret %= c;
	}

	return ret % c;
}

但是,是否仍然有缺陷呢?

🆗,它的时间复杂度并没有得到优化,还是O(b)

所以,我们再来想办法优化:

4. 优化二:快速幂算法的核心思想

快速幂算法的核心思想就是每一步都把指数分成两半,而相应的底数做平方运算。这样不仅能把非常大的指数给不断变小,所需要执行的循环次数也变小,而最后表示的结果却一直不会变。

我们来举个例子:

比如算3^10
那其实可以这样写:
3^10=(3^2)^5=9^5=9*9^4=9*81^2
那观察这个式子其实我们能发现这样的规律:

  1. 如果指数是是偶数的话,那么指数除以2,底数平方,前后的值是相等的。(ret*3^10=ret*(3^2)^5
  2. 如果指数是奇数的话,先将ret*=底数,然后依然是指数除以2,底数平方,前后值相同(ret*9^5=ret*9*81^2

那我们来算一下这种写法的时间复杂度:

这样优化之后呢,每次指数的值都会/=2,即b/=2,那之前我们要循环b次,现在就是O(logb),即以2为底,b的对数。

那我们来写一下代码:

在这里插入图片描述
那此外,为了防止输入的a过大的话,我们上来可以直接对a取模
在这里插入图片描述

5. 终极优化:位运算优化

那针对上面的代码,有两处地方我们其实还可以进行一个优化:

首先
在这里插入图片描述
判断指数是偶数还是奇数这里,还有一种更高效的方法就是使用位运算,让b&1,因为1的补码只有最后一位为1,其余全为0,如果b是奇数的话,那它的最后一位为1,b&1的结果就是1,如果b是偶数,那最后一位为0,b&1的结果是0
在这里插入图片描述
然后就是:
在这里插入图片描述
b/=2这里,我们可以用b>>=1代替(整数算术右移一位相当于除以2并向下取整)
在这里插入图片描述
关于移位操作符如果大家遗忘了可以看: 【C操作符详解】之 移位操作符

我找了一道OJ,我们可以来测试一下:

在这里插入图片描述
在这里插入图片描述

没有问题!

6. 源码

#include <iostream>
using namespace std;
long long fastPow(long long a, long long b, long long c)
{
	long long ret = 1;
	a %= c;
	while (b)
	{
		if (b & 1)
		{
			ret *= a;
			ret %= c;
		}
		a *= a;
		a %= c;
		b >>= 1;
	}
	return ret;
}

int main()
{
	long long a = 0;
	long long b = 0;
	long long m = 0;
	cin >> a >> b >> m;
	cout << fastPow(a, b, m) << endl;
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1254364.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中海油“海安杯”一站到底知识竞赛真的很有特色

中海油“海安杯”一站到底知识竞赛规格高&#xff0c;赛制复杂&#xff0c;天纵知识竞赛系统为此次知识竞赛提供了软件支持。本次竞赛设置选手区和擂台区两个区域。比赛共分为五个轮次&#xff0c;五个轮次选手区所有参赛选手均需答题。 第一轮&#xff1a;“脱颖而出” 所有参…

叠加原理(superposition principle)

叠加原理&#xff08;superposition principle&#xff09;指对线性系统而言&#xff0c;两个或多个输入产生的输出&#xff0c;等于这几个输入单独引起的输出的和&#xff0c;即输入的叠加等于各输入单独引起的输出的叠加。 例如&#xff0c;如果输入产生的输出是&#xff0c;…

B树与B+树的对比

B树&#xff1a; m阶B树的核心特性&#xff1a; 树中每个节点至多有m棵子树&#xff0c;即至多含有m-1个关键字根节点的子树数属于[2, m]&#xff0c;关键字数属于[1, m-1]&#xff0c;其他节点的子树数属于 [ ⌈ m 2 ⌉ , m ] [\lceil \frac{m}{2}\rceil, m] [⌈2m​⌉,m]&am…

Spring的依赖注入,依赖注入的基本原则,依赖注入的优势

文章目录 Spring的依赖注入依赖注入的基本原则依赖注入有什么优势查找定位操作与应用代码完全无关。有哪些不同类型的依赖注入实现方式&#xff1f;构造器依赖注入和 Setter方法注入的区别 Spring的依赖注入 控制反转IoC是一个很大的概念&#xff0c;可以用不同的方式来实现。…

电子学会C/C++编程等级考试2022年09月(二级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:统计误差范围内的数 统计一个整数序列中与指定数字m误差范围小于等于X的数的个数。 时间限制:5000 内存限制:65536输入 输入包含三行: 第一行为N,表示整数序列的长度(N <= 100); 第二行为N个整数,整数之间以一个空格分…

【教学类-06-12】20231126 (一)如何让加减乘除题目从小到大排序(以1-20之间加法为例,做正序排列用)

结果展示 优化后 优化前 背景需求&#xff1a; 生成列表 单独抽取显示题目排序方法 存在问题: 我希望 00 01 02……这样排序&#xff0c;但是实际上&#xff0c;除了第一个加数会从小到大排序&#xff0c;第二个被加数的第十位数和个位数都会从小到大排序&#xff0c;也就是…

定长子网划分和变长子网划分问题_二叉树解法_通俗易懂_配考研真题

引入:定长子网划分和变长子网划分的基本概念 定长子网划分和变长子网划分的基本概念 目前常用的子网划分&#xff0c;是基于CIDR的子网划分&#xff0c;也就是将给定的CIDR地址块划分为若干个较小的CIDR地址块。 定长子网划分: 使用同一个子网掩码来划分子网&#xff0c;因…

使用VC++设计程序对一幅256级灰度图像进行全局固定阈值分割、自适应阈值分割

图像分割–全局固定阈值分割、自适应阈值分割 获取源工程可访问gitee可在此工程的基础上进行学习。 该工程的其他文章&#xff1a; 01- 一元熵值、二维熵值 02- 图像平移变换&#xff0c;图像缩放、图像裁剪、图像对角线镜像以及图像的旋转 03-邻域平均平滑算法、中值滤波算法、…

线性表,也是Java中数组的知识点!

线性表定义&#xff1a; 由n (n≥0)个数据特性相同的元素构成的有限序列称为线性表&#xff0c;(n0)的时候被称为空表。 线性表的顺序表示 线性表的顺序存储又被称为顺序表 优点 无需为表示表中元素之间的逻辑关系而增加额外的存储空间可以随意读取任意位置的元素 缺点 插入…

Autosar MCAL-RH850P1HC-MCAL配置环境搭建

文章目录 前言下载安装包软件安装安装SIP包安装MCAL文件配置工程配置生成代码测试静态代码路径总结前言 对于RH850P1HC,官网有免费的MCAL,但官网的MCAL没有CAN模块(原厂反馈为Bosch IP,CAN Driver他们没有),也没有FEE模块。如果需要,可以找第三方软件公司,如ETAS.虽然M…

机器学习:攻击方法FGSM系列

任务 FGSM I-FGSM MI-FGSM Ensemble Attack 攻击评价指标 准确率越低表明攻击越好 数据 预训练模型 BaseLine 实践

SpringBoot——LiteFlow引擎框架

优质博文&#xff1a;IT-BLOG-CN 一、LiteFlow 简介 LiteFlow是一个轻量且强大的国产规则引擎框架&#xff0c;可用于复杂的组件化业务的编排领域。帮助系统变得更加丝滑且灵活。利用LiteFlow&#xff0c;你可以将瀑布流式的代码&#xff0c;转变成以组件为核心概念的代码结构…

计算机组成原理-Cache的基本概念和原理

文章目录 存储系统存在的问题Cache的工作原理局部性原理性能分析例题界定何为局部部分问题总结 存储系统存在的问题 增加Cache层来缓和CPU和主存的工作速度矛盾 Cache的工作原理 启动某个程序后&#xff0c;将程序的代码从辅存中取出放入内存中&#xff0c;再从内存中将代码…

nginx反向代理解决跨域前端实践

需求实现 本地请求百度的一个搜索接口&#xff0c;用nginx代理解决跨域思路&#xff1a;前端和后端都用nginx代理到同一个地址8080&#xff0c;这样访问接口就不存在跨域限制 本地页面 查询一个百度搜索接口&#xff0c;运行在http://localhost:8035 index.js const path …

Kanna库代码示例

编写一个使用Kanna库的网络爬虫程序。以下是代码的详细解释&#xff1a; swift import Kanna // 创建一个对象 let proxy Proxy(host: ") // 创建一个Kanna对象 let kanna Kanna(proxy: proxy) // 创建一个请求对象 let request Request(url: "") // 使用…

AIGC ChatGPT4总结Linux Shell命令集合

在Linux中,Shell命令的数量非常庞大,因为Linux提供了各种各样的命令来处理系统任务。这些命令包括GNU核心工具集、系统命令、shell内置命令以及通过安装获得的第三方应用程序命令。以下是一些常见的Linux命令分类及其示例,但请注意,这不是一个全面的列表,因为列出所有命令…

typeof,instanceof

1.typeof typeof运算符返回的结果是以小写的字符串表示的变量的类型 2.instanceof instanceof运算符用于判断右边构造函数的原型对象是否在左边对象的原型链上 let arr[]let obj{}let datenew Dateconsole.log(arr instanceof Array)console.log(arr instanceof Object)conso…

算法竞赛备赛进阶之状态压缩训练

状态压缩 状态压缩DP是一种暴力的算法&#xff0c;它需要遍历每个状态&#xff0c;而每个状态是多个事件的集合。这种算法通常用于小规模问题的求解&#xff0c;因为它的复杂度是指数级别的。 状态压缩DP的两个基本特征包括问题的数据规模特别小&#xff0c;可以通过2的阶乘次…

显示器校准软件BetterDisplay Pro mac中文版介绍

BetterDisplay Pro mac是一款显示器校准软件&#xff0c;可以帮助用户调整显示器的颜色和亮度&#xff0c;以获得更加真实、清晰和舒适的视觉体验。 BetterDisplay Pro mac软件特点 - 显示器校准&#xff1a;可以根据不同的需求和环境条件调整显示器的颜色、亮度和对比度等参数…

Ansible的重用(include和import)

环境 管理节点&#xff1a;Ubuntu 22.04控制节点&#xff1a;CentOS 8Ansible&#xff1a;2.15.6 重用 Ansible提供四种可重用的工件&#xff1a; variable文件&#xff1a;只包含变量的文件task文件&#xff1a;只包含task的文件playbook&#xff1a;可包含play、变量、ta…