算法 —— 快速幂

目录

 P1045 [NOIP2003 普及组] 麦森数

P1226 【模板】快速幂

原理I

原理II

P1226 代码解析

P1045 代码解析  


P1045 [NOIP2003 普及组] 麦森数

本题来自洛谷:P1045 [NOIP2003 普及组] 麦森数,根据题意,我们可以看到本题需要计算最少2的1000次方,如果我们要从2的1次方开始计算需要乘二1000次,这无疑是需要消耗大量时间的,在解决本题之前可以先学习一下下面算法:快速幂

P1226 【模板】快速幂

本题来自洛谷:P1226 【模板】快速幂,题目已经告诉你需要使用快速幂算法来解决问题。

根据题解大佬给出的介绍,本蒟蒻对此进行了总结:

很显然,快速幂算法就是为了解决大量乘法次数而产生的,这种算法可以让计算机快速计算出

a^b,暴力相乘的话,电脑要计算 b 次,用快速幂,计算次数在 log(b) 级别。


原理I

先看以下幂运算的特性,相信大家觉得不难:

 了解了这些特性,我们可以利用它来解决问题,过程如下:

  1. 幂与1进行按位与运算
  2. 为1就说明该位对应的二进制存在,需要乘
  3. 为0说明该位对应的二进制不存在,a自乘提升后的值不需要乘

 直接来一个例子给大家更好的认识此过程:

假设我们拿到了 𝑎,并且 𝑏=11。想求 𝑎 ^ 11,但是又不想乘11次,有点慢。以电脑视角稍稍观察一下 𝑏=11,二进制下是 𝑏=1011。

制作一个 𝑏𝑎𝑠𝑒。现在 𝑏𝑎𝑠𝑒=𝑎,表示的是,𝑎 ^ 1 = 𝑎,待会 𝑏𝑎𝑠𝑒会发生改变的。

制作一个 𝑎𝑛𝑠,初值 1,准备用来做答案。

由于11的二进制有4位,所以我们要进行4次循环判断:

1、循环一,看 𝑏 的最后一位是 1 吗? 如果是,代表 a 的一次方存在,以 𝑎𝑛𝑠 ∗= 𝑏𝑎𝑠𝑒。

if(b & 1)ans *= base;/*关于 b & 1:
x & y 是二进制 x 和 y 的每一位分别进行“与运算”的结果。
与运算,即两者都为 1 时才会返回 1,否则返回 0。
那么 b & 1二进制
b     =    1011
1     =    0001
b&1   =    0001因为 1(二进制)的前面几位全部都是 0,
所以只有 b 二进制最后一位是 1 时,b & 1 才会返回 1。*/

 判断完成之后base上升一次(自乘)

base *= base;

同时b最低位就要舍弃掉了,下一次循环用前一位来与1进行按位与运算,舍弃方式直接右移一位。

b >>= 1;

2、循环二,再看看 𝑏,此时b的二进制表示为101,最后一位还是 1,这说明有 a 的二次方存在,我们依旧𝑎𝑛𝑠 ∗= 𝑏𝑎𝑠𝑒(此时base为a的二次方),继续进行上述操作。

3、循环三,继续看看 𝑏,此时b的二进制表示为10,最后一位是0,说明 a 的三次方不存在了,我们不需要让ans *= base,但是base还要继续自乘上升,因为 b 还没变成0,判断条件依旧成立。

4、循环四, 𝑏 此时为1了,按位与后成立,说明有 a 的四次方,依旧𝑎𝑛𝑠 ∗= 𝑏𝑎𝑠𝑒(base此时为a的四次方)b右移一位后变成0,循环结束。

总的来说,如果 𝑏 在二进制上的某一位是 1,我们就把答案乘上对应的 a^(2^n)。代码如下:

int quickPower(int a, int b)//求a的b次方
{int ans = 1, base = a;//ans为答案,base为a^(2^n)while(b > 0)//b是一个变化的二进制数  每次循环右移位变零{//b&1表示b在二进制下最后一位是不是1,如果是:把ans乘上对应的a^(2^n)if(b & 1)ans *= base;base *= base;//base自乘,由a^(2^n)变成a^(2^(n+1))b >>= 1;//位运算,b右移一位}return ans;
}

 可以看到上述代码只需要循环4次就可以求出2的11次方,而用一个变量不断乘2需要11次循环才能实现,这无疑是大大提高了效率


原理II

快速幂有很多种理解方式。从头开始,看以下图片

 可以看到我们凭借幂是否为奇数将3的11次方拆成了好几个小部分,依次运算,那么用代码如何实现呢?先看思路:假设我们以3的11次方为例

第一层循环,𝑏=11,一个奇数。将 3 ^ 11 分解来看,本层只需把 𝑎𝑛𝑠 ∗= 3。那后面的3 ^ 10呢?我们到下一层再搞定。下几层的总目标是让 𝑎𝑛𝑠∗= 3 ^ 10,也就是让 𝑎𝑛𝑠∗=9 ^ 5。来到下一层的是 𝑥 = 3 ∗ 3 = 9 且 𝑏 = 11 / 2 = 5。

第二层循环,几乎独立于第一层存在。𝑏=5,一个奇数。将 9 ^ 5 分解为 9 * 9 ^ 4 来看。本层只需把 𝑎𝑛𝑠∗=9,后面的我们到下一层再搞定。下几层的总目标是让 𝑎𝑛𝑠∗= 9 ^ 4,也就是让 𝑎𝑛𝑠∗=81 ^ 2。于是 𝑥 = 9∗9 = 81  且 𝑏 = 5/2 = 2。

第三层循环,𝑏=2,不是奇数,只把 81 ^ 2 当作 (81 ^ 2) ^ 1。下几层的总目标是让 𝑎𝑛𝑠∗=81^2。于是 𝑥=81∗81=6561,𝑏=2/2=1。

第四层循环,𝑏=1,是奇数。这时候已经不用看成什么分解了,𝑎𝑛𝑠∗=6561 就可完成总目标,b/2 为 0,结束循环。

代码和上面一样,因为 𝑏 & 1 与 𝑏 %  2 == 1 等效。𝑏 /= 2 与 𝑏 >>= 1 等效。

很显然上述思路是一个分治思想,下面附上代码:

int quick_pow(int a, int b) // a为底数  b为幂
{if (b == 1) //最后一层  没办法再分return a;else{int c = quick_pow(a, b / 2);if (b % 2 == 0) // 81^2这种情况return c * c;elsereturn c * c * a; // 3 * ((3^2)^5)}
}

P1226 代码解析

快速幂经常要结合取余运算,这里介绍取余运算有一些好用的性质,包括:

  1. ( A + B ) % b = ( A  % b + B % b ) % b
  2. ( A × B ) %  b = ( ( 𝐴 % 𝑏 ) × ( 𝐵 % 𝑏 ) ) %  𝑏

学习完以上内容,我们尝试解决最开始留下的问题吧,首先是【模板】快速幂的代码解析:

利用上述性质可以解决本题,不要缩短运算时间后就认为问题解决了,试想一下当数据超过了long long的最大值应该如何解决,所以我们在运算时就要着手处理取余操作

#include<bits/stdc++.h>
using namespace std;
//注意本题 a 和 b 都可能为INT_MAX 用int不够
long long quickPower(long long a, long long b, long long c)
{long long ans = 1, base = a;while (b > 0){if (b & 1){ans *= base;ans %= c;  }base *= base;base %= c;b >>= 1;}return ans;
}int main()
{long long a, b, p; cin >> a >> b >> p;cout << a << '^' << b << " mod " << p << '=' << quickPower(a, b, p) << endl;return 0;
}


P1045 代码解析 

#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int f[1001],p,res[1001],sav[1001];//乘法要开两倍长度
void result_1()
{memset(sav,0,sizeof(sav));for(register int i=1;i<=500;i+=1)for(register int j=1;j<=500;j+=1)sav[i+j-1]+=res[i]*f[j];//先计算每一位上的值(不进位)for(register int i=1;i<=500;i+=1){sav[i+1]+=sav[i]/10;//单独处理进位问题,不容易出错sav[i]%=10;}memcpy(res,sav,sizeof(res));//cstring库里的赋值函数,把sav的值赋给res
}
void result_2()//只是在result_1的基础上进行了细微的修改
{memset(sav,0,sizeof(sav));for(register int i=1;i<=500;i+=1)for(register int j=1;j<=500;j+=1)sav[i+j-1]+=f[i]*f[j];for(register int i=1;i<=500;i+=1){sav[i+1]+=sav[i]/10;sav[i]%=10;}memcpy(f,sav,sizeof(f));
}
int main()
{scanf("%d",&p);printf("%d\n",(int)(log10(2)*p+1));res[1]=1;f[1]=2;//高精度赋初值while(p!=0)//快速幂模板{if(p%2==1)result_1();p/=2;result_2();}res[1]-=1;for(register int i=500;i>=1;i-=1)//注意输出格式,50个换一行,第一个不用if(i!=500&&i%50==0)printf("\n%d",res[i]);else printf("%d",res[i]);return 0;
}

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

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

相关文章

C语言程序设计实验8实验报告

1&#xff0e;实验目的 &#xff08;1&#xff09;掌握循环语句 &#xff08;2&#xff09;学习使用递归 &#xff08;3&#xff09;学习使用程序调试 2&#xff0e;实验内容、算法、流程图及主要符号说明 &#xff08;1&#xff09;用辗转相减求最大公约数的递归定义是&a…

ArkTS学习笔记_自定义组件

自定义组件学习笔记 (一)、自定义组件的基本结构 1、struct:自定义组件基于struct实现 格式: struct + 自定义组件名 + {...}简单示例: @Entry @Component struct Index {// 组件的状态 @State message: string = Hello World// 组件的UI构造方法 build() {Row() {C…

python 读取矢量图svg

在Python中读取矢量图&#xff0c;通常指的是读取如SVG&#xff08;可缩放矢量图形&#xff09;或PDF等格式的文件。对于这些格式的处理&#xff0c;Python有几个库可以做到&#xff0c;如matplotlib、cairo、inkscape的Python接口等。以下是使用matplotlib库读取SVG文件的一个…

基于JAVA的智慧社区信息管理系统设计

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

【Android高级UI】PorterDuffMode颜色混合公式

效果展示 色彩混合公式 参数说明 S&#xff0c;源图形D&#xff0c;目标图形A&#xff0c;透明度C&#xff0c;RGB色Saturate&#xff0c;饱和度

js的多线程示例

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>普通worker</title></head><body…

PingRAT:一款基于ICMP的隐蔽型C2流量转发工具

关于PingRAT PingRAT是一款基于ICMP的隐蔽型C2流量转发工具&#xff0c;该工具专为红队成员和攻防演练任务而设计&#xff0c;主要利用了ICMP Payload来实现其功能&#xff0c;该工具不仅能转发C2流量&#xff0c;而且还可以帮助广大研究人员增强C2网络通信流量传输的隐蔽性。…

基于Cpp的OpenCV4.8入门学习笔记(二)

文章目录 前言一、OpenCV图像的一些基本操作&#xff08;day 02&#xff09;16. OpenCV中提供的随机数生成17. 绘制和填充多边形&#xff0c;以五边形为例18. 鼠标事件控制绘画&#xff0c;截取ROI区域19. 图像的像素类型转换与归一化操作20. 图像的放缩与插值21. 图像的翻转&a…

(leecode学习)14. 最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 示例 1&#xff1a; 输入&#xff1a;strs ["flower","flow","flight"] 输出&#xff1a;"fl"示例 2&#xff1a; 输入…

day02.08.成员运算符•二

# 成员运算符&#xff08;掌握&#xff09; # in, not in print(3 in [3,4,5]) # print("name" in "abcnamehaha") # print(name not in {"name": "ikun", "age": 30}) # 身份运算符&#xff08;了解&#xff09; # is…

SWDIO管脚作为GPIO

下面是使用FRDM-K32L2B3开发板和SDK中的frdmk32l2b_gpio_led_output程序做了一些测试&#xff0c;configure SWDIO pin as GPIO pin的流程。 查看手册&#xff0c;找到SWDIO对应的管脚&#xff0c;可以看到PTA3对应的SWDIO管脚。 2.修改Demo程序&#xff0c;在程序中设置SWDIO…

npm和yarn清理缓存命令

yarn清除缓存 1、查看yarn全局缓存目录 yarn cache dir2、清除缓存 yarn cache cleannpm 清除缓存 1、将node-modules文件夹先删除 2、清理缓存命令&#xff1a; npm cache clean --force3、重新安装一次即可 npm install /cnpm install

【杰理蓝牙开发】AC632 开发板烧录实例

AC632 开发板烧录实例 0. 个人简介 && 授权须知1. 硬件板卡介绍2. 代码烧录2.1 使用USB接口烧录2.2 使用串口烧录 3. 为什么要用烧录器供电&#xff1f; 0. 个人简介 && 授权须知 &#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c…

代码随想录训练营第三十六天 1049最后一块石头的重量II 494目标和

第一题&#xff1a; 原题链接&#xff1a;1049. 最后一块石头的重量 II - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 首先确认这是一道01背包问题的题目&#xff0c;如何转换&#xff1a;剩下尽可能小的重量&#xff0c;如何剩下呢&#xff1f;跟分割等和子集很…

论文略读:LoRA Learns Less and Forgets Less

202405 arxiv 1 主要思想 LORA相比于全参数训练&#xff0c;学的少&#xff0c;但忘的也少 2 实验分析 2.1 训练的表现 在编程和数学任务中&#xff0c;LoRA相比全参数微调表现出明显的劣势 2.2 遗忘的表现 这边的遗忘&#xff0c;是指在数据集A上预训练&#xff0c;然后在…

18.x86游戏实战-找角色人物名字

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

c语言复习

0概念复习 gcc 旨在编译 C 语言代码。g 旨在编译 C 语言代码。 1.定义常用 #define pi 3.14 int m,n,i,sum,k1,count0,t; #include <ctype.h> #include <string.h> 2.函数常用 3.应试回答和八股 char x[5]{a&#xff0c;b&#xff0c;\0&#xff0c;c&#xff0c…

Checkpoint log is not found or is corrupted. Job not submitted.

问题 Checkpoint log is not found or is corrupted. Job not submitted. 解决 这个功能现在LSF需要第三方软件才能支持了&#xff0c;比如通过脚本集成blcr&#xff0c;blcr提供checkpoint&#xff0c;提交作业时-k是必须的&#xff0c;这个参数要设置存放checkpoint log的…

Linux编程乐趣《简单、有趣、好玩的Linux命令》

文章目录 一、黑客帝国&#xff08;cmatrix&#xff09;1.1 centOS 手动安装&#xff1a;1.2 . 运行1.3 . 效果 二、cal命令三、htop四、sl:蒸汽火车五、cowsay:会说话的小牛六、boxes七、pv 一、黑客帝国&#xff08;cmatrix&#xff09; 1.1 centOS 手动安装&#xff1a; #…

【数学建模与优化】:解析与实践

目录 数学建模概述 1. 什么是数学模型 2. 数学模型的分类 2.1 按应用领域分类 2.2 按建模方法分类 2.3 按是否考虑随机因素分类 2.4 按变量的连续性分类 2.5 按对对象内部规律了解程度分类 2.6 按变量的基本关系分类 2.7 按是否考虑时间变化分类 3. 数学规划及优化模…